Avail feature updated
[ninja.git] / application / media / js / jquery.easywidgets.js
blob62c0b75d35fc74c13e55e4df3c5fa41b8fc50718
1 /**
2 * op5 NOTE: I found this online and stuck it in here. It doesn't
3 * necessarily work for us, but at least now we have the expanded
4 * source.
6 * Easy Widgets 2.0 for jQuery and jQuery UI
8 * David Esperalta <http://www.davidesperalta.com/>
9 *
10 * More information, examples and latest version in the website:
11 * <http://www.bitacora.davidesperalta.com/archives/projects/easywidgets/>
13 * I based my work on a tutorial writen by James Padolsey
14 * <http://nettuts.com/tutorials/javascript-ajax/inettuts/>
16 * You should have received a copy of the GNU General Public License
17 * along with Easy Widgets. If not, see <http://www.gnu.org/licenses/>
20 (function($) {
22 ///////////////////////////
23 // Public plugin methods //
24 ///////////////////////////
26 /**
27 * Main public method of plugin
29 * Call this method to initialize the plugin, that prepare all the available
30 * widgets in the document, and execute the appropiate task on every widget.
32 * Basically call the InitializeWidgets() private function, with the second
33 * param by default: using this method we not prepare widgets on demand, but
34 * prepare all widgets found in the document.
36 * See the mentioned function for more details, and how we use this function
37 * too in another plugin public method: AddEasyWidget(), see it for details.
39 * @access public
40 * @see InitializeWidgets()
41 * @param settings Array with the plugin settings
42 * @return Boolean True in every case
45 $.fn.EasyWidgets = function(settings) {
46 InitializeWidgets(settings, false);
47 return true;
50 /**
51 * Add a new widget "on demand"
53 * This public method can be use to add a new widget "on demand" into certain
54 * place. The method need the HTML markup for widget, and this can specify
55 * all the available widget options.
57 * In this method we use the private InitializeWidgets() function, also used
58 * in another public method of the plugin: EasyWidgets(). Note that in this
59 * case the second param for this funtion specify that in this case we want
60 * to add a widget "on demand".
62 * For more details see the refered private function.
64 * @access public
65 * @see InitializeWidgets()
66 * @param html String Widget HTML markup
67 * @param placeId String Element ID to place the Widget
68 * @param settings Array with the plugin settings
69 * @return Boolean True if widget is finally added, False if not
72 $.fn.AddEasyWidget = function(html, placeId, settings) {
73 var canAdd = true;
74 var widget = $(html);
75 var s = $.extend(true, $.fn.EasyWidgets.defaults, settings);
76 if ($.isFunction(s.callbacks.onAddQuery)) {
77 canAdd = s.callbacks.onAddQuery(widget, placeId);
79 if (canAdd) {
80 $('#' + placeId).append(html);
81 if ($.isFunction(s.callbacks.onAdd)) {
82 s.callbacks.onAdd(widget, placeId);
84 InitializeWidgets(s, true);
85 return true;
86 } else {
87 return false;
91 /**
92 * Disable all widgets (fix then) in document
94 * This public method can be use to fix the widgets on document, in other
95 * words, disable the widgets, because the user cant move this after the
96 * widgets as been disables.
98 * @access public
99 * @see EnableEasyWidgets()
100 * @param settings Array with the plugin settings
101 * @return Boolean True if widgets are finally disables, False if not
104 $.fn.DisableEasyWidgets = function(settings) {
105 var canDisable = true;
106 var s = $.extend(true, $.fn.EasyWidgets.defaults, settings);
107 if ($.isFunction(s.callbacks.onDisableQuery)) {
108 canDisable = s.callbacks.onDisableQuery();
110 if (canDisable) {
111 $(s.selectors.places).sortable('disable');
112 $(s.selectors.widget).each(function() {
113 var widget = $(this);
114 if (widget.hasClass(s.options.movable)) {
115 widget.find(s.selectors.header).css('cursor', 'default');
116 widget.find(s.selectors.widgetMenu).css('display', 'none');
119 if ($.isFunction(s.callbacks.onDisable)) {
120 s.callbacks.onDisable();
122 SetCookie(s.cookies.disableName, 1, s);
123 return true;
124 } else {
125 return false;
130 * Enable all widgets (make movables) in document
132 * This public method can be use to make movables the widgets on document,
133 * in other words, enable the widgets, because the user can move this after
134 * the widgets as been enables.
136 * Note that the widgets are enables by default, so, this method have sense
137 * in case that you use before another method of plugin: DisableEasyWidgets()
139 * @access public
140 * @see DisableEasyWidgets()
141 * @param settings Array with the plugin settings
142 * @return Boolean True if widgets are finally enables, False if not
145 $.fn.EnableEasyWidgets = function(settings) {
146 var canEnable = true;
147 var s = $.extend(true, $.fn.EasyWidgets.defaults, settings);
148 if ($.isFunction(s.callbacks.onEnableQuery)) {
149 canEnable = s.callbacks.onEnableQuery();
151 if (canEnable) {
152 $(s.selectors.places).sortable('enable');
153 $(s.selectors.widget).each(function() {
154 var widget = $(this);
155 if (widget.hasClass(s.options.movable)) {
156 widget.find(s.selectors.header).css('cursor', 'move');
159 if ($.isFunction(s.callbacks.onEnable)) {
160 s.callbacks.onEnable();
162 if (s.behaviour.useCookies) {
163 SetCookie(s.cookies.disableName, 0, s);
165 return true;
166 } else {
167 return false;
172 * Hide all widgets in document
174 * This public method can be use to hide all the document visible widgets.
175 * Note that this method and related is thinking if you use the plugin
176 * cookies feature.
178 * In other case, you can use directly something like this:
180 * $('widgets-class-selector').hide();
182 * So, this method can sense if you use the plugin cookies feature, because
183 * the plugin update the appropiate cookie with the needed information, to
184 * mantain the widgets hidden even if user refresh the page.
186 * @access public
187 * @see HideEasyWidget()
188 * @see ShowEasyWidgets()
189 * @param settings Array with the plugin settings
190 * @return Boolean True in every case
193 $.fn.HideEasyWidgets = function(settings) {
194 var s = $.extend(true, $.fn.EasyWidgets.defaults, settings);
195 $(s.selectors.widget + ':visible').each(function() {
196 var canHide = true;
197 var thisWidget = $(this);
198 var thisWidgetId = thisWidget.attr('id');
199 if ($.isFunction(s.callbacks.onHideQuery)) {
200 canHide = s.callbacks.onHideQuery(thisWidget);
202 if (canHide) {
203 ApplyEffect(
204 thisWidget,
205 s.effects.widgetHide,
206 s.effects.effectDuration,
207 false
209 if (s.behaviour.useCookies && thisWidgetId) {
210 UpdateCookie(thisWidgetId, s.cookies.closeName, s);
212 if ($.isFunction(s.callbacks.onHide)) {
213 s.callbacks.onHide(thisWidget);
217 return true;
221 * Show all widgets in document
223 * This public method can be use to show all the document hidden widgets.
224 * Note that this method and related is thinking if you use the plugin
225 * cookies feature.
227 * In other case, you can use directly something like this:
229 * $('widgets-class-selector').show();
231 * So, this method can sense if you use the plugin cookies feature, because
232 * the plugin update the appropiate cookie with the needed information, to
233 * mantain the widgets showing even if user refresh the page.
235 * @access public
236 * @see ShowEasyWidget()
237 * @see HideEasyWidgets()
238 * @param settings Array with the plugin settings
239 * @return Boolean True in every case
242 $.fn.ShowEasyWidgets = function(settings) {
243 var s = $.extend(true, $.fn.EasyWidgets.defaults, settings);
244 $(s.selectors.widget + ':hidden').each(function() {
245 var canShow = true;
246 var widget = $(this);
247 var widgetId = widget.attr('id');
248 var haveId = ($.trim(widgetId) != '');
249 if ($.isFunction(s.callbacks.onShowQuery)) {
250 canShow = s.callbacks.onShowQuery(widget);
252 if (canShow) {
253 ApplyEffect(
254 widget,
255 s.effects.widgetShow,
256 s.effects.effectDuration,
257 true
259 if (haveId && s.behaviour.useCookies) {
260 CleanCookie(widgetId, s.cookies.closeName, s);
262 if ($.isFunction(s.callbacks.onShow)) {
263 s.callbacks.onShow(widget);
267 return true;
271 * Show an individual widget
273 * This public method can be use to show an individual hidden widget.
274 * Note that this method and related is thinking if you use the plugin
275 * cookies feature.
277 * In other case, you can use directly something like this:
279 * $('widget-id-selector').show();
281 * So, this method can sense if you use the plugin cookies feature, because
282 * the plugin update the appropiate cookie with the needed information, to
283 * mantain the widgets showing even if user refresh the page.
285 * @access public
286 * @see HideEasyWidget()
287 * @see ShowEasyWidgets()
288 * @param widgetId String Widget element identifier
289 * @param settings Array with the plugin settings
290 * @return Boolean True if widget finally is show, False if not
293 $.fn.ShowEasyWidget = function(widgetId, settings) {
294 var canShow = true;
295 var widget = $('#' + widgetId);
296 if (widget.css('display') == 'none') {
297 var s = $.extend(true, $.fn.EasyWidgets.defaults, settings);
298 if ($.isFunction(s.callbacks.onShowQuery)) {
299 canShow = s.callbacks.onShowQuery(widget);
301 if (canShow) {
302 ApplyEffect(
303 widget,
304 s.effects.widgetShow,
305 s.effects.effectDuration,
306 true
308 if (s.behaviour.useCookies) {
309 CleanCookie(widgetId, s.cookies.closeName, s);
311 if ($.isFunction(s.callbacks.onShow)) {
312 s.callbacks.onShow(widget);
314 return true;
315 } else {
316 return false;
318 } else {
319 return false;
324 * Hide an individual widget
326 * This public method can be use to hide an individual visible widget.
327 * Note that this method and related is thinking if you use the plugin
328 * cookies feature.
330 * In other case, you can use directly something like this:
332 * $('widget-id-selector').hide();
334 * So, this method can sense if you use the plugin cookies feature, because
335 * the plugin update the appropiate cookie with the needed information, to
336 * mantain the widgets showing even if user refresh the page.
338 * @access public
339 * @see ShowEasyWidget()
340 * @see HideEasyWidgets()
341 * @param widgetId String Widget element identifier
342 * @param settings Array with the plugin settings
343 * @return Boolean True if widget finally is hide, False if not
346 $.fn.HideEasyWidget = function(widgetId, settings) {
347 var canHide = true;
348 var widget = $('#' + widgetId);
349 if (widget.css('display') != 'none') {
350 var s = $.extend(true, $.fn.EasyWidgets.defaults, settings);
351 if ($.isFunction(s.callbacks.onHideQuery)) {
352 canHide = s.callbacks.onHideQuery(widget);
354 if (canHide) {
355 ApplyEffect(
356 widget,
357 s.effects.widgetHide,
358 s.effects.effectDuration,
359 false
361 if (s.behaviour.useCookies) {
362 UpdateCookie(widgetId, s.cookies.closeName, s);
364 if ($.isFunction(s.callbacks.onHide)) {
365 s.callbacks.onHide(widget);
367 return true;
368 } else {
369 return false;
371 } else {
372 return false;
376 /////////////////////////////
377 // Plugin default settings //
378 /////////////////////////////
381 * Plugin default settings
383 * This is the settings that plugin use in case that you not provide your
384 * own plugin settings. Also, you dont need to provide all the settings, but
385 * change only that you need: the plugin use the default settings that you
386 * not provided, and also the settings that you provide.
388 * In other words, the plugin merge your own settings with plugin defaults.
391 $.fn.EasyWidgets.defaults = {
393 // Behaviour of the plugin
394 behaviour: {
396 // Miliseconds delay between mousedown and drag start
397 dragDelay: 100,
399 // Miliseconds delay between mouseup and drag stop
400 dragRevert: 100,
402 // Determinme the opacity of Widget when start drag
403 dragOpacity: 0.8,
405 // Cookies (require Cookie plugin) to store positions and states
406 useCookies: false
409 // Some effects that can be apply sometimes
410 effects: {
412 // Miliseconds for effects duration
413 effectDuration: 500,
415 // Can be none, slide or fade
416 widgetShow: 'none',
417 widgetHide: 'none',
418 widgetClose: 'none',
419 widgetExtend: 'none',
420 widgetCollapse: 'none',
421 widgetOpenEdit: 'none',
422 widgetCloseEdit: 'none',
423 widgetCancelEdit: 'none'
426 // Only for the optional cookie feature
427 cookies: {
429 // Cookie path
430 path: '',
432 // Cookie domain
433 domain: '',
435 // Cookie expiration time in days
436 expires: 90,
438 // Store a secure cookie?
439 secure: false,
441 // Cookie name for close Widgets
442 closeName: 'ew-close',
444 // Cookie name for disable all Widgets
445 disableName: 'ew-disable',
447 // Cookie name for positined Widgets
448 positionName: 'ew-position',
450 // Cookie name for collapsed Widgets
451 collapseName: 'ew-collapse'
454 // Options name to use in the HTML markup
455 options: {
457 // To recognize a movable Widget
458 movable: 'movable',
460 // To recognize a editable Widget
461 editable: 'editable',
463 // To recognize a collapse Widget
464 collapse: 'collapse',
466 // To recognize a removable Widget
467 removable: 'removable',
469 // To recognize a collapsable Widget
470 collapsable: 'collapsable',
472 // To recognize Widget that require confirmation when remove
473 closeConfirm: 'closeconfirm'
476 // Callbacks functions
477 callbacks: {
479 // When a Widget is added on demand, send the widget object and place ID
480 onAdd: null,
482 // When a editbox is closed, send the link and the widget objects
483 onEdit: null,
485 // When a Widget is show, send the widget object
486 onShow: null,
488 // When a Widget is hide, send the widget object
489 onHide: null,
491 // When a Widget is closed, send the link and the widget objects
492 onClose: null,
494 // When Widgets are enabled using the appropiate public method
495 onEnable: null,
497 // When a Widget is extend, send the link and the widget objects
498 onExtend: null,
500 // When Widgets are disabled using the appropiate public method
501 onDisable: null,
503 // When a editbox is closed, send a ui object, see jQuery::sortable()
504 onDragStop: null,
506 // When a Widget is collapse, send the link and the widget objects
507 onCollapse: null,
509 // When a Widget is try to added, send the widget object and place ID
510 onAddQuery: null,
512 // When a editbox is try to close, send the link and the widget objects
513 onEditQuery: null,
515 // When a Widget is try to show, send the widget object
516 onShowQuery: null,
518 // When a Widget is try to hide, send the widget object
519 onHideQuery: null,
521 // When a Widget is try to close, send the link and the widget objects
522 onCloseQuery: null,
524 // When a editbox is cancel (close), send the link and the widget objects
525 onCancelEdit: null,
527 // When Widgets are enabled using the appropiate public method
528 onEnableQuery: null,
530 // When a Widget is try to expand, send the link and the widget objects
531 onExtendQuery: null,
533 // When Widgets are disabled using the appropiate public method
534 onDisableQuery: null,
536 // When a Widget is try to expand, send the link and the widget objects
537 onCollapseQuery: null,
539 // When a editbox is try to cancel, send the link and the widget objects
540 onCancelEditQuery: null,
542 // When one Widget is repositioned, send the positions serialization
543 onChangePositions: null,
545 // When Widgets need repositioned, get the serialization positions
546 onRefreshPositions: null
549 // Selectors in HTML markup. All can be change by you, but not all is
550 // used in the HTML markup. For example, the "editLink" or "closeLink"
551 // is prepared by the plugin for every Widget.
552 selectors: {
554 // Container of a Widget (into another element that use as place)
555 // The container can be "div" or "li", for example. In the first case
556 // use another "div" as place, and a "ul" in the case of "li".
557 container: 'div',
559 // Class identifier for a Widget
560 widget: '.widget',
562 // Class identifier for a Widget place (parents of Widgets)
563 places: '.widget-place',
565 // Class identifier for a Widget header (handle)
566 header: '.widget-header',
568 // Class for the Widget header menu
569 widgetMenu: '.widget-menu',
571 // Class identifier for Widget editboxes
572 editbox: '.widget-editbox',
574 // Class identifier for Widget content
575 content: '.widget-content',
577 // Class identifier for editbox close link or button, for example
578 closeEdit: '.widget-close-editbox',
580 // Class identifier for a Widget edit link
581 editLink: '.widget-editlink',
583 // Class identifier for a Widget close link
584 closeLink: '.widget-closelink',
586 // Class identifier for Widgets placehoders
587 placeHolder: 'widget-placeholder',
589 // Class identifier for a Widget collapse link
590 collapseLink: '.widget-collapselink'
593 // To be translate the plugin into another languages
594 // But this variables can be used to show images instead
595 // links text, if you preffer. In this case set the HTML
596 // of the IMG elements.
597 i18n: {
599 // Widget edit link text
600 editText: 'Edit',
602 // Widget close link text
603 closeText: 'Close',
605 // Widget extend link text
606 extendText: 'Extend',
608 // Widget collapse link text
609 collapseText: 'Collapse',
611 // Widget cancel edit link text
612 cancelEditText: 'Cancel',
614 // Widget edition link title
615 editTitle: 'Edit this widget',
617 // Widget close link title
618 closeTitle: 'Close this widget',
620 // Widget confirmation dialog message
621 confirmMsg: 'Remove this widget?',
623 // Widget cancel edit link title
624 cancelEditTitle: 'Cancel edition',
626 // Widget extend link title
627 extendTitle: 'Extend this widget',
629 // Widget collapse link title
630 collapseTitle: 'Collapse this widget'
634 //////////////////////////////
635 // Private plugin functions //
636 //////////////////////////////
639 * Initialize the widgets
641 * This private function is used in two methods of the plugin, the main
642 * public method: EasyWidgets() and AddEasyWidget() public method. In other
643 * words, this function is the main function of the plugin, and is use to
644 * initialize the widgets at a first time, and initialize the widgets added
645 * on demand.
647 * This function separate different things into other private functions:
648 * for more details see the related and used here plugin private functions.
650 * @access private
651 * @param settings Array with the plugin settings
652 * @param widgetOnDemand Boolean Widget added on demand or not
653 * @return Boolean True in every case
656 function InitializeWidgets(
657 settings, widgetOnDemand) {
658 var b = widgetOnDemand;
659 var d = $.fn.EasyWidgets.defaults;
660 var s = $.extend(true, d, settings);
661 $(s.selectors.widget).each(function() {
662 PrepareWidgetBehaviour($(this), b, s);
664 RepositionedWidgets(s);
665 MakeWidgetsSortables(s);
666 CleanWidgetsCookies(s, b);
667 return true;
671 * Prepare the widgets behaviour
673 * This private function is called from another: InitializeWidgets()
674 * to prepare the behaviour of a found widget: append the widget menu
675 * if is needed, put into this the appropiate links, etc.
677 * As you can see, another private plugin functions are used here,
678 * we refer you to this functions for more details about this task.
679 * However, here is an important question about this function logical:
681 * This function can be use to deal with "normal" widgets and widgets
682 * added on demand. This function can be called to prepare certain
683 * widget that as been prepared when page onload: so, this widgets
684 * cannot be prepared again.
686 * To evit the duplication of the widget menus, basically, we find
687 * for this widget menu, and, if is empty, this widget need to be
688 * prepared, but, if this widget have a menu yet, cannot need to
689 * be prepared.
691 * This condition only have sense when added widgets on demand, if
692 * not is the case, no one widget have a menu before prepared, so,
693 * are prepared here the first time that this function is called.
695 * @access private
696 * @see InitializeWidgets()
697 * @see AddWidgetEditLink()
698 * @see AddWidgetRemoveLink()
699 * @see AddWidgetCollapseLink()
700 * @param widget jQuery object with a widget
701 * @param widgetOnDemand Boolean Widget added on demand or not
702 * @param settings Array with the plugin settings
703 * @return Boolean True if widget are prepared, False if is yet prepared
706 function PrepareWidgetBehaviour(widget, widgetOnDemand, settings) {
707 var s = settings;
708 var widgetMenu = widget.find(s.selectors.widgetMenu);
709 if (widgetMenu.html() == null) {
710 var widgetId = widget.attr('id');
711 var haveId = ($.trim(widgetId) != '');
712 widget.find(s.selectors.editbox).hide();
713 if (widgetOnDemand && haveId && s.behaviour.useCookies) {
714 // Force this widget out of closed widgets cookie
715 // because in other case is possible that widget
716 // are added, but in fact not show in the document
717 CleanCookie(widgetId, s.cookies.closeName, s);
719 if (!widgetOnDemand && haveId && s.behaviour.useCookies
720 && GetCookie(s.cookies.closeName) != null) {
721 var cookieValue = GetCookie(s.cookies.closeName);
722 if (cookieValue.indexOf(widgetId) != -1) {
723 // But in case of not on demand widget, is possible
724 // to hide the widget, if is present in the appropiate
725 // related cookie
726 widget.hide();
729 var menuWrap = '<span class="' + s.selectors
730 .widgetMenu.replace(/\./, '') + '"></span>';
731 widget.find(s.selectors.header).append(menuWrap);
732 // Now this menu is a valid wrap to add the links
733 widgetMenu = widget.find(s.selectors.widgetMenu);
734 // The order of this function call is important
735 // because determine the order of links appear
736 AddWidgetCollapseLink(widget, widgetMenu, s);
737 AddWidgetEditLink(widget, widgetMenu, s);
738 AddWidgetRemoveLink(widget, widgetMenu, s);
739 return true;
740 } else {
741 return false;
746 * Repositioned the widgets
748 * This private function is called from InitializeWidgets() and is used
749 * to repositioned the widgets in the appropiate places into the document.
751 * Some important question about this function is that the plugin can
752 * repositioned the widgets follow certain string, that containt the
753 * needed information.
755 * This string is produced in WidgetsPositionsChange() private function,
756 * and bassically contain the places IDs and the widgets IDs saved in
757 * a know format, that here we read to apply just later.
759 * Take a look at this: the mentioned string is saved in a cookie if you
760 * use the cookies feature of the plugin. But in any case the plugin send
761 * to you this string in the "onChangePositions()" callback.
763 * What is this? Suppose that you cannot use cookies, but still want to
764 * repositioned the widgets. So, you can get the refered string and save
765 * it in a database, for example.
767 * Then, just when this function is executed, you can provide this string
768 * returning it in the "onRefreshPositions()" plugin callback. Then, if you
769 * provide here a string that contain the widgets positions, the plugin use
770 * this string to repositioned the widgets.
772 * If you use the cookies plugin feature, the widget read the appropiate
773 * cookie, get the string previously saved (see WidgetsPositionsChange())
774 * and repositioned the widgets. Of course, if you not provide any string
775 * and also not use the cookies feature, the widgets cannot be positioned.
777 * Another thing more. You can see at WidgetsPositionsChange() how we
778 * conform the appropiate string, so, in this function we read the string
779 * based on the appropiate format. This string is like this:
781 * place-1=widget-1,widget-2|place-1=widget-3,widget-4
783 * Note one more thing: the order of the string is not casual: reflect the
784 * real order of the places and widgets in the document when the string is
785 * formed, so, the order of the widgets after this function is executed is
786 * the correct, because we follow the string as is.
788 * @access private
789 * @see InitializeWidgets()
790 * @see PrepareSortablePlaces()
791 * @see WidgetsPositionsChange()
792 * @return Boolean True in every case
795 function RepositionedWidgets(settings) {
796 var s = settings;
797 var positions = '';
798 if ($.isFunction(s.callbacks.onRefreshPositions)) {
799 positions = s.callbacks.onRefreshPositions();
801 // Only if not provide a string widget positions,
802 // use cookies and the appropiate cookie is not empty
803 if (($.trim(positions) == '') && s.behaviour.useCookies
804 && GetCookie(s.cookies.positionName) != null) {
805 // We get the widgets positions from the cookie
806 positions = GetCookie(s.cookies.positionName)
808 if ($.trim(positions) != '') {
809 // Get the widgets places IDs and widgets IDs
810 var places = positions.split('|');
811 var totalPlaces = places.length;
812 for (var i = 0; i < totalPlaces; i++) {
813 // Every part contain a place ID and possible widgets IDs
814 var place = places[i].split('=');
815 // Validate (more or less) the format of the part that must
816 // contain two element: A place ID and one or more widgets IDs
817 if (place.length == 2) {
818 // Subpart one: the place ID
819 var placeSel = '#' + place[0];
820 // Subpart two: one or more widgets IDs
821 var widgets = place[1].split(',');
822 var totalWidgets = widgets.length;
823 // Here we have a place and one or more widgets IDs
824 for (var j = 0; j < totalWidgets; j++) {
825 if ($.trim(widgets[j]) != '') {
826 // So, append every widget in the appropiate place
827 var widgetSel = '#' + widgets[j];
828 $(widgetSel).appendTo(placeSel);
834 return true;
838 * Make widgets sortables
840 * This private function make found widgets as sortable items. This
841 * is called from another plugin private funtion: InitializeWidgets()
843 * As you can see, another private plugin functions are used here:
844 * we refer you to this functions for more details about this task.
846 * @access private
847 * @see InitializeWidgets()
848 * @see GetSortableItems()
849 * @see PrepareSortableHeaders()
850 * @see PrepareSortablePlaces()
851 * @param settings Array with the plugin settings
852 * @return Boolean True in every case
855 function MakeWidgetsSortables(settings) {
856 var sortables = GetSortableItems(settings);
857 PrepareSortableHeaders(sortables, settings);
858 PrepareSortablePlaces(sortables, settings);
859 return true;
863 * Find widgets and places as sortables items
865 * And return it. This function is called from MakeWidgetsSortables()
866 * to find the widgets and places as sortable items to work with this.
868 * @access private
869 * @see MakeWidgetsSortables()
870 * @param settings Array with the plugin settings
871 * @return Boolean True in every case
874 function GetSortableItems(settings) {
875 var fixesSel = '';
876 var s = settings;
877 // Iterate all the widgets in document
878 $(s.selectors.widget).each(function(count) {
879 // When found a not movable widget
880 if (!$(this).hasClass(s.options.movable)) {
881 // Try to get the widget ID
882 if (!this.id) {
883 // And if not found prepare a special one
884 this.id = 'fixed-widget-id-' + count;
886 // Because this widget (fixed) not can be
887 // put as a sortable item, so, add to the
888 // fixed widgets selector, to use bellow
889 if (fixesSel == '') {
890 fixesSel += '#' + this.id;
891 } else {
892 fixesSel += ',' + '#' + this.id;
896 // We prepare now the widget that cannot be put as
897 // sortable items, because are fixed widgets. We cannot
898 // use directly the fixed widgets selectors, because is
899 // no one fixed widget is found the selector is like this:
900 // :not(), that is, a emtpy "not selector", and this cause
901 // problems with jQuery version 1.3
902 var notFixes = '';
903 if ($.trim(fixesSel) == '') {
904 // So, if no fixed widgets are found, dont use the not selector
905 notFixes = '> ' + s.selectors.container;
906 } else {
907 // Use only in case that one or more fixed widgets are found
908 notFixes = '> ' + s.selectors.container + ':not(' + fixesSel + ')';
910 // Its all. Return not fixed widgets and places as sortable items
911 return $(notFixes, s.selectors.places);
915 * Prepare sortables widgets headers
917 * This private function is called from another: MakeWidgetsSortables()
918 * and is used to prepare the widget headers as sortable items. Some
919 * behaviour is needed here, and the mayor part is based in the sortable
920 * feature of the jQuery UI library.
922 * In other words, this function prepare the widgets sortable headers
923 * to can be use as the widget handle, that the users can be use to move
924 * the widget into one place to another.
926 * For more information we refer you to the jQuery UI sortable feature
927 * documentation at this website for example: <http://www.api.jquery.com/>
929 * @access private
930 * @see MakeWidgetsSortables()
931 * @param sortableItems jQuery object with found sortable items
932 * @param settings Array with the plugin settings
933 * @return Boolean True in every case
936 function PrepareSortableHeaders(sortableItems, settings) {
937 var s = settings;
938 sortableItems.find(s.selectors.header).css({
939 cursor: 'move'
940 }).mousedown(function(e) {
941 var header = $(this);
942 var widget = header.parent();
943 sortableItems.css({ width: '' });
944 widget.css({
945 width: widget.width() + 'px'
947 }).mouseup(function() {
948 var header = $(this);
949 var widget = header.parent();
950 if (!widget.hasClass('dragging')) {
951 widget.css({ width: '' });
952 } else {
953 $(s.selectors.places).sortable('disable');
956 return true;
960 * Prepare sortables widgets places
962 * This private function is called from another: MakeWidgetsSortables()
963 * and is used to prepare the widget places as sortable items. Some
964 * behaviour is needed here, and the mayor part is based in the sortable
965 * feature of the jQuery UI library.
967 * For more information we refer you to the jQuery UI sortable feature
968 * documentation at this website for example: <http://www.api.jquery.com/>
970 * @access private
971 * @see MakeWidgetsSortables()
972 * @see WidgetsPositionsChange()
973 * @param sortableItems jQuery object with found sortable items
974 * @param settings Array with the plugin settings
975 * @return Boolean True in every case
978 function PrepareSortablePlaces(sortableItems, settings) {
979 var s = settings;
980 $(s.selectors.places).sortable('destroy');
981 $(s.selectors.places).sortable({
982 items: sortableItems,
983 containment: 'document',
984 forcePlaceholderSize: true,
985 handle: s.selectors.header,
986 delay: s.behaviour.dragDelay,
987 revert: s.behaviour.dragRevert,
988 opacity: s.behaviour.dragOpacity,
989 connectWith: $(s.selectors.places),
990 placeholder: s.selectors.placeHolder,
991 start: function(e, ui) {
992 $(ui.helper).addClass('dragging');
993 return true;
995 stop: function(e, ui) {
996 WidgetsPositionsChange(s);
997 $(ui.item).css({ width: '' });
998 $(ui.item).removeClass('dragging');
999 $(s.selectors.places).sortable('enable');
1000 if ($.isFunction(s.callbacks.onDragStop)) {
1001 s.callbacks.onDragStop(e, ui);
1003 return true;
1006 // Ok, we take this place to disable widgets based on certain cookie
1007 if (s.behaviour.useCookies && (GetCookie(s.cookies.disableName) == 1)) {
1008 $.fn.DisableEasyWidgets(s);
1010 return true;
1014 * Handle the widgets positions changes
1016 * This function is called from the "stop" event of sortable widgets as
1017 * you can see here: PrepareSortablePlaces(), and is used to provide to
1018 * you of a string that contain the widgets positions in certain format.
1020 * This string structure is like:
1022 * place-1=widget-1,widget-2|place-1=widget-3,widget-4
1024 * See bellow how we conform this. You can save this string in a database
1025 * for example, and provide latter, when the "onRefreshPositions()" callback
1026 * is executed. So, the plugin use this string to repositioned the widgets
1027 * as you can see in RepositionedWidgets() function.
1029 * @access private
1030 * @see RepositionedWidgets()
1031 * @see PrepareSortablePlaces()
1032 * @param settings Array with the plugin settings
1033 * @return Boolean True in every case
1036 function WidgetsPositionsChange(settings) {
1037 var s = settings;
1038 var positions = '';
1039 $(s.selectors.places).each(function() {
1040 var widgets = '';
1041 var place = $(this);
1042 var places = place.attr('id') + '=';
1043 place.children(s.selectors.widget).each(function() {
1044 var widget = this;
1045 var widgetId = widget.id;
1046 var haveId = ($.trim(widgetId) != '');
1047 if (haveId) {
1048 if (widgets == '') {
1049 widgets += widgetId;
1050 } else {
1051 widgets += ',' + widgetId;
1055 places += widgets;
1056 if (positions == '') {
1057 positions += places;
1058 } else {
1059 positions += '|' + places;
1062 // You can save the positions string in a database, for example,
1063 // using the "onChangePositions()" plugin callback. So, when the
1064 // "onRefreshPositions()" callback is executed, you can retrieve
1065 // the string and returnt it: so the plugin use this string to
1066 // repositioned the widgets.
1067 if ($.isFunction(s.callbacks.onChangePositions)) {
1068 s.callbacks.onChangePositions(positions);
1070 // @todo Maybe we only put the positions on the cookie
1071 // if the user font use the "onChangePositions()" callback, because
1072 // at this time, ever if no use the cookie value (the user provide)
1073 // the positions from "onRefreshPositions()" callback) the positions
1074 // are saved in the cookie...
1075 if (s.behaviour.useCookies) {
1076 // However, you need to use the cookies feature
1077 // to make possible the widgets repositioned
1078 if (GetCookie(s.cookies.positionName) != positions) {
1079 SetCookie(s.cookies.positionName, positions, s);
1082 return true;
1086 * Prepare a widget collapse menu link
1088 * @access private
1089 * @see PrepareWidgetBehaviour()
1090 * @param widget jQuery object with a widget encapsulation
1091 * @param widgetMenu jQuery object with a widget menu encapsulation
1092 * @param settings Array with the plugin settings
1093 * @return Boolean Truein every case
1096 function AddWidgetCollapseLink(widget, widgetMenu, settings) {
1097 var s = settings;
1098 var link = '';
1099 var widgetId = widget.attr('id');
1100 var haveId = $.trim(widgetId) != '';
1101 var content = widget.find(s.selectors.content);
1102 if (widget.hasClass(s.options.collapsable)) {
1103 if (widget.hasClass(s.options.collapse)) {
1104 link = MenuLink(
1105 s.i18n.extendText,
1106 s.i18n.extendTitle,
1107 s.selectors.collapseLink
1109 content.hide();
1110 } else {
1111 link = MenuLink(
1112 s.i18n.collapseText,
1113 s.i18n.collapseTitle,
1114 s.selectors.collapseLink
1117 if (haveId && s.behaviour.useCookies &&
1118 GetCookie(s.cookies.collapseName) != null) {
1119 var cookieValue = GetCookie(s.cookies.collapseName);
1120 if (cookieValue.indexOf(widgetId) != -1) {
1121 link = MenuLink(
1122 s.i18n.extendText,
1123 s.i18n.extendTitle,
1124 s.selectors.collapseLink
1126 content.hide();
1129 $(link).mousedown(function(e) {
1130 e.stopPropagation();
1131 }).click(function() {
1132 var canExtend = true;
1133 var canCollapse = true;
1134 var link = $(this);
1135 var widget = link.parents(s.selectors.widget);
1136 var widgetId = widget.attr('id');
1137 var haveId = $.trim(widgetId) != '';
1138 var content = widget.find(s.selectors.content);
1139 var contentVisible = content.css('display') != 'none';
1140 link.blur();
1141 if (contentVisible) {
1142 if ($.isFunction(s.callbacks.onCollapseQuery)) {
1143 canCollapse = s.callbacks.onCollapseQuery(link, widget);
1145 if (canCollapse) {
1146 ApplyEffect(
1147 content,
1148 s.effects.widgetCollapse,
1149 s.effects.effectDuration,
1150 false
1152 link.html(s.i18n.extendText);
1153 link.attr('title', s.i18n.extendTitle);
1154 if (s.behaviour.useCookies && widgetId) {
1155 UpdateCookie(widgetId, s.cookies.collapseName, s);
1157 if ($.isFunction(s.callbacks.onCollapse)) {
1158 s.callbacks.onCollapse(link, widget);
1161 } else {
1162 if ($.isFunction(s.callbacks.onExtendQuery)) {
1163 canExtend = s.callbacks.onExtendQuery(link, widget);
1165 if (canExtend) {
1166 link.html(s.i18n.collapseText);
1167 link.attr('title', s.i18n.collapseTitle);
1168 ApplyEffect(
1169 content,
1170 s.effects.widgetExtend,
1171 s.effects.effectDuration,
1172 true
1174 if (haveId && s.behaviour.useCookies) {
1175 CleanCookie(widgetId, s.cookies.collapseName, s);
1177 if ($.isFunction(s.callbacks.onExtend)) {
1178 s.callbacks.onExtend(link, widget);
1182 return false;
1183 }).appendTo(widgetMenu);
1185 return true;
1189 * Prepare a widget edit menu link
1191 * @access private
1192 * @see PrepareWidgetBehaviour()
1193 * @param widget jQuery object with a widget encapsulation
1194 * @param widgetMenu jQuery object with a widget menu encapsulation
1195 * @param settings Array with the plugin settings
1196 * @return Boolean Truein every case
1199 function AddWidgetEditLink(widget, widgetMenu, settings) {
1200 var s = settings;
1201 var link = '';
1202 if (widget.hasClass(s.options.editable)) {
1203 link = MenuLink(
1204 s.i18n.editText,
1205 s.i18n.editTitle,
1206 s.selectors.editLink
1208 widget.find(s.selectors.closeEdit).click(function(e) {
1209 var link = $(this);
1210 var widget = link.parents(s.selectors.widget);
1211 var editbox = widget.find(s.selectors.editbox);
1212 var editLink = widget.find(s.selectors.editLink);
1213 link.blur();
1214 ApplyEffect(
1215 editbox,
1216 s.effects.widgetCloseEdit,
1217 s.effects.effectDuration,
1218 false
1220 editLink.html(s.i18n.editText);
1221 editLink.attr('title', s.i18n.editTitle);
1222 return false;
1224 $(link).mousedown(function(e) {
1225 e.stopPropagation();
1226 }).click(function() {
1227 var link = $(this);
1228 var canShow = canHide = true;
1229 var widget = link.parents(s.selectors.widget);
1230 var editbox = widget.find(s.selectors.editbox);
1231 var editboxVisible = editbox.css('display') != 'none';
1232 link.blur();
1233 if (editboxVisible) {
1234 if ($.isFunction(s.callbacks.onCancelEditQuery)) {
1235 canHide = s.callbacks.onCancelEditQuery(link, widget);
1237 if (canHide) {
1238 ApplyEffect(
1239 editbox,
1240 s.effects.widgetCancelEdit,
1241 s.effects.effectDuration,
1242 false
1244 link.html(s.i18n.editText);
1245 link.attr('title', s.i18n.editTitle);
1246 if ($.isFunction(s.callbacks.onCancelEdit)) {
1247 s.callbacks.onCancelEdit(link, widget);
1250 } else {
1251 if ($.isFunction(s.callbacks.onEditQuery)) {
1252 canShow = s.callbacks.onEditQuery(link, widget);
1254 if (canShow) {
1255 link.html(s.i18n.cancelEditText);
1256 link.attr('title', s.i18n.cancelEditTitle);
1257 ApplyEffect(
1258 editbox,
1259 s.effects.widgetOpenEdit,
1260 s.effects.effectDuration,
1261 true
1263 if ($.isFunction(s.callbacks.onEdit)) {
1264 s.callbacks.onEdit(link, widget);
1268 return false;
1269 }).appendTo(widgetMenu);
1271 return true;
1275 * Prepare a widget remove menu link
1277 * @access private
1278 * @see PrepareWidgetBehaviour()
1279 * @param widget jQuery object with a widget encapsulation
1280 * @param widgetMenu jQuery object with a widget menu encapsulation
1281 * @param settings Array with the plugin settings
1282 * @return Boolean Truein every case
1285 function AddWidgetRemoveLink(widget, widgetMenu, settings) {
1286 var s = settings;
1287 var link = '';
1288 if (widget.hasClass(s.options.removable)) {
1289 link = MenuLink(
1290 s.i18n.closeText,
1291 s.i18n.closeTitle,
1292 s.selectors.closeLink
1294 $(link).mousedown(function(e) {
1295 e.stopPropagation();
1296 }).click(function() {
1297 var link = $(this);
1298 var canRemove = true;
1299 var widget = link.parents(s.selectors.widget);
1300 var widgetId = widget.attr('id');
1301 var haveId = ($.trim(widgetId) != '');
1302 link.blur();
1303 if ($.isFunction(s.callbacks.onCloseQuery)) {
1304 canRemove = s.callbacks.onCloseQuery(link, widget);
1306 if (canRemove) {
1307 if (!widget.hasClass(s.options.closeConfirm)
1308 || confirm(s.i18n.confirmMsg)) {
1309 if (haveId && s.behaviour.useCookies) {
1310 UpdateCookie(widgetId, s.cookies.closeName, s);
1312 ApplyEffect(
1313 widget,
1314 s.effects.widgetClose,
1315 s.effects.effectDuration,
1316 false
1318 if ($.isFunction(s.callbacks.onClose)) {
1319 s.callbacks.onClose(link, widget);
1323 return false;
1324 }).appendTo(widgetMenu);
1326 return true;
1330 * Clean widgets related cookies
1332 * This private function is called from InitializeWidgets() and used to
1333 * clean certain widgets related cookies. What is this? Well, basically
1334 * here we find for no more used widgets IDs into the appropiate cookies
1335 * values, and remove from this.
1337 * Why? Because in this form the related cookies ever still clean. ;)
1338 * This cookies are the "closed widgets" and "collapses widgets" cookies,
1339 * that store widgets IDs in the same way: separated by commas. So, find
1340 * widgets IDs that in fact not found in the document, and remove from the
1341 * appropiate cookie value, remainded the rest of the widgets IDs.
1343 * Because this function is called from the main plugin method, called
1344 * itself every time that a page that contain widgets is refresh, or when
1345 * add widgets on demand, we only try to clean the cookies in a "random"
1346 * mode, because, finally, is not problem that a cookie contain widgets
1347 * IDs that dont exists.
1349 * So, to save resources, we clean the cookies only in no on demand widgets,
1350 * and only in some "random" times, as you can see in the bellow code.
1352 * @access private
1353 * @see InitializeWidgets()
1354 * @param settings Array with the plugin settings
1355 * @param widgetOnDemand Boolean Depend if deal with on demand widget or not
1356 * @return Boolean True in every case
1359 function CleanWidgetsCookies(settings, widgetOnDemand) {
1360 var s = settings;
1361 var cleanCookies = !widgetOnDemand && s.behaviour.useCookies
1362 && (Math.ceil(Math.random() * 3) == 1);
1363 if (cleanCookies) {
1364 var i = j = 0;
1365 var cookies = new Array(
1366 s.cookies.closeName,
1367 s.cookies.collapseName
1369 var cookiesLen = cookies.length;
1370 var widgetsIds = new Array();
1371 $(s.selectors.widget).each(function(count) {
1372 var widgetId = $(this).attr('id');
1373 if ($.trim(widgetId) != '') {
1374 widgetsIds[count] = widgetId;
1377 for (i = 0; i < cookiesLen; i++) {
1378 if (GetCookie(cookies[i])) {
1379 var widgetId = '';
1380 var cleanValue = '';
1381 var storedValue = GetCookie(cookies[i]).split(',');
1382 var storedWidgets = storedValue.length;
1383 for (j = 0; j < storedWidgets; j++) {
1384 widgetId = $.trim(storedValue[j]);
1385 if ($.inArray(widgetId, widgetsIds) != -1) {
1386 if ($.trim(cleanValue) == '') {
1387 cleanValue += widgetId;
1388 } else {
1389 cleanValue += ',' + widgetId;
1393 SetCookie(cookies[i], cleanValue, s);
1397 return true;
1401 * Get a specific cookie value
1403 * This function is based in jQuery Cookie plugin by Klaus Hartl
1405 * @access private
1406 * @param name String with the cookie name
1407 * @return Null|String Cookie value or nothing
1410 function GetCookie(name) {
1411 var result = null;
1412 if (document.cookie && $.trim(document.cookie) != '') {
1413 var cookies = document.cookie.split(';');
1414 var cookiesLen = cookies.length;
1415 if (cookiesLen > 0) {
1416 for (var i = 0; i < cookiesLen; i++) {
1417 var cookie = $.trim(cookies[i]);
1418 if (cookie.substring(0, name.length + 1) == (name + '=')) {
1419 result = decodeURIComponent(cookie.substring(name.length + 1));
1420 break;
1425 return result;
1429 * Set a specific cookie value
1431 * This function is based in jQuery Cookie plugin by Klaus Hartl
1433 * @access private
1434 * @param name String with the cookie name
1435 * @param value String with the cookie value
1436 * @param settings Array with plugin settings to use
1437 * @return Boolean True in every case
1440 function SetCookie(name, value, settings) {
1441 var s = settings;
1442 var expires = '';
1443 var nType = 'number';
1444 if (s.cookies.expires && (typeof s.cookies.expires
1445 == nType) || s.cookies.expires.toUTCString) {
1446 var date = null;
1447 if (typeof s.cookies.expires == nType) {
1448 date = new Date();
1449 date.setTime(date.getTime() + (s.cookies.expires * 24 * 60 * 60 * 1000));
1450 } else {
1451 date = s.cookies.expires;
1453 // use expires attribute, max-age is not supported by IE
1454 expires = '; expires=' + date.toUTCString();
1456 var path = s.cookies.path ? '; path=' + s.cookies.path : '';
1457 var domain = s.cookies.domain ? '; domain=' + s.cookies.domain : '';
1458 var secure = s.cookies.secure ? '; secure' : '';
1459 document.cookie = [name, '=', encodeURIComponent(value),
1460 expires, path, domain, secure].join('');
1461 return true;
1465 * Clean a Widget Id from a cookie
1467 * We use this in some places, so, centralize here. We clean certain
1468 * related cookie: two of the plugins related cookies using the same
1469 * structure to save their data, and can be clean in the same way.
1471 * A string with comma separated Widgets IDs is stored in this cookies,
1472 * and "clean a cookie" want to say: remove certain Widget ID from this
1473 * cookie, because this widget is now visible or extended.
1475 * @access private
1476 * @param widgetId String with a Widget identifier
1477 * @param cookieName String with the cookie name
1478 * @param settings Array with plugin settings to use
1479 * @return Boolean True in every case
1482 function CleanCookie(widgetId, cookieName, settings) {
1483 var value = GetCookie(cookieName);
1484 if (value != null) {
1485 if (value.indexOf(widgetId) != -1) {
1486 value = value.replace(',' + widgetId, '');
1487 value = value.replace(widgetId + ',', '');
1488 value = value.replace(widgetId, '');
1490 SetCookie(cookieName, value, settings);
1492 return true;
1496 * Update a Widget Id from a cookie
1498 * We use this in some places, so, centralize here. We update certain
1499 * related cookie: two of the plugins related cookies using the same
1500 * structure to save their data, and can be update in the same way.
1502 * A string with comma separated Widgets IDs is stored in this cookies,
1503 * and "update a cookie" want to say: put certain Widget ID in this
1504 * cookie, because this widget is now closed or collapsed.
1506 * @access private
1507 * @param widgetId String with a Widget identifier
1508 * @param cookieName String with the cookie name
1509 * @param settings Array with plugin settings to use
1510 * @return Boolean True in every case
1513 function UpdateCookie(widgetId, cookieName, settings) {
1514 var value = GetCookie(cookieName);
1515 if (value == null) {
1516 value = widgetId;
1517 } else if (value.indexOf(widgetId) == -1) {
1518 value = value + ',' + widgetId;
1520 SetCookie(cookieName, value, settings);
1521 return true;
1525 * Auxiliar function to prepare Widgets header menu links.
1527 * @access private
1528 * @param text Link text
1529 * @param title Link title
1530 * @param aClass CSS class (behaviour) of link
1531 * @return String HTML of the link
1534 function MenuLink(text, title, aClass) {
1535 var l = '<a href="#" title="TITLE" class="CLASS">TEXT</a>';
1536 l = l.replace(/TEXT/g, text);
1537 l = l.replace(/TITLE/g, title);
1538 l = l.replace(/CLASS/g, aClass.replace(/\./, ''));
1539 return l;
1543 * Auxiliar function to show, hide and apply effects.
1545 * @access private
1546 * @param jqObj jQuery object to apply the effect and show or hide
1547 * @param effect String that identifier what effect must be applied
1548 * @param duration Miliseconds to the effect duration
1549 * @param show Boolean True if want to show the object, False to be hide
1550 * @return Boolean True in every case
1553 function ApplyEffect(jqObj, effect, duration, show) {
1554 var n = 'none',
1555 f = 'fade',
1556 s = 'slide';
1557 if (!show) {
1558 if (effect == n) {
1559 jqObj.hide();
1560 } else if (effect == f) {
1561 jqObj.fadeOut(duration);
1562 } else if (effect == s) {
1563 jqObj.slideUp(duration);
1565 } else {
1566 if (effect == n) {
1567 jqObj.show();
1568 } else if (effect == f) {
1569 jqObj.fadeIn(duration);
1570 } else if (effect == s) {
1571 jqObj.slideDown(duration);
1574 return true;
1576 })(jQuery);