Alien-GvaScript

 view release on metacpan or  search on metacpan

lib/Alien/GvaScript/lib/GvaScript.js  view on Meta::CPAN

      new Ajax.Updater(content, url, {onComplete: callback});
      return true;
    }
  },

  select: function (node) {
    var previousNode = this.selectedNode;

    // re-selecting the current node is a no-op
    if (node == previousNode) return;

    // deselect the previously selected node
    if (previousNode) {
        var label = this.label(previousNode);
        if (label) {
          Element.removeClassName(label, this.classes.selected);
        }
    }

    // select the new node
    this.selectedNode = node;
    if (node) {
      this._assertNodeOrLeaf(node, 'select node');
      var label = this.label(node);
      if (!label) {
        throw new Error("selected node has no label");
      }
      else {
        Element.addClassName(label, this.classes.selected);

        if (this.isVisible(label)) {
          // focus has not yet been given to label
          if(! label.hasAttribute('hasFocus'))
            label.focus();
        }
      }
    }

    // cancel if there was any select execution pending
    if (this._selectionTimeoutId) clearTimeout(this._selectionTimeoutId);

    // register code to call the selection handlers after some delay
    var callback = this._selectionTimeoutHandler.bind(this, previousNode);
    this._selectionTimeoutId =
      setTimeout(callback, this.options.selectDelay);
  },

  scrollTo: function(node, with_content) {
    if(!node) return;

    var container = this.options.scrollingContainer;
    if(typeof container == 'string') {
      container = $(container);
    }
    if(!container) return;

    // donot invoke scroll if scrolling is disabled
    // first test if scrolling is enabled on the scrolling container
    if(container.tagName.toLowerCase() == 'html') {
      // on document body
      if(document.body.style.overflow == 'hidden'
        || document.body.style.overflowY == 'hidden'
        || document.body.scroll == 'no') // IE
      return;
    }
    else {
      // on element
      if(container.style.overflow == 'hidden'
        || container.style.overflowY == 'hidden')
      return;
    }

    // test if the node in 'in view'
    _container_y_start = container.scrollTop;
    _container_y_end   = _container_y_start + container.clientHeight;
    _node_y  = Element.cumulativeOffset(node).top + (with_content? node.offsetHeight: 0);

    // calculate padding space between the selected node and
    // the edge of the scrollable container
    _perc = this.options.autoScrollPercentage || 0;
    _padding = container.clientHeight * _perc / 100;

    // calculate delta scroll to affect on scrollingContainer
    _delta = 0;

    // node is beneath scrolling area
    if(_node_y > _container_y_end - _padding) {
      _delta = _node_y - _container_y_end + _padding;
    }

    // node is above scrolling area
    if(_node_y < _container_y_start + _padding) {
      _delta = _container_y_start - _node_y - _padding;
    }

    if(_delta != 0) {
      // amount required to scroll to greater than available document height
      if(_delta > container.clientHeight - _padding) {
        // make label top
        var lbl_pos = Element.cumulativeOffset(this.label(node)).top;
        container.scrollTop = lbl_pos - _padding;
      }
      else
      container.scrollTop += parseInt(_delta)
    }
    return;
  },

  label: function(node) {
    this._assertNodeOrLeaf(node, 'label: arg type');
    return Element.navigateDom(node.firstChild, 'nextSibling',
                               this.classes.label);
  },

  content: function(node) {
    if (this.isLeaf(node)) return null;
    this._assertNode(node, 'content: arg type');
    return Element.navigateDom(node.lastChild, 'previousSibling',
                               this.classes.content);
  },

  parentNode: function (node) {
    this._assertNodeOrLeaf(node, 'parentNode: arg type');
    return Element.navigateDom(
      node.parentNode, 'parentNode', this.classes.node,
      this.isRootElement.bind(this));
  },

  nextSibling: function (node) {

lib/Alien/GvaScript/lib/GvaScript.js  view on Meta::CPAN

  // second param is the HTTP method (post or get)
  // DALNOTE 10.01.09 : pas de raison de faire le choix de la  méthode HTTP
  // dans  setAdditionalParams()! TOFIX. Apparemment, utilisé une seule fois
  // dans DMWeb (root\src\tab_composition\form.tt2:43)
  setAdditionalParams : function(params, method) {
    this.additional_params = params;
    if (method) this.options.http_method = method;
  },

  addAdditionalParam : function(param, value) {
    if (!this.additional_params)
      this.additional_params = {};
    this.additional_params[param] = value;
  },

  setdatasource : function(datasource) {

    // remember datasource in private property
    this._datasource = datasource;

    // register proper "updateChoices" function according to type of datasource
    var ds_type = typeof datasource;
    this._updateChoicesHandler
      = (ds_type == "string")   ? this._updateChoicesFromAjax
      : (ds_type == "function") ? this._updateChoicesFromCallback
      : (ds_type == "object" && datasource instanceof Array)
                                ? this._updateChoicesFromArray
      : (ds_type == "object" && datasource instanceof Object)
                                ? this._updateChoicesFromJSONP
      : undefined;
     if (!this._updateChoicesHandler)
      throw new Error("unexpected datasource type");
  },

  // 'fireEvent' function is copied from GvaScript.fireEvent, so that "this"
  // in that code gets properly bound to the current object
  fireEvent: GvaScript.fireEvent,

  // Set the element for the AC to look at to adapt its position. If elem is
  // null, stop observing the scroll.
  // DALNOTE 10.01.09 : pas certain de l'utilité de "set_observed_scroll"; si
  // l'élément est positionné correctement dans le DOM par rapport à son parent,
  // il devrait suivre le scroll automatiquement. N'est utilisé dans DMWeb que
  // par "avocat.js".
  set_observed_scroll : function(elem) {
    if (!elem) {
        Event.stopObserving(this.observed_scroll, 'scroll',
                            correct_dropdown_position);
        return;
    }

    this.observed_scroll = elem;
    this.currentScrollTop = elem.scrollTop;
    this.currentScrollLeft = elem.scrollLeft;
    var correct_dropdown_position = function() {
      if (this.dropdownDiv) {
        var dim = Element.getDimensions(this.inputElement);
        var pos = this.dropdownDiv.positionedOffset();
        pos.top  -= this.observed_scroll.scrollTop - this.currentScrollTop;
        pos.left -= this.observed_scroll.scrollLeft;
        this.dropdownDiv.style.top  = pos.top   + "px";
        this.dropdownDiv.style.left = pos.left  + "px";
      }
      this.currentScrollTop = this.observed_scroll.scrollTop;
      this.currentScrollLeft = this.observed_scroll.scrollLeft;
    }

    Event.observe(elem, 'scroll',
                  correct_dropdown_position.bindAsEventListener(this));
  },


//----------------------------------------------------------------------
// PRIVATE METHODS
//----------------------------------------------------------------------

  _updateChoicesFromAjax: function (val_to_complete, continuation) {

    // copies into local variables, needed for closures below (can't rely on
    // 'this' because 'this' may have changed when the ajax call comes back)
    var autocompleter = this;
    var inputElement  = this.inputElement;

    inputElement.style.backgroundColor = ""; // remove colorIllegal

    // abort prev ajax request on this input element
    if (this._runningAjax[inputElement.name])
      this._runningAjax[inputElement.name].transport.abort();

    Element.addClassName(inputElement, this.classes.loading);

    // encode value to complete 
    val_to_complete = val_to_complete.split("").map(function (c) {
      if (c.match(/[@\+\/]/)) {
        return encodeURIComponent(c);
      }
      else {
        return escape(c);
      }
    }).join("");
    
    var complete_url = this._datasource + val_to_complete;

    this._runningAjax[inputElement.name] = new Ajax.Request(
      complete_url,
      {asynchronous: true,
       method: this.options.http_method,
       parameters: this.additional_params, // for example {C_ETAT_AVOC : 'AC'}

       // DALNOTE 10.01.09: forcer du JSON dans le body du POST est spécifique
       // DMWeb; pour le cas général il faut pouvoir envoyer du
       // x-www-form-urlencoded ordinaire
       postBody: this.options.http_method == 'post'
                     ? Object.toJSON(this.additional_params)
                     : null,

       contentType: "text/javascript",
       evalJSON: 'force', // will evaluate even if header != 'application/json'
       onSuccess: function(xhr) {
          // aborted by the onblur handler
          if (xhr.transport.status == 0) return;

          autocompleter._runningAjax[inputElement.name] = null;

          if (xhr.responseJSON) continuation(xhr.responseJSON);

          // autocompleter input already blurred without _blurHandler being
          // called (autocompleter is strict and needs its choices to
          // be able to fire its final status
          if (xhr['blurAfterSuccess']) autocompleter._blurHandler();
       },
       onFailure: function(xhr) {
          autocompleter._runningAjax[inputElement.name] = null;
          autocompleter.displayMessage("pas de réponse du serveur");
       },
       onComplete: function(xhr) {
          Element.removeClassName(inputElement,
                                  autocompleter.classes.loading);
       }
      });
  },

  _updateChoicesFromCallback : function(val_to_complete, continuation) {
     continuation(this._datasource(val_to_complete));

lib/Alien/GvaScript/lib/GvaScript.js  view on Meta::CPAN


//     if (window.console) console.log('updateChoices', value);

    this._updateChoicesHandler(value, continuation);
  },


  // does the reverse of "autocomplete()"
  // doesnot fire if input blurred from click on choice list
  _blurHandler: function(event) {

    // remove choice list
    if (this.dropdownDiv)  this._removeDropdownDiv();

    // xhr is still active: waiting for response from server
    if (_xhr = this._runningAjax[this.inputElement.name]) {

      // if autocompleter is strict, need to wait for xhr to
      // finish before calling the _blurHandler to fire the
      // autocompleter's finalState
      if (this.options.strict) {
        _xhr['blurAfterSuccess'] = true;
        return;
      }

      _xhr.transport.abort();
      _xhr = null;
      Element.removeClassName(this.inputElement, this.classes.loading);
    }

    // if strict mode, inform client about the final status
    if (this.options.strict) {
      var value = this._getValueToComplete();

      // if value has changed, invalidate previous list of choices
      if (value != this.lastValue) {
        this.choices = null;
      }

      // if blank and blankOK, this is a legal value
      if (!value && this.options.blankOK) {
        this._updateDependentFields(this.inputElement, "");
        this.fireEvent({ type       : "LegalValue",
                         value      : "",
                         choice     : null,
                         controller : null  }, this.inputElement);
      }

      // if choices are known, just inspect status
      else if (this.choices) {
        this._fireFinalStatus(this.inputElement, this.choices);
      }

      // if not enough chars to get valid choices, this is illegal
      else if (value.length < this.options.minimumChars) {
        var return_value = this.fireEvent({
          type: "IllegalValue", value: value
        }, this.inputElement);

        if(! return_value) {
          this.inputElement.style.backgroundColor = this.options.colorIllegal;
          this._updateDependentFields(this.inputElement, null);
        }
      }

      // otherwise get choices and then inspect status (maybe asynchronously)
      else  {
        this._updateChoices(this._fireFinalStatus.bind(this,
                                                       this.inputElement));
      }
    }

    this.fireEvent("Leave", this.inputElement);
    this.inputElement = null;
  },

  _fireFinalStatus: function (inputElement, choices) {
  // NOTE: takes inputElement and choices as arguments, because it might be
  // called asynchronously, after "this" has been detached from the input
  // element and the choices array, so we cannot call the object properties.

    var input_val = this._getValueToComplete(inputElement.value);

    var index = null;

    // inspect the choice list to automatically choose the appropriate candidate
    for (var i=0; i < choices.length; i++) {
        var val = this._valueFromChoiceItem(choices[i]);

        if (val == input_val) {
            index = i;
            break; // break the loop because this is the best choice
        }
        else if (val.toUpperCase() == input_val.toUpperCase()) {
            index = i;  // is a candidate, but we may find a better one
        }
    }

    // if automatic choice did not work, but we have only 1 choice, and this is
    // not blank on purpose, then force it into the field
    if (index === null && choices.length == 1
                       && (input_val || !this.options.blankOK ))
        index = 0;

    if (index !== null) {
        var choice = choices[index];
        var val = this._valueFromChoiceItem(choice);

        // put canonical value back into input field
        this._setValue(val, inputElement);

        // for backwards compatibility, we generate a "Complete" event, but
        // with a fake controller (because the real controller might be in a
        // diffent state).
        this.fireEvent({ type      : "Complete",
                         referrer  : "blur",    // input blur fired this event
                         index     : index,
                         choice    : choice,
                         controller: {choices: choices} }, inputElement);

        // update dependent fields
        this._updateDependentFields(inputElement, choice);

        // for new code : generate a "LegalValue" event
        this.fireEvent({ type       : "LegalValue",
                         value      : val,
                         choice     : choice,
                         controller : null  }, inputElement);

    }
    else {
        var return_value = this.fireEvent({
          type       : "IllegalValue",
          value      : input_val,
          controller : null
        }, inputElement);

        if(! return_value) {
          inputElement.style.backgroundColor = this.options.colorIllegal;
          this._updateDependentFields(inputElement, null);
        }
    }
  },

  _updateDependentFields: function(inputElement, choice) {
        // "choice" might be
        //   - an object or nonempty string ==> update dependent fields
        //   - an empty string              ==> clear dependent fields
        //   - null                         ==> put "ILLEGAL_***"
        var attr       = inputElement.getAttribute('ac:dependentFields');
        var dep_fields = attr ? eval("("+attr+")")
                              : this.options.dependentFields;
        if (!dep_fields) return;

        var form       = inputElement.form;
        var name_parts = inputElement.name.split(/\./);

        for (var k in dep_fields) {
            name_parts[name_parts.length - 1] = k;
            var related_name    = name_parts.join('.');
            var related_field   = form[related_name];
            var value_in_choice = dep_fields[k];
            if (related_field) {
                related_field.value
                    = (value_in_choice == "")        ? ""
                    : (choice === null)              ? "!!ILLEGAL_" + k + "!!"
                    : (typeof choice == "object")    ? 
                      (choice[value_in_choice]       ? choice[value_in_choice] : "")
                    : (typeof choice == "string")    ? choice
                    : "!!UNEXPECTED SOURCE FOR RELATED FIELD!!";
            }
        }
    },

  // if clicking in the 20px right border of the input element, will display
  // or hide the drowpdown div (like pressing ARROWDOWN or ESC)
  _clickHandler: function(event) {
    var x = event.offsetX || event.layerX; // MSIE || FIREFOX
    if (x > Element.getDimensions(this.inputElement).width - 20) {
        if ( this.dropdownDiv ) {
            this._removeDropdownDiv();
            Event.stop(event);
        }
        else
            this._ArrowDownHandler(event);
    }
  },

  _ArrowDownHandler: function(event) {
    var value = this._getValueToComplete();
    var valueLength = (value || "").length;
    if (valueLength < this.options.minimumChars)
      this.displayMessage("liste de choix à partir de "
                            + this.options.minimumChars + " caractères");
    else
      this._displayChoices();
    Event.stop(event);
  },

lib/Alien/GvaScript/lib/GvaScript.js  view on Meta::CPAN

    // browser behaviour can update the value; then come back through a
    // timeout to update the Autocompleter
  },



  _checkNewValue: function() {

    // abort if the timeout occurs after a blur (no input element)
    if (!this.inputElement) {
//       if (window.console) console.log('_checkNewValue ... no input elem');
      return;
    }

    // several calls to this function may be queued by setTimeout,
    // so we perform some checks to avoid doing the work twice
    if (this._timeLastCheck > this._timeLastKeyDown) {

//       if (window.console) console.log('_checkNewValue ... done already ',
//                   this._timeLastCheck, this._timeLastKeyDown);

      return; // the work was done already
    }

    var now = (new Date()).getTime();

    var deltaTime = now - this._timeLastKeyDown;
    if (deltaTime + this.options.deltaTime_tolerance
          <  this.options.checkNewValDelay) {

//       if (window.console) console.log('_checkNewValue ... too young ',
//                                       now, this._timeLastKeyDown);

      return; // too young, let olders do the work
    }


    this._timeLastCheck = now;
    var value = this._getValueToComplete();
//     if (window.console)
//         console.log('_checkNewValue ... real work [value = %o]  - [lastValue = %o] ',
//                              value, this.lastValue);
    this.lastValue = this.lastTypedValue = value;

    // create a list of choices if we have enough chars
    if (value.length >= this.options.minimumChars) {

        // first create a "continuation function"
        var continuation = function (choices) {

          // if, meanwhile, another keyDown occurred, then abort
          if (this._timeLastKeyDown > this._timeLastCheck) {
//             if (window.console)
//               console.log('after updateChoices .. abort because of keyDown',
//                           now, this._timeLastKeyDown);
            return;
          }

          this.choices = choices;
          if (choices && choices.length > 0) {
            this.inputElement.style.backgroundColor = ""; // remove colorIllegal
            if (this.options.autoSuggest)
              this._displayChoices();
          }
          else if (this.options.strict && (!this.options.blankOK)) {
            this.inputElement.style.backgroundColor = this.options.colorIllegal;
          }
        };

        // now call updateChoices (which then will call the continuation)
        this._updateChoices(continuation.bind(this));
      }
  },


  // return the value to be completed
  // TODO : for multivalued, should return the value under the cursor,
  // instead returning sytematically the last value
  _getValueToComplete : function(value) {
     // NOTE: the explicit value as argument is only used from
     //_fireFinalStatus(), when we can no longer rely on
     // this.inputElement.value
    value = value || this.inputElement.value;
    if (this.options.multivalued) {
      var vals = value.split(this.options.multivalue_separator);
      value    = vals[vals.length-1];
    }
    return value;
  },

  _setValue : function(value, inputElement) {
        // NOTE: the explicit inputElement as argument is only used from
        // _fireFinalStatus(), when we can no longer rely on this.inputElement

    // default inputElement is the one bound to this autocompleter
    if (!inputElement) inputElement = this.inputElement;

    // if multivalued, the completed value replaces the last one in the list
    if (this.options.multivalued) {
      var _sep = inputElement.value.match(this.options.multivalue_separator);
      if (_sep) {
        var vals = inputElement.value.split(this.options.multivalue_separator);
        vals[vals.length-1] = value;
        value = vals.join(_sep[0]); // join all vals with first separator found
      }
    }

    // setting value in input field
    inputElement.value = this.lastValue = value;
  },



  _typeAhead : function () {
    var curLen     = this.lastTypedValue.length;
    var index      = this.choiceList.currentHighlightedIndex;
    var suggestion = this._valueFromChoice(index);
    var newLen     = suggestion.length;
    this._setValue(suggestion);

    if (this.inputElement.createTextRange){ // MSIE
      var range = this.inputElement.createTextRange();
      range.moveStart("character", curLen); // no need to moveEnd
      range.select(); // will call focus();
    }
    else if (this.inputElement.setSelectionRange){ // Mozilla
      this.inputElement.setSelectionRange(curLen, newLen);
    }
  },



//----------------------------------------------------------------------
// methods for the dropdown list of choices
//----------------------------------------------------------------------

  _mkDropdownDiv : function() {
    this._removeDropdownDiv();

    // the autocompleter has been blurred ->
    // do not display the div
    if(!this.inputElement) return null;

    // if observed element for scroll, reposition
    var movedUpBy   = 0;
    var movedLeftBy = 0;
    if (this.observed_scroll) {
        movedUpBy   = this.observed_scroll.scrollTop;
        movedLeftBy = this.observed_scroll.scrollLeft;
    }

    // create div
    var div = new Element('div');
    div.className = this.classes.dropdown;

    // positioning
    var coords = Position.cumulativeOffset(this.inputElement);
    var dim     = Element.getDimensions(this.inputElement);
    div.style.left      = coords[0] + this.options.offsetX - movedLeftBy + "px";
    div.style.top       = coords[1] + dim.height -movedUpBy + "px";
    div.style.maxHeight = this.options.maxHeight + "px";
    div.style.minWidth  = this.options.minWidth + "px";
    div.style.zIndex    = 32767; //Seems to be the highest valid value

    // insert into DOM
    document.body.appendChild(div);

    // simulate minWidth on old MSIE (must be AFTER appendChild())
    // maxHeight cannot be simulated untill displayChoices
    if (navigator.userAgent.match(/\bMSIE [456]\b/)) {
      div.style.width  = this.options.minWidth + "px";
    }

    // mouseenter and mouseleave events to control
    // whether autocompleter has been blurred
    var elem = this.inputElement;
    div.observe('mouseenter', function(e) {
      Element.stopObserving(elem, "blur", this.reuse.onblur);
    }.bind(this));
    div.observe('mouseleave', function(e) {
      Element.observe(elem, "blur", this.reuse.onblur);
    }.bind(this));

    return this.dropdownDiv = div;
  },



  _displayChoices: function() {

    // if no choices are ready, can't display anything
    if (!this.choices) return;

    var toCompleteVal = this._getValueToComplete();

    if (this.choices.length > 0) {
      var ac = this;

      // create a choiceList
      var cl = this.choiceList = new GvaScript.ChoiceList(this.choices, {
        labelField        : this.options.labelField,
        scrollCount       : this.options.scrollCount,
        choiceItemTagName : this.options.choiceItemTagName,
        htmlWrapper       : this.options.htmlWrapper
      });
      cl.currentHighlightedIndex = ac._idx_to_hilite;

      // TODO: explain and publish method "choiceElementHTML", or redesign
      // and make it a private method
      if ( this.choiceElementHTML ) {
        cl.choiceElementHTML = this.choiceElementHTML;
      }

      cl.onHighlight = function(event) {
        if (ac.options.typeAhead)
          ac._typeAhead();
        ac.fireEvent(event, ac.inputElement);
      };
      cl.onPing = function(event) {
        ac._completeFromChoiceElem(event.target);
      };
      cl.onCancel = function(event) {
        ac._removeDropdownDiv();
      };

      // append div to DOM
      var choices_div = this._mkDropdownDiv();
      // fill div now so that the keymap gets initialized
      cl.fillContainer(choices_div);
      // set height of div for IE6 (no suppport for maxHeight!)
      if (navigator.userAgent.match(/\bMSIE [456]\b/)) {
        choices_div.style.height =
          (choices_div.scrollHeight > this.options.maxHeight)?
            this.options.maxHeight + 'px' :
            'auto';
      }

      // determine if there is a space to dislay
      // the choices list under the input
      // if not, display above.
      // onscreen height needed for displaying the choices list
      var _h_needed = Element.viewportOffset(this.inputElement)[1]
                      + this.inputElement.offsetHeight
                      + choices_div.offsetHeight;
      var _h_avail  = document.viewport.getHeight();
      // move choices list on top of the input element
      if(_h_needed >= _h_avail) {
        var div_top = choices_div.offsetTop
                      - choices_div.offsetHeight
                      - this.inputElement.offsetHeight;
        if (div_top >= 0)
          choices_div.style.top = div_top + 'px';
      }

      // catch keypress on TAB while choiceList has focus
      cl.keymap.rules[0].TAB = cl.keymap.rules[0].S_TAB = function(event) {
        var index = cl.currentHighlightedIndex;
        if (index != undefined) {

          var elem = cl._choiceElem(index);

          // generate a "Ping" on the choiceList, like if user had
          // pressed RETURN to select the current highlighted item
          cl.fireEvent({type : "Ping",
                        index: index}, elem, cl.container);

          // NO Event.stop() here, because the navigator should
          // do the tabbing (pass focus to next/previous element)
        }
      };

      // more key handlers when the suggestion list is displayed
      this.keymap.rules.push(cl.keymap.rules[0]);

    }
    else
      this.displayMessage("pas de suggestion");
  },

  _removeDropdownDiv: function() {
    // remove the dropdownDiv that was added previously by _mkDropdownDiv();
    // that div contained either a menu of choices or a message to the user
    if (this.dropdownDiv) {
      // remove mouseenter and mouseleave observers
      this.dropdownDiv.stopObserving();
      Element.remove(this.dropdownDiv);
      this.dropdownDiv = null;
    }

    // if applicable, also remove rules previously pushed by _displayChoices
    if (this.keymap.rules.length > 1)
      this.keymap.rules.pop();
  },

  _valueFromChoice: function(index) {
    if (!this.choices) return null;
    var choice = this.choices[index];
    return (choice !== null) ? this._valueFromChoiceItem(choice) : null;
  },

  _valueFromChoiceItem: function(choice) {
    return (typeof choice == "string") ? choice
                                       : choice[this.options.valueField];
  },



  //triggered by the onPing event on the choicelist, i.e. when the user selects
  //one of the choices in the list
  _completeFromChoiceElem: function(elem) {
    // identify the selected line and handle it
    var num = parseInt(elem.id.match(/\.(\d+)$/)[1], 10);

    // add the value to the input element
    var value = this._valueFromChoice(num);
    if (value !== null) {
      this._setValue(value)
      this._removeDropdownDiv();
      
      // ADDED LEMOINEJ 26.09.13
      this._timeLastCheck = this._timeLastKeyDown = 0;      
      this._checkNewValue();

      if (!this.options.multivalued) {
        this.inputElement.select();
      }

      this._updateDependentFields(this.inputElement, this.choices[num]);

      // fire events: "Complete" for backwards compatibility, "LegalValue"
      // for regular use
      var eventNames =  ["Complete", "LegalValue"];
      // for loop : can't use .each() from prototype.js because it alters "this"
      for (var i = 0; i < eventNames.length; i++) {
        this.fireEvent({
          type      : eventNames[i],
          referrer  : "select",    // choice selection fired this event
          index     : num,
          choice    : this.choices[num],
          controller: {choices: this.choices}
          }, elem, this.inputElement);
      }
    }
  }
}



//----------customButtons.js
// depends: keyMap.js

GvaScript.CustomButtons = {};

GvaScript.CustomButtons.Button = Class.create();
// Renders Buttons in the following HTML structure
// <span class="gva-btn-container">
//         <span class="left"/>
//         <span class="center">
//                 <button class="btn" style="width: auto;" id="btn_1227001526005">
//                         Créer
//                 </button>
//         </span>
//         <span class="right"/>
// </span>
Object.extend(GvaScript.CustomButtons.Button.prototype, function() {
    var bcss = CSSPREFIX();

    function _extendCss(button_options) {
        // basic class
        var button_css = bcss+'-btn-container';

        // extended classes
        switch (typeof button_options.css) {
            case 'object': button_css += (' ' + button_options.css.join(' ')); break;
            case 'string': button_css += (' ' + button_options.css); break;
            default: break;
        }
        button_options.button_css = button_css;
    }
    var _button_template = new Template(
          '<span class="#{button_css}" id="#{id}">'
        + '<span class="left"></span>'
        + '<span class="center">'
            + '<button type="#{type}" style="width:#{width}" '
            + ' class="btn">#{label}'
            + '</button>'
        + '</span>'
        + '<span class="right"></span>'
        + '</span>'
    );
    function _render(button_options) {
        _extendCss(button_options);
        return _button_template.evaluate(button_options);
    }
    function _evalCondition(button_condition) {
        if(typeof button_condition == 'function') return button_condition();
        else
        if(eval(button_condition)) return true;
        else                       return false;
    }
    return {
        destroy: function() {
            // test that element still in DOM
            if(this.btnElt) this.btnElt.stopObserving('click');
        },
        initialize: function(container, options) {
            var defaults = {
                id: 'btn_' + (new Date()).getTime(),
                callback: Prototype.emptyFunction,
                condition: true,
                width: 'auto',
                type: 'button',
                label: 'GVA_SCRIPT_BUTTON'
            };
            this.options = Object.extend(defaults, options || {});

            if(_evalCondition(this.options.condition)) {
                try {
                    this.container = $(container);
                    this.container.insert(_render(this.options));
                    this.btnContainer = $(this.options.id); // the outer <span/>

                    this.btnElt = this.btnContainer.down('.btn'); // the <button/>

                    // setting inline style on the button container
                    if(typeof this.options.style != 'undefined') {
                        this.btnContainer.setStyle(this.options.style);
                    }

                    // setting tabindex on button if any
                    if(typeof this.options.tabindex != 'undefined') {
                        this.btnElt.writeAttribute('tabindex', this.options.tabindex);
                    }

                    this.btnElt.observe('click', this.options.callback.bind(this.btnElt));
                } catch (e) {}
            }
        }
    }
}());

GvaScript.CustomButtons.ButtonNavigation = Class.create();
Object.extend(GvaScript.CustomButtons.ButtonNavigation.prototype, function() {
        // private members
        var bcss = CSSPREFIX();

        function _leftHandler(event) {
            var selectedBtn = this.selectedBtn;
            if (selectedBtn) {
                var nextBtn = this.previousBtn(selectedBtn);

                if (nextBtn) this.select(nextBtn);
                else         selectedBtn.flash();

                Event.stop(event);
            }
        }
        function _rightHandler(event) {
            var selectedBtn = this.selectedBtn;
            if (selectedBtn) {
                var prevBtn = this.nextBtn(selectedBtn);

                if (prevBtn) this.select(prevBtn);
                else         selectedBtn.flash();

                Event.stop(event);
            }
        }
        function _tabHandler(event) {
            if (this.options.preventListBlur)
                if (this.isLast(this.selectedBtn))
                    Event.stop(event);
        }
        function _shiftTabHandler(event) {
            if (this.options.preventListBlur)
                if (this.isFirst(this.selectedBtn))
                    Event.stop(event);
        }
        function _homeHandler(event) {
            if (this.selectedBtn) {
                this.select(this.firstBtn());
                Event.stop(event);
            }
        }
        function _endHandler(event) {
            if (this.selectedBtn) {
                this.select(this.lastBtn());
                Event.stop(event);
            }
        }
        function _addHandlers() {
            this.buttons.each(function(btnContainer) {
                var btn;
                // if the button is a GvaScript.CustomButtons.BUTTON, then the actual <button> element
                // will be embedded and selectable via .btn classname:
                // <span class="gva-btn-container">
                //         <span class="left"/>
                //         <span class="center">
                //                 <button accesskey="r" class="btn" style="width: auto;" id="btn_1226916357164">
                //                         Rechercher dans Calvin
                //                 </button>
                //         </span>
                //         <span class="right"/>
                // </span>
                // this will be cleaner when all application buttons are transformed into
                // GvaScript.CustomButtons.Button instances
                if(btnContainer.tagName.search(/^(INPUT|BUTTON)$/i) > -1) btn = btnContainer;
                else {
                    btn = btnContainer.down('.btn');
                    btn.visible        = function() {return btnContainer.visible();}
                    // support focus function on span.buttonContainer
                    btnContainer.focus = function() {btn.focus();}
                }

                if(typeof btn == 'undefined') return;

            }, this);

            this.container.register('button.btn', 'focus', function(e) {
                this.select.call(this, e._target.up('.'+bcss+'-btn-container'));
            }.bind(this));
            this.container.register('button.btn', 'blur', function(e) {
                this.select.call(this, null);
            }.bind(this));
        }

        // public members
        return {
            destroy: function() {
                // test that element still in DOM
                if(this.container) this.container.unregister();
                this.keymap.destroy();
            },
            initialize: function(container, options) {
                var defaults = {
                    preventListBlur     : false,
                    flashDuration       : 100,     // milliseconds
                    flashClassName      : 'flash',
                    keymap              : null,
                    selectFirstBtn      : true,
                    className           : bcss+'-button'
                };
                this.options   = Object.extend(defaults, options || {});
                this.container = $(container);

                // initializing the keymap
                var keyHandlers = {
                    LEFT:       _leftHandler     .bindAsEventListener(this),
                    RIGHT:      _rightHandler    .bindAsEventListener(this),
                    TAB:        _tabHandler      .bindAsEventListener(this),
                    S_TAB:      _shiftTabHandler .bindAsEventListener(this),
                    HOME:       _homeHandler     .bindAsEventListener(this),
                    END:        _endHandler      .bindAsEventListener(this)
                };
                this.keymap = new GvaScript.KeyMap(keyHandlers);
                this.keymap.observe("keydown", container, {
                    preventDefault:false,
                    stopPropagation:false
                });

lib/Alien/GvaScript/lib/GvaScript.js  view on Meta::CPAN

            this.back.removeClassName('inactive');
            this.first.removeClassName('inactive');
        }
        else {
            this.back.addClassName('inactive');
            this.first.addClassName('inactive');
        }
        if(this.hasNext()) {
            this.forward.removeClassName('inactive');
            this.last.removeClassName('inactive');
        }
        else {
            this.forward.addClassName('inactive');
            this.last.addClassName('inactive');
        }
        this.links_container.show();
    }
    /* Create pagination controls and append them to the placeholder 'PG:frame' */
    function _addPaginationElts() {
        // append the pagination buttons
        this.links_container.insert(pagination_buttons);

        this.first    = this.links_container.down('.first');
        this.last     = this.links_container.down('.last');
        this.forward  = this.links_container.down('.forward');
        this.back     = this.links_container.down('.back');
        this.textElem = this.links_container.down('.text');

        this.first.observe  ('click', this.getFirstPage.bind(this));
        this.last.observe   ('click', this.getLastPage.bind(this));
        this.back.observe   ('click', this.getPrevPage.bind(this));
        this.forward.observe('click', this.getNextPage.bind(this));
    }

    return {
        destroy: function() {
            this.first.stopObserving();
            this.last.stopObserving();
            this.back.stopObserving();
            this.forward.stopObserving();
        },
        initialize: function(url, options) {

            var defaults = {
                reset                : 'no',    // if yes, first call sends RESET=yes,
                                                // subsequent calls don't (useful for
                                                // resetting cache upon first request)
                step                 : 20,

                method               : 'post',  // POST so we get dispatched to *_PROCESS_FORM
                parameters           : $H({}),
                onSuccess            : Prototype.emptyFunction,

                lazy                 : false,   // false: load first page with Paginator initialization
                                                // true: donot load automatically, loadContent would
                                                // have to be called explicity
                timeoutAjax          : 15,
                errorMsg             : "Problème de connexion. Réessayer et si le problème persiste, contacter un administrateur."
            };
            this.options = Object.extend(defaults, options || {});
            this.options.errorMsg = "<h3 style='color: #183E6C'>" + this.options.errorMsg + "</h3>";

            this.links_container = $(this.options.links_container);
            this.list_container  = $(this.options.list_container);
            this.url             = url;

            // initialization of flags
            this.index         = 1;
            this.end_index     = 0;
            this.total         = 0;

            this._executing    = false; // loadContent one at a time

            // set the css for the paginator container
            this.links_container.addClassName(paginator_css);
            // and hide it
            this.links_container.hide();
            // add the pagination elements (next/prev links + text)
            _addPaginationElts.apply(this);

            this.links_container.addClassName(bcss+'-widget');
            this.links_container.store('widget', this);

            // load content by XHR
            if(!this.options.lazy) this.loadContent();
        },

        hasPrevious: function() {
            return this.index != 1;
        },

        hasNext: function() {
            return this.end_index != this.total;
        },

        /* Get the next set of index to 1records from the current url */
        getNextPage: function(btn) {
            if(this._executing == false && this.hasNext()) {
                this.index += this.options.step;
                this.loadContent();
                return true;
            }
            else
            return false;
        },

        /* Get the prev set of records from the current url */
        getPrevPage: function() {
            if(this._executing == false && this.hasPrevious()) {
                this.index -= this.options.step;
                this.loadContent();
                return true;
            }
            else
            return false;
        },

        getLastPage: function() {
            if(this._executing == false && this.hasNext()) {
                this.index = Math.floor(this.total/this.options.step)*this.options.step+1;
                this.loadContent();

lib/Alien/GvaScript/lib/GvaScript.js  view on Meta::CPAN

            new Ajax.Request(url, {
                evalJSON: 'force',  // force evaluation of response into responseJSON
                method: this.options.method,
                parameters: this.options.parameters,
                requestTimeout: this.options.timeoutAjax * 1000,
                onTimeout: function(req) {
                    this._executing = false;
                    this.list_container.update(this.options.errorMsg);
                }.bind(this),
                // on s'attend à avoir du JSON en retour
                onFailure: function(req) {
                    this._executing = false;
                    var answer = req.responseJSON;
                    var msg = answer.error.message || this.options.errorMsg;
                    this.list_container.update(msg);
                }.bind(this),
                onSuccess: function(req) {
                    this._executing = false;

                    var answer = req.responseJSON;
                    if(answer) {
                        var nb_displayed_records = this.options.onSuccess(answer);
                        this.total     = answer.total; // total number of records

                        this.end_index = Math.min(this.total, this.index+nb_displayed_records-1); // end index of records on current page

                        this.textElem.innerHTML = (this.total > 0)?
                            this.index + " &agrave; " + this.end_index + " de " + this.total: '0';
                        _toggleNavigatorsVisibility.apply(this);
                    }
                 }.bind(this)
            });
        }
    }
}());

//----------grid.js
// depends: custom-buttons.js
//          paginator.js
//          choiceList.js
GvaScript.Grid = Class.create();

Object.extend(GvaScript.Grid.prototype, function() {
    var bcss = CSSPREFIX();
    function _compileDTO(dto) {
        switch(typeof dto) {
            case 'object': return $H(dto).update({VUE: 'JSON'});
            case 'string': return $H(dto.toQueryParams()).update({VUE: 'JSON'});
            default: return {VUE: 'JSON'};
        }
    }
    function _compileCss(column) {
        switch (typeof column.css) {
            case 'object': return ' '+column.css.join(' ');
            case 'string': return ' '+column.css;
            default: return '';
        }
    }
    function _compileWidth(column) {
        switch (typeof column.width) {
        case 'number': return ' style="width: '+column.width+'px"';
            case 'string':
                if(isNaN(column.width)) return ' style="width: '+column.width+'"';
                else                    return ' style="width: '+column.width+'px"';
            default: return '';
        }
    }
    function _compileTitle(column) {
        switch (typeof column.title) {
            case 'string': return 'title= '+'"'+column.title+'"';
            default: return '';
        }
    }        
    function _evalCondition(column, grid) {
        if(typeof column.condition == 'undefined') return true;
        else
        if(typeof column.condition == 'function')  return column.condition(grid);
        else
        if(eval(column.condition))                 return true;
        else                                       return false;
    }
    function _getColumnValue(column, elt) {
        switch(typeof column.value) {
            case 'function' : if(val = column.value(elt)) return val; else return (column.default_value || '');
            case 'string'   : if(val = elt[column.value]) return val; else return (column.default_value || '');
            default: return '';
        }
    }

    return {
        destroy: function() {
            // do not destroy if not initialized !
            if(GvaScript.Grids.unregister(this.id)) {
                if(this.choiceList)    this.choiceList.destroy();
                if(this.actionButtons) this.actionButtons.destroy();
            }
        },
        initialize: function(id, datasource, options) {
            var defaults = {
                css            : '',
                dto            : {},
                columns        : [],
                actions        : [],
                grabfocus      : true,
                pagesize       : 'auto',  // fill available grid height
                gridheight     : 'auto',  // available space
                recordheight   : 21,      // default record height in pixels
                requestTimeout : 15,
                method         : 'post',  // default XHR method
                errorMsg       : "Problème de connexion. Réessayer et si le problème persiste, contacter un administrateur.",
                onShow         : Prototype.emptyFunction,
                onPing         : Prototype.emptyFunction,
                onEmpty        : Prototype.emptyFunction,
                onCancel       : Prototype.emptyFunction
            }

            this.options = Object.extend(defaults, options || {});

            this.id                =  id;
            this.grid_container    = $(this.options.grid_container);
            this.toolbar_container = $(this.options.toolbar_container);
            this.columns           = this.options.columns;
            this.datasource        = datasource;
            // determine pagesize to send to paginator



( run in 0.375 second using v1.01-cache-2.11-cpan-119454b85a5 )