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

Last change on this file since 12201 was 10884, checked in by grum, 14 years ago

fix bug:2302 - release 3.5.1

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