noheader: Remove leftovers from "noheader"
[ninja.git] / application / views / js / widgets.js
blobe98c3d65c1d5f12d2406af6710176c17e1fa2749
1 // FUN FACT: This depends on global_search.js for sprintf
2 init_easywidgets();
3 $(document).ready(function() {
4         $(".widget-place").bind('click', function() {
5                 $("#page_settings").hide();
6         });
7         $('#show_global_widget_refresh').click(function (ev) {
8                 ev.preventDefault();
9                 // maybe someone clicked the input?
10                 if (ev.target != this)
11                         return;
13                 if ($('#widget_global_slider').is('div')) {
14                         $('#widget_global_slider').remove();
15                 } else {
16                         var content = '<div id="widget_global_slider"><div style="height:10px;background-color: #ffffff;"></div>';
17                         content += '<br /><input style="border:0px; display: inline; padding: 0px; margin-bottom: 7px" size="3" type="text" name="global_widget_refresh" id="global_widget_refresh" value="' + global_refresh + '" />';
18                         content += '</div>';
19                         $("#show_global_widget_refresh").append(content);
20                         $("#widget_global_slider div").slider({
21                                 value: global_refresh,
22                                 min: 0,
23                                 max: 500,
24                                 step: 10,
25                                 slide: function(event, ui) {
26                                         $("#global_widget_refresh").val(ui.value);
27                                         global_refresh = ui.value;
28                                         update_save_interval();
29                                 }
30                         });
31                 }
32         });
33 });
35 // create array prototype to sole the lack of in_array() in javascript
36 Array.prototype.has = function(value) {
37         var i;
38         for (var i = 0, loopCnt = this.length; i < loopCnt; i++) {
39                 if (this[i] === value) {
40                         return true;
41                 }
42         }
43         return false;
46 function init_easywidgets(){
47         window.easywidgets_obj = $.fn.EasyWidgets({
48                 behaviour : {
49                         useCookies : false
50                 },
51                 i18n : {
52                         editText : '<img src="' + _site_domain + 'application/views/icons/12x12/box-config.png" alt="Settings" />',
53                         closeText : '<img src="' + _site_domain + 'application/views/icons/12x12/box-close.png" alt="Close widget" />',
54                         collapseText : '<img src="' + _site_domain + 'application/views/icons/12x12/box-mimimize.png" alt="Collapse" />',
55                         cancelEditText : '<img src="' + _site_domain + 'application/views/icons/12x12/box-config.png" alt="Cancel" />',
56                         extendText : '<img src="' + _site_domain + 'application/views/icons/12x12/box-maximize.png" alt="Extend" />'
57                 },
58                 effects : {
59                         effectDuration : 150,
60                         widgetShow : 'slide',
61                         widgetHide : 'slide',
62                         widgetClose : 'slide',
63                         widgetExtend : 'slide',
64                         widgetCollapse : 'slide',
65                         widgetOpenEdit : 'slide',
66                         widgetCloseEdit : 'slide',
67                         widgetCancelEdit : 'slide'
68                 },
69                 callbacks : {
70                         onChangePositions : function(str){
71                                 // triggered by drag & drop
72                                 save_widget_order(str);
73                         },
74                         onClose: function(link, widget) {
75                                 // triggered by X on the widget's own window panel
76                                 save_widget_state('hide', widget.data('name'), widget.data('instance_id'));
77                                 var menu_siblings = $('.widget-selector').filter('[data-name=' + widget.data('name') + ']');
78                                 var this_entry = menu_siblings.filter('[data-instance_id=' + widget.data('instance_id') + ']');
79                                 if (menu_siblings.length > 1)
80                                         this_entry.detach();
81                                 else
82                                         this_entry.removeClass('selected').addClass('unselected').attr('data-instance_id', '');
83                                 widget.detach();
84                                 delete loaded_widgets[widget.data('name')+'-'+widget.data('instance_id')];
85                         },
86                         onHide: function(widget) {
87                                 // trigged by unchecking box in the widget menu
88                                 save_widget_state('hide', widget.data('name'), widget.data('instance_id'));
89                                 var menu_siblings = $('.widget-selector').filter('[data-name=' + widget.data('name') + ']');
90                                 var this_entry = menu_siblings.filter('[data-instance_id=' + widget.data('instance_id') + ']');
91                                 if (menu_siblings.length > 1)
92                                         this_entry.detach();
93                                 else
94                                         this_entry.removeClass('selected').addClass('unselected').attr('data-instance_id', '');
95                                 widget.detach();
96                                 delete loaded_widgets[widget.data('name')+'-'+widget.data('instance_id')];
97                         },
98                         onAdd: function(w) {
99                                 // triggered by checking box in the widget menu
100                                 new widget(w.data('name'), w.data('instance_id'));
101                         }
102                 }
103         });
106 // Global variable to keep track of when user decides to reload the interface.
107 // This is needed since we want to prevent the spawning of additional ajax calls.
108 var _is_refreshing = false;
110 var _global_save = 0;           // timeout handler variable
111 var global_refresh = 60;        // keeps track of the refresh rate set by slider
114 *       Keep track of the timeout for when to save
115 *       the global refresh rate for widgets.
116 *       Is reset to 5 sec between all changes
118 function update_save_interval()
120         if (_global_save) {
121                 clearTimeout(_global_save);
122         }
123         _global_save = setTimeout("set_widget_refresh()", 5000);
127 *       Save new refresh rate for all widgets to database
128 *       and display notification if successful or not
130 function set_widget_refresh()
132         _is_refreshing = true;
134         var url = _site_domain + _index_page + "/widget/set_widget_refresh/";
135         var page_name = _current_uri;
136         var value = global_refresh;
137         var data = {page: escape(page_name), value: value, type: 'refresh_interval'};
138         $.ajax({
139                 url: url,
140                 dataType:'json',
141                 data: data,
142                 type: 'POST',
143                 success: function(data) {
144                         if (data.success == true) {
145                                 $.jGrowl(sprintf(_widget_refresh_msg, value), { header: _success_header });
146                                 $('#widget_global_slider').remove();
147                                 $('.widget-editbox [name=refresh_interval]').each(function() {
148                                         $(this).attr('value', value);
149                                 });
150                                 window.location.reload();
151                         } else {
152                                 $.jGrowl(_widget_refresh_error, { header: _error_header });
153                         }
154                 },
155                 error: function(obj, msg){$.jGrowl(_widget_global_refresh_error, { header: _error_header });}
156         });
160 *       Save widget order to database
162 function save_widget_order(order_str)
164         $.ajax(
165                 _site_domain + _index_page + "/widget/save_widgets_order/",
166                 {
167                         data: {
168                                 page: escape(_current_uri),
169                                 widget_str: order_str
170                         },
171                         type: 'POST'
172                 }
173          );
176 function control_widgets(item) {
177         var it = $(item);
178         if (it.hasClass('selected')) {
179                 $.fn.HideEasyWidget('widget-' + it.data('name') + '-' + it.data('instance_id'), window.easywidgets_obj);
180                 it.removeClass('selected').addClass('unselected').data('instance_id', '');
181         }
182         else {
183                 copy_widget_instance(it.data('name'), it.data('instance_id'), function(new_widget) {
184                         if (!it.data('instance_id')) {
185                                 it.data('instance_id', new_widget.data('instance_id')).attr('data-instance_id', new_widget.data('instance_id'));
186                         }
187                 });
188                 it.removeClass('unselected').addClass('selected');
189         }
192 function copy_widget_instance(name, instance_id, cb) {
193         $.ajax({
194                 url: _site_domain + _index_page + '/widget/copy_widget_instance',
195                 dataType: 'html',
196                 type: 'POST',
197                 data: {page: _current_uri, widget: name, 'instance_id': instance_id},
198                 success: function(data) {
199                         var new_widget;
200                         var this_widget = $('#widget-' + name + '-' + instance_id);
201                         if (this_widget.length) {
202                                 this_widget.after(data);
203                                 new_widget = this_widget.next('.widget');
204                         }
205                         else {
206                                 var container = $('#widget-placeholder');
207                                 container.append(data);
208                                 new_widget = container.find('.widget:last');
209                         }
210                         if (cb)
211                                 cb(new_widget);
212                 }
213         });
216 function save_widget_state(what, widget_name, instance_id)
218         $.ajax(
219                 _site_domain + _index_page + "/widget/save_widget_state/",
220                 {
221                         data: {
222                                 page: escape(_current_uri),
223                                 method: what,
224                                 name: widget_name,
225                                 instance_id: instance_id
226                         },
227                         type: 'POST'
228                 }
229         );
232 var loaded_widgets = {};
235 *       Ninja widget class
237 function widget(name, instance_id)
239         var self = this;
240         this.ajax_url = _site_domain + _index_page + '/widget/';
242         this._refresh_interval = 0;
243         this.save_interval = 0;
244         this.current_uri = _current_uri;
245         this.content_area = 'widget-content';
246         this.is_updating = false;
248         // initialize widget
249         var widget_ok = this.init_widget(name, instance_id);
250         if (widget_ok == false) {
251                 // here we should probably notify user that
252                 // the widget isn't found
253                 if(name) {
254                         $.jGrowl(sprintf(_widget_notfound_error, name), { header: _error_header });
255                 }
256                 return;
257         }
259         // only enable refresh and interval editing
260         // if we have a content_area and there's a sider div
261         if (this.content_area && $("#" + this.widget_id + " .refresh_slider").length) {
262                 this.set_refresh_interval(true);
263                 this.init_slider();
264         }
265         this.init_title_edit();
266         if (widget.widgets[name])
267                 for (var i in widget.widgets[name])
268                         widget.widgets[name][i].call(this);
270 widget.loadimg = new Image(16,16);
271 widget.loadimg.src = _site_domain + 'application/media/images/loading_small.gif';
274  *      Initialize some internal values.
275  */
276 widget.prototype.init_widget = function(name, instance_id) {
277         var self = this;
278         this.set_id(name, instance_id);
280         // check that widget isn't already loaded
281         if (loaded_widgets[this.id]) {
282                 return;
283         }
285         if (!$('#' + this.widget_id).text())
286                 return false;
287         this.current_interval = $('#' + this.widget_id + ' .refresh_interval').val();
288         this.title =  $('#' + this.id + '_title').text();
289         if (this.current_uri == 'external_widget/show_widget') {
290                 $('.widget-menu .widget-collapselink').hide();
291                 $('.widget-menu .widget-closelink').hide();
292                 $('#' + this.id + '_title').removeClass(this.id + '_editable');
293         }
295         $('#' + this.widget_id + '.duplicatable .widget-menu').prepend('<a class="widget-copylink" title="Copy this widget" href="#"><img alt="Copy" src="' + _site_domain + 'application/views/icons/12x12/copy.png"/></a>');
296         $('#' + this.widget_id + ' .widget-copylink').click(function(ev) {
297                 ev.preventDefault();
298                 copy_widget_instance(self.name, self.instance_id, function (new_widget) {
299                         $('.widget-selector').filter(':last').after('<li id="li-'+new_widget.data('name')+'-'+new_widget.data('instance_id')+'" data-name="'+new_widget.data('name')+'" data-instance_id="'+new_widget.data('instance_id')+'" class="selected widget-selector" onclick="control_widgets(this)">'+new_widget.find('#'+new_widget.data('name')+'-'+new_widget.data('instance_id')+'_title').text()+'</li>');
300                 });
301         });
302         loaded_widgets[this.id] = 1;
304         return true;
307 widget.prototype.set_current_uri = function(uri) {
308         this.current_uri = uri;
311 widget.prototype.set_id = function(name, instance_id) {
312         if (!name)
313                 return;
314         this.id = name + '-' + instance_id;
315         this.name = name;
316         this.instance_id = instance_id;
317         this.widget_id = 'widget-' + this.id;
321 *       Fetch current widget state through AJAX call
323 widget.prototype.update_display = function() {
324         var self = this;
326         if (this.content_area != false && $('#' + this.widget_id).is(':visible')) {
327                 if (this.is_updating || _is_refreshing) {
328                         /**
329                         * Prevent multiple instances of the same widget
330                         * from trying to fetch data at the same time as this
331                         * will possibly hog the system. Also prevent new
332                         * ajax calls when user has decided to reload the page
333                         */
334                         return;
335                 }
336                 this.is_updating = true;
338                 // add a loading img to indicate update progress
339                 $("#" + this.widget_id + ' .widget-header').append('<img src="' + widget.loadimg.src + '" class="widget_loadimg" />');
340                 $("#" + this.widget_id + ' .widget-header .widget_loadimg').css('opacity', 0.4).css('padding-left', '15px').css('width', '12px').css('height', '12px');
342                 var params = {
343                         widget_name: this.name,
344                         instance_id: this.instance_id,
345                         page: this.current_uri
346                 };
347                 $.ajax({
348                         url: this.ajax_url + "widget/" + this.name + "/index/?" + jQuery.param(params),
349                         dataType:'json',
350                         success: function(data) {
351                                 $("#" + self.widget_id + ' .' + self.content_area).html(data);
352                                 self.is_updating = false;
354                                 // remove load image
355                                 $("#" + self.widget_id + ' .widget-header .widget_loadimg').remove();
356                         }
357                 });
358         }
362 *       Save widget settings to db
364 widget.prototype.save_settings = function(data) {
365         var self = this;
366         $.ajax(
367                 this.ajax_url + "save_widget_setting/",
368                 {
369                         type: 'POST',
370                         data: data,
371                         complete: function() {
372                                 $.jGrowl(sprintf(_widget_settings_msg, self.name), { header: _success_header });
373                         }
374                 }
375         );
379 *       Save custom widget setting
381 widget.prototype.save_custom_val = function(newval, fieldname, cb) {
382         var self = this;
383         $.ajax(
384                 this.ajax_url + "save_dynamic_widget_setting/",
385                 {
386                         data: {
387                                 page: this.current_uri,
388                                 fieldvalue: newval,
389                                 fieldname: fieldname,
390                                 widget: this.name,
391                                 instance_id: this.instance_id
392                         },
393                         complete: function(data) {
394                                 if (typeof cb == 'function') {
395                                         cb.call(self, data);
396                                 }
397                                 $.jGrowl(sprintf(_widget_settings_msg, self.name), { header: _success_header });
398                         },
399                         type: 'POST'
400                 }
401         );
404 widget.widgets = {};
405 widget.register_widget_load = function(widget_name, cb) {
406         if (!widget.widgets[widget_name])
407                 widget.widgets[widget_name] = [cb];
408         else {
409                 for (var i =0; i <  widget.widgets[widget_name].length; i++) {
410                         if (widget.widgets[widget_name][i].toString() == cb.toString())
411                                 return
412                 }
413                 widget.widgets[widget_name].push(cb);
414         }
418  *      Set the refresh interval to use for widget
419  *      and also pass this value on to be saved to db
421 widget.prototype.set_refresh_interval = function(is_init){
422         var self = this;
423         if (this._refresh_interval) {
424                 clearInterval(this._refresh_interval);
425         }
427         if (this.current_interval>0) {
428                 var interval = (this.current_interval * 1000);
429                 //this._refresh_interval = setInterval("update_display()", interval);
430                 this._refresh_interval = setInterval(function() {self.update_display();}, interval);
431         }
433         if (!is_init) {
434                 // update widget settings
435                 var data = {page: this.current_uri, refresh_interval: this.current_interval, widget: this.name, instance_id: this.instance_id};
436                 this.save_settings(data);
437         }
441 *       Since the slider is possible to move rather fast,
442 *       we add a delay (timeout) to this before we do anything with it.
443 *       The timeout is cleared when a new value is selected and only saved
444 *       until there is no activity (new value) for 5 seconds
446 widget.prototype.control_save_interval = function() {
447         var self = this;
448         if (this.save_interval) {
449                 clearTimeout(this.save_interval);
450         }
451         this.save_interval = setTimeout(function() {self.set_refresh_interval();}, 5000);
454 widget.prototype.init_slider = function() {
455         var self = this;
456         $("#" + this.widget_id + " .refresh_slider").slider({
457                 value: this.current_interval,
458                 min: 0,
459                 max: 500,
460                 step: 10,
461                 slide: function(event, ui) {
462                         $("#" + self.widget_id + " .refresh_interval").val(ui.value);
463                         self.current_interval = ui.value;
464                         self.control_save_interval();
465                 }
466         });
467         $("#" + this.widget_id + " .refresh_interval").val($("#" + this.widget_id + " .refresh_slider").slider("value")).change(function() {
468                 $("#" + self.widget_id + " .refresh_slider").slider("value", $(this).val());
469                 self.current_interval = $(this).val();
470                 self.control_save_interval();
471         });
475 widget.prototype.init_title_edit = function() {
476         var self = this;
477         $("." + this.id + "_editable").editable(function(value, settings) {
478                 var data = {page: self.current_uri, widget:self.name, instance_id:self.instance_id, widget_title:value};
479                 value = $.trim(value);
480                 // don't save an empty title
481                 if (value.length) {
482                         self.save_settings(data);
483                         self.title = value;
484                 } else {
485                         value = self.title;
486                 }
487                 return value;
488         }, {
489                 type : 'text',
490                 style : 'margin-top: -4px',
491                 event : 'dblclick',
492                 width : 'auto',
493                 height : '14px',
494                 submit : 'OK',
495                 cancel : 'cancel',
496                 placeholder:'Double-click to edit'
497         });