source: extensions/GrumPluginClasses/js/criteriaBuilder.js @ 7327

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

fix bug on the rbuilder and ajax initialization and enhance some template & css properties

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