source: extensions/GrumPluginClasses/js/rbCriteriaBuilder.js @ 7530

Last change on this file since 7530 was 7370, checked in by grum, 14 years ago

externalise and pack some js ; rename criteriaBuilder.js files ; improve templates & css theming ; fix bug and add functionnalities for request builder ; update key languages

  • Property svn:executable set to *
File size: 17.2 KB
Line 
1/**
2 * -----------------------------------------------------------------------------
3 * file: criteriaBuilder.js
4 * file version: 1.1.0
5 * date: 2010-10-21
6 *
7 * JS file provided by the piwigo's plugin "GrumPluginClasses"
8 *
9 * -----------------------------------------------------------------------------
10 * Author     : Grum
11 *   email    : grum@piwigo.com
12 *   website  : http://photos.grum.fr
13 *   PWG user : http://forum.phpwebgallery.net/profile.php?id=3706
14 *
15 *   << May the Little SpaceFrog be with you ! >>
16 * -----------------------------------------------------------------------------
17 *
18 * -----------------------------------------------------------------------------
19 * This plugin use the Nested Sortable Plugin / version 1.0.1
20 *    Copyright (c) 2007 Bernardo de Padua dos Santos
21 *    Dual licensed under the MIT (MIT-LICENSE.txt)
22 *    and GPL (GPL-LICENSE.txt) licenses.
23 *
24 *    http://code.google.com/p/nestedsortables/
25 * -----------------------------------------------------------------------------
26 *
27 * an object dedicaded for Piwigo, giving easy user methods to make complex
28 * requests
29 *
30 * constructor : myCB = new criteriaBuilder(containerId [, options]);
31 *
32 *  - containerId : the Id of the DOM element where the criteria will be added
33 *  - options : an object with this properties :
34 *       . textAND       : String, with default value = 'AND'
35 *                         displayed text to indicates the type of operator AND
36 *       . textOR        : String, with default value = 'OR',
37 *                         displayed text to indicates the type of operator OR
38 *       . textHint      : String, with default value = '',
39 *       . classGroup    : String, with default value = '',
40 *       . classItem     : String, with default value = '',
41 *       . classOperator : String, with default value = '',
42 *       . classHelper   : String, with default value = 'helper',
43 *       . opacity       : String, with default value = 0.8,
44 *       . onEdit        : handler on a function to manage click on the 'Edit'
45 *                         button ; event.data contain the item Id
46 *       . onDelete      : handler on a function to manage click on the 'Delete'
47 *                         button ; event.data contain the item Id
48 *
49 *
50 * :: HISTORY ::
51 *
52 * | release | date       |
53 * | 1.0.0   | 2010/04/27 | * start to coding
54 * |         |            |
55 * | 1.1.0   | 2010/10/21 | * change ajax methods
56 * |         |            |
57 * |         |            | * fix bug : if there is no criteria, don't send
58 * |         |            |   request
59 * |         |            |
60 * |         |            |
61 * |         |            |
62 * |         |            |
63 * |         |            |
64 *
65 */
66
67
68
69function criteriaBuilder(container)
70{
71  var itemsId = {
72          group:'iCbGroup',
73          item:'iCbItem',
74          container:container,
75        },
76      counters = {
77          group:0,
78          item:0,
79        },
80      options = {
81          textAND:'AND',
82          textOR:'OR',
83          textNoCriteria:'There is no criteria ! At least, one criteria is required to do search...',
84          textHint:'',
85          textSomethingWrong:'An error has occured on the server-side',
86          textCaddieUpdated:'Caddie was updated',
87          classGroup:'',
88          classItem:'',
89          classOperator:'',
90          classHelper:'helper',
91          opacity:0.8,
92          onEdit:null,
93          onDelete:null,
94          onRequestSuccess:null,
95          onRequestError:null,
96          onGetPageSuccess:null,
97          onGetPageError:null,
98          helpEditUrl:'',
99          helpDeleteUrl:'',
100          helpMove:'',
101          helpSwitchCondition:'',
102          ajaxUrl:'',
103        },
104      extraData = new Array();
105
106  if(arguments.length==2)
107  {
108    if(typeof arguments[1]=='object')
109    {
110      options = jQuery.extend(options, arguments[1]);
111    }
112  }
113
114  /**
115   *
116   *
117   */
118  this.doAction = function (fct)
119  {
120    switch(fct)
121    {
122      case 'add':
123        /* function 'add' : adding an item
124         * the second parameter is the item content
125         * the third parameter is extra data associated with item
126         */
127        if(arguments.length==3)
128        {
129          if(typeof arguments[1]=='string')
130          {
131            addItem(arguments[1], arguments[2]);
132          }
133        }
134        break;
135      case 'delete':
136        /* function 'delete' : remove an item
137         * the second parameter is the item ID
138         */
139        if(arguments.length==2)
140        {
141          if(typeof arguments[1]=='string')
142          {
143            deleteItem(arguments[1]);
144          }
145        }
146        break;
147
148      case 'edit':
149        /* function 'edit' : edit an item content
150         * the second parameter is the item ID
151         * the third parameter is the new content
152         * the fourth parameter is the new extra data associated with item
153         */
154        if(arguments.length==4)
155        {
156          if(typeof arguments[1]=='string' && typeof arguments[2]=='string' )
157          {
158            editItem(arguments[1], arguments[2], arguments[3]);
159          }
160        }
161        break;
162
163      case 'get':
164        /* function 'get' : returns extra data associated with item, ready to be
165         * used
166         */
167        return(getItems());
168        break;
169
170      case 'getExtraData':
171        /* function 'getExtraData' : returns extra data associated with item in
172         * native format
173         */
174        if(arguments.length==2)
175        {
176          return(getExtraData(arguments[1]));
177        }
178        else
179        {
180          return(null);
181        }
182        break;
183
184      case 'clear':
185        /* function 'clear' : clear all criteria
186         */
187        clearItems();
188        break;
189
190      case 'send':
191        /* function 'send' : send request to the server
192         */
193        sendRequest();
194        break;
195
196      case 'getPage':
197        /* function 'send' : send request to the server
198         * the second parameter is the request number
199         * the third parameter is the page number
200         * the fourth parameter is the number of ityem per page
201         */
202        if(arguments.length==4)
203        {
204          getPage(arguments[1], arguments[2], arguments[3]);
205        }
206        break;
207
208      case 'setOptions':
209        /* function 'setOptions' : allows to set options after the object was
210         * created
211         */
212        if(arguments.length==2)
213        {
214          return(setOptions(arguments[1]));
215        }
216        break;
217    }
218  };
219
220  /**
221   * wrap an item in a new group
222   *
223   * !it's considered that the current item parent is the criteria builder area!
224   *
225   * @param String item : ID of the item to be wrapped
226   */
227  var addGroup = function (itemId)
228  {
229    counters.group++;
230
231    var content="<li id='"+itemsId.group+counters.group+"' class='cbGroup cbSortable cbOpAND "+options.classGroup+" cbItemUnique'>";
232    content+="<ul></ul></li>";
233
234    $('#'+itemId).wrap(content);
235
236    content="<div class='cbSortHandle' style='display:none;'>";
237
238    content+="<div class='cbItemButtons' style='float:left;'>";
239    content+="<div class='iconMove' id='iImgMoveItem"+counters.item+"' title=\""+options.helpMove+"\"></div>";
240    content+="<div class='iconSwitchCondition' id='iImgSwitchCItem"+counters.item+"' title=\""+options.helpSwitchCondition+"\"></div>";
241    content+="</div>";
242
243    content+="<div id='"+itemsId.group+counters.group+"OpAND' class='"+options.classOperator+"' style='display:none;'>"+options.textAND+"</div>";
244
245    content+="<div id='"+itemsId.group+counters.group+"OpOR' class='"+options.classOperator+"' style='display:none;'>"+options.textOR+"</div>";
246    content+="</div>";
247
248    $("#"+itemsId.group+counters.group).prepend(content);
249
250    $('#'+itemsId.group+counters.group+'OpOR, #'+itemsId.group+counters.group+'OpAND, #'+itemsId.group+counters.group+' div.iconSwitchCondition ').bind('click', itemsId.group+counters.group, onSwitchOperator);
251
252    applyNested();
253  };
254
255  /**
256   * remove a group
257   *
258   * @param String groupId : ID of the group
259   */
260  var removeGroup = function (groupId)
261  {
262    $('#'+groupId).remove();
263  };
264
265  /**
266   * add a new item in the criteria builder area
267   *
268   * @param String content : content of the new item
269   */
270  var addItem = function (itemContent, data)
271  {
272    counters.item++;
273
274    var content="<li id='"+itemsId.item+counters.item+"' class='cbItem cbSortable "+options.classItem+"'>";
275
276    content+="<div class='cbItemButtons' style='float:right;'>";
277
278    if(options.onEdit!=null &&
279       jQuery.isFunction(options.onEdit)) content+="<div class='iconEdit' id='iImgEdit"+counters.item+"' title=\""+options.helpEdit+"\"></div>";
280
281    if(options.onDelete!=null &&
282       jQuery.isFunction(options.onDelete)) content+="<div class='iconDelete' id='iImgDelete"+counters.item+"' title=\""+options.helpDelete+"\"></div>";
283
284    content+="</div><div class='cbSortHandle'>";
285    content+="<div class='cbItemButtons' style='float:left;'> <div class='iconMove' id='iImgMoveItem"+counters.item+"' title=\""+options.helpMove+"\"></div></div>";
286    content+="<div class='itemContent'>"+itemContent+"</div></div></li>";
287
288    $('#'+itemsId.container).append(content);
289
290    addGroup(itemsId.item+counters.item);
291
292    if(options.onEdit!=null) $('#iImgEdit'+counters.item).bind('click', itemsId.item+counters.item, options.onEdit);
293
294    if(options.onDelete!=null) $('#iImgDelete'+counters.item).bind('click', itemsId.item+counters.item, options.onDelete);
295
296
297    extraData[counters.item]=data;
298  };
299
300  /**
301   * remove an item from the criteria builder area and do a check to determine
302   * if parent group have to be removed
303   *
304   * @param String itemId : ID of the item to remove
305   */
306  var deleteItem = function (itemId)
307  {
308    if($('#'+itemId).length!=0)
309    {
310      $('#'+itemId).remove();
311      re=/[0-9]*$/;
312      extraData[eval(re.exec(itemId)[0])]=null;
313      manage();
314    }
315  };
316
317  /**
318   * modify the content of an item
319   *
320   * if set, trigger the 'onEdit' function after the item was modified
321   *
322   * @param String itemId : ID of the item to modify
323   * @param String content : the new content to be applied
324   */
325  var editItem = function (itemId, content, data)
326  {
327    if($('#'+itemId).length!=0)
328    {
329      $('#'+itemId+' .itemContent').html(content);
330      re=/[0-9]*$/;
331      extraData[eval(re.exec(itemId)[0])]=data;
332    }
333  };
334
335  /**
336   * clear all the criteria
337   */
338  var clearItems = function()
339  {
340    $('#'+itemsId.container).NestedSortableDestroy();
341    $('#'+itemsId.container).html("");
342    counters.item=0;
343    counters.group=0;
344    extraData=new Array();
345  };
346
347  /**
348   * used by the getItems to serialize extraData objects
349   *
350   * @param String prefix : the prefix name for variable in the serialized string
351   * @param value : the value of the variable
352   * @return String : the serialized object
353   */
354  var serializeData=function(prefix, value)
355  {
356    returned='';
357    if(typeof value =='object')
358    {
359      for(var key in value )
360      {
361        if(typeof value[key] =='object')
362        {
363          returned+=serializeData(prefix+'['+key+']', value[key]);
364        }
365        else
366        {
367          returned+='&'+prefix+'['+key+']='+value[key];
368        }
369      }
370    }
371    else
372    {
373      returned+='&'+prefix+'='+value;
374    }
375    return(returned);
376  };
377
378
379  /**
380   *
381   * @return String : items in a string ready to use in an url
382   */
383  var getItems = function()
384  {
385    //group & items tree
386    serialized=jQuery.iNestedSortable.serialize(itemsId.container)['hash'];
387
388    //items extraData
389    tmp=Array();
390    for(i=0;i<extraData.length;i++)
391    {
392      if(extraData[i]!=null)
393      {
394        serialized+=serializeData('extraData['+i+']', extraData[i]);
395      }
396    }
397
398    //group Operators
399    $('#'+itemsId.container+' .cbGroup').each(
400      function ()
401      {
402        re=/[0-9]*$/;
403        serialized+='&operator['+re.exec(this.id)[0]+']=';
404        if($(this).hasClass('cbOpOR'))
405        {
406          serialized+='OR';
407        }
408        else
409        {
410          serialized+='AND';
411        }
412      }
413    );
414
415    return(serialized);
416  };
417
418
419  /**
420   *
421   * @return : return extradata (in native format) associated with item
422   */
423  var getExtraData = function(itemId)
424  {
425    re=/[0-9]*$/;
426    extraDataNumber=re.exec(itemId)[0];
427
428    return(extraData[extraDataNumber]);
429  };
430
431
432  /**
433   *
434   * @param Object options : set the given option
435   */
436  var setOptions = function(optionsToSet)
437  {
438    options = jQuery.extend(options, optionsToSet);
439  };
440
441  /**
442   * display/hide operator title for a group
443   *
444   * @param String groupId : ID of the group
445   * @param Boolean visible : set true to display the title, false to hide it
446   */
447  var displayOperator = function (groupId, visible)
448  {
449    if($('#'+groupId).hasClass('cbOpAND'))
450    {
451      if(visible)
452      {
453        $('#'+groupId+'OpAND').css('display', 'block');
454      }
455      else
456      {
457        $('#'+groupId+'OpAND').css('display', 'none');
458      }
459    }
460    else
461    {
462      if(visible)
463      {
464        $('#'+groupId+'OpOR').css('display', 'block');
465      }
466      else
467      {
468        $('#'+groupId+'OpOR').css('display', 'none');
469      }
470    }
471    if(visible)
472    {
473      $('#'+groupId).children('div.cbSortHandle').css('display', 'block');
474    }
475    else
476    {
477      $('#'+groupId).children('div.cbSortHandle').css('display', 'none');
478    }
479  };
480
481  /**
482   * manage the criteria builder groups&items
483   *
484   * check validity for a group : an empty group is removed
485   * check validity for an item : for an item directly attached to the criteria
486   *                              builder area is wrapped in a new group
487   */
488  var manage = function ()
489  {
490    $('#'+itemsId.container+' li').each(
491      function()
492      {
493        if($(this).hasClass('cbGroup'))
494        {
495          if($('#'+this.id+' li.cbItem').length==0)
496          {
497            // a group without item is removed
498            removeGroup(this.id);
499          }
500          else if($('#'+this.id+' li.cbItem').length==1)
501          {
502            $('#'+this.id).addClass('cbItemUnique').removeClass('cbItemMultiple');
503            displayOperator(this.id, false);
504          }
505          else
506          {
507            $('#'+this.id).removeClass('cbItemUnique').addClass('cbItemMultiple');
508            displayOperator(this.id, true);
509          }
510        }
511        else if($(this).hasClass('cbItem'))
512        {
513          if($(this).parent().get(0).id==itemsId.container)
514          {
515            // an item without group as parent is wrapped in a new group
516            addGroup(this.id);
517          }
518        }
519      }
520    );
521  };
522
523  /**
524   * this function make the groups&items ready to be sorted & grouped
525   */
526  var applyNested = function ()
527  {
528   // $.data($('#'+itemsId.container)[0], 'id', this);
529    $('#'+itemsId.container).NestedSortableDestroy();
530    $('#'+itemsId.container).NestedSortable(
531      {
532        accept: 'cbSortable',
533        noNestingClass: 'cbItem',
534        opacity: options.opacity,
535        helperclass: options.classHelper,
536        serializeRegExp:/.*/i,
537        autoScroll: true,
538        handle: '.cbSortHandle',
539        ghosting:false,
540        nestingPxSpace:15,
541        currentNestingClass:'cbItemOverGroup',
542
543        onChange: function(serialized) {
544          manage();
545        },
546      }
547    );
548  };
549
550  /**
551   * switch the operator for a group
552   *
553   * event.data = ID of the group
554   */
555  onSwitchOperator = function (event)
556  {
557    groupId=event.data;
558
559    if($('#'+groupId).hasClass('cbOpAND'))
560    {
561      $('#'+groupId).removeClass('cbOpAND').addClass('cbOpOR');
562      $('#'+groupId+'OpAND').css('display', 'none');
563      $('#'+groupId+'OpOR').css('display', 'block');
564    }
565    else
566    {
567      $('#'+groupId).removeClass('cbOpOR').addClass('cbOpAND');
568      $('#'+groupId+'OpAND').css('display', 'block');
569      $('#'+groupId+'OpOR').css('display', 'none');
570    }
571  };
572
573  /**
574   * send the request to the server
575   *
576   */
577  var sendRequest = function()
578  {
579    if(extraData.length==0)
580    {
581      alert(options.textNoCriteria);
582      return(false);
583    }
584
585    datas=encodeURI('ajaxfct=public.rbuilder.searchExecute&requestName='+itemsId.container+'&'+getItems());
586    $.ajax(
587      {
588        type: "POST",
589        url: options.ajaxUrl,
590        async: true,
591        data: datas,
592        success: function(msg)
593          {
594            if(options.onRequestSuccess!=null && jQuery.isFunction(options.onRequestSuccess)) options.onRequestSuccess(msg);
595          },
596        error: function(msg)
597          {
598            if(options.onRequestError!=null && jQuery.isFunction(options.onRequestError)) options.onRequestError(msg);
599          },
600       }
601     );
602
603  };
604
605  /**
606   * get a result page from the server
607   *
608   */
609  var getPage = function(requestNumber, pageNumber, numberPerPage)
610  {
611    $.ajax(
612      {
613        type: "POST",
614        url: options.ajaxUrl,
615        async: true,
616        data: {ajaxfct:'public.rbuilder.searchGetPage', page:pageNumber, requestNumber:requestNumber, numPerPage:numberPerPage},
617        success: function(msg)
618          {
619            if(options.onGetPageSuccess!=null && jQuery.isFunction(options.onGetPageSuccess)) options.onGetPageSuccess(msg);
620          },
621        error: function(msg)
622          {
623            if(options.onGetPageError!=null && jQuery.isFunction(options.onGetPageError)) options.onGetPageError(msg);
624          },
625       }
626     );
627
628  };
629
630  applyNested();
631};
632
633
634criteriaBuilder.makeExtendedData = function(owner, data)
635{
636  return(
637    {
638      owner:owner,
639      param:data,
640    }
641  );
642}
Note: See TracBrowser for help on using the repository browser.