Alien-GvaScript
view release on metacpan or search on metacpan
lib/Alien/GvaScript/lib/GvaScript.js view on Meta::CPAN
var v = versionString.replace(/_.*|\./g, '');
v = parseInt(v + '0'.times(4-v.length));
return versionString.indexOf('_') > -1 ? v-1 : v;
}
if((typeof Prototype=='undefined') ||
(typeof Element == 'undefined') ||
(typeof Element.Methods=='undefined') ||
(convertVersionString(Prototype.Version) <
convertVersionString(GvaScript.REQUIRED_PROTOTYPE)))
throw("GvaScript requires the Prototype JavaScript framework >= " +
GvaScript.REQUIRED_PROTOTYPE);
}
};
GvaScript.load();
//----------protoExtensions.js
//-----------------------------------------------------
// Some extensions to the prototype javascript framework
//-----------------------------------------------------
// fire value:change event when setValue method
// is used to change the value of a Form Element
Form.Element.Methods.setValue = Form.Element.Methods.setValue.wrap(
function($p, element, value) {
var oldvalue = $F(element);
var _return = $p(element, value);
element.fire('value:change', {oldvalue: oldvalue, newvalue: value});
return _return;
}
);
Element.addMethods();
// adds the method flash to SPAN, DIV, INPUT, BUTTON elements
// flashes an element by adding a classname for a brief moment of time
// options: {classname: // classname to add (default: flash)
// duration: // duration in ms to keep the classname (default: 100ms)}
var _element_list = ['DIV', 'INPUT',
'BUTTON', 'TEXTAREA', 'A',
'H1', 'H2', 'H3', 'H4', 'H5'];
// for the moment, SPAN not supported on WebKit (see prototype.js bug in
// https://prototype.lighthouseapp.com/projects/8886/tickets/976-elementaddmethodsspan-fails-on-webkit)
if (!Prototype.Browser.WebKit) _element_list.push('SPAN');
Element.addMethods(_element_list, {
flash: function(element, options) {
if (element._IS_FLASHING) return;
element = $(element);
options = options || {};
var duration = options.duration || 100;
var classname = options.classname || 'flash';
element._IS_FLASHING = true;
var endFlash = function() {
this.removeClassName(classname);
this._IS_FLASHING = false;
};
element.addClassName(classname);
setTimeout(endFlash.bind(element), duration);
}
});
// utilities for hash
// expands flat hash into a multi-level deep hash
// javascript version of Perl CGI::Expand::expand_hash
Hash.expand = function(flat_hash) {
var tree = {};
// iterate on keys in the flat hash
for (var k in flat_hash) {
var parts = k.split(/\./);
var loop = {tree: tree, key: "root"};
// iterate on path parts within the key
for (var i = 0 ; i < parts.length; i++) {
var part = parts[i];
// if no subtree yet, build it (Array or Object)
if (!loop.tree[loop.key])
loop.tree[loop.key] = part.match(/^\d+$/) ? [] : {};
// walk down to subtree
loop = {tree: loop.tree[loop.key], key:part};
}
// store value in leaf
loop.tree[loop.key] = flat_hash[k];
}
return tree.root;
}
// collapses deep hash into a one level hash
Hash.flatten = function(deep_hash, prefix, tree) {
tree = tree || {};
for (var i in deep_hash) {
var v = deep_hash[i];
var new_prefix = prefix? prefix + '.' + i : i;
switch (typeof(v)) {
case "function": continue; break;
case "object" : Hash.flatten(v, new_prefix, tree); break;
case "string" :
case "number" : tree["" + new_prefix + ""] = v; break;
default : break;
}
}
return tree;
}
// utilities for string
Object.extend(String.prototype, {
chomp: function() {
return this.replace(/(\n|\r)+$/, '');
}
});
Object.extend(Element, {
lib/Alien/GvaScript/lib/GvaScript.js view on Meta::CPAN
break;
case "object" :
event = arguments[0];
break;
default:
throw new Error("invalid first argument to fireEvent()");
}
var propName = "on" + event.type;
var handler;
var target = arguments[1]; // first element where the event is triggered
var currentTarget; // where the handler is found
// event already fired and executing
if(GvaScript.eventsQueue.hasEvent(target, event.type)) return;
// try to find the handler, first in the HTML elements, then in "this"
for (var i = 1, len = arguments.length; i < len; i++) {
var elem = arguments[i];
if (handler = elem.getAttribute(propName)) {
currentTarget = elem;
break;
}
}
if (currentTarget === undefined)
if (handler = this[propName])
currentTarget = this;
if (handler) {
// build context and copy into event structure
var controller = this;
if (!event.target) event.target = target;
if (!event.srcElement) event.srcElement = target;
if (!event.currentTarget) event.currentTarget = currentTarget;
if (!event.controller) event.controller = controller;
// add the event to the queue, it's about to be fired
GvaScript.eventsQueue.pushEvent(target, event.type);
var event_return = null; // return value of event execution
if (typeof(handler) == "string") {
// string will be eval-ed in a closure context where 'this', 'event',
// 'target' and 'controller' are defined.
var eval_handler = function(){return eval( handler ) };
handler = eval_handler.call(currentTarget); // target bound to 'this'
}
if (handler instanceof Function) {
// now call the eval-ed or pre-bound handler
event_return = handler(event);
}
else {
// whatever was returned by the string evaluation
event_return = handler;
}
// event executed, pop from the queue
// keep a safety margin of 1sec before allowing
// the same event on the same element to be refired
// TODO: is 1sec reasonable
window.setTimeout(function() {
GvaScript.eventsQueue.popEvent(target, event.type)
}, 1000);
return event_return;
}
else
return null; // no handler found
};
//----------keyMap.js
//constructor
GvaScript.KeyMap = function (rules) {
if (!(rules instanceof Object)) throw "KeyMap: invalid argument";
this.rules = [rules];
return this;
};
GvaScript.KeyMap.prototype = {
destroy: function() {
Event.stopObserving(this.elem, this.eventType, this.eventHandler);
},
eventHandler: function(event) {
var keymap = this;
// translate key code into key name
event.keyName = GvaScript.KeyMap.KEYS.BUILTIN_NAMES[event.keyCode]
|| String.fromCharCode(event.keyCode);
// add Control|Shift|Alt modifiers
event.keyModifiers = "";
if (event.ctrlKey && !this.options.ignoreCtrl) event.keyModifiers += "C_";
if (event.shiftKey && !this.options.ignoreShift) event.keyModifiers += "S_";
if (event.altKey && !this.options.ignoreAlt) event.keyModifiers += "A_";
// but cancel all modifiers if main key is Control|Shift|Alt
if (event.keyName.search(/^(CTRL|SHIFT|ALT)$/) == 0)
event.keyModifiers = "";
// try to get the corresponding handler, and call it if found
var handler = keymap._findInStack(event, keymap.rules);
if (handler) {
var toStop = handler.call(keymap, event);
Event.detailedStop(event, toStop || this.options);
}
},
observe: function(eventType, elem, options) {
this.eventType = eventType || 'keydown';
this.elem = elem || document;
// "Shift" modifier usually does not make sense for keypress events
if (eventType == 'keypress' && !options)
options = {ignoreShift: true};
this.options = Class.checkOptions(Event.stopNone, this.options || {});
lib/Alien/GvaScript/lib/GvaScript.js view on Meta::CPAN
loadContent: function (node) {
var url = node.getAttribute('tn:contenturl');
// TODO : default URL generator at the tree level
if (url) {
var content = this.content(node);
if (!content) {
content = document.createElement('div');
content.className = this.classes.content;
var content_type = node.getAttribute('content_type');
if (content_type) content.className += " " + content_type;
content.innerHTML = "loading " + url;
node.insertBefore(content, null); // null ==> insert at end of node
}
this.fireEvent("BeforeLoadContent", node, this.rootElement);
var treeNavigator = this; // needed for closure below
var callback = function() {
treeNavigator.initSubTree(content);
treeNavigator.fireEvent("AfterLoadContent", node, this.rootElement);
};
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)
}
lib/Alien/GvaScript/lib/GvaScript.js view on Meta::CPAN
else
node = sibling;
}
else
node = this.parentNode(node);
}
// case 3: no next Node
return null;
},
// find previous displayed node (i.e. skipping hidden nodes).
previousDisplayedNode: function (node) {
this._assertNodeOrLeaf(node, 'previousDisplayedNode: arg type');
var node_init = node;
while (node) {
node = this.previousSibling(node);
if (node && this.isVisible(node))
return this.lastVisibleSubnode(node);
}
// if no previous sibling
return this.parentNode(node_init);
},
enclosingNode: function (elem) {
return Element.navigateDom(
$(elem), 'parentNode', this.classes.nodeOrLeaf,
this.isRootElement.bind(this));
},
// flash the node
flash: function (node) {
var label = this.label(node);
ASSERT(label, "node has no label");
label.flash({duration: 200});
},
fireEvent: function(eventName, elem) {
var args = [eventName];
while (elem) {
args.push(elem);
elem = this.parentNode(elem);
}
args.push(this.rootElement);
return GvaScript.fireEvent.apply(this, args);
},
//-----------------------------------------------------
// Private methods
//-----------------------------------------------------
// quick navigation initialization:
// - exit navi_mode
// - clear navi_word
// - clear match result
_clear_quick_navi: function() {
if(this._quick_navi_mode !== false)
window.clearTimeout(this._quick_navi_mode);
this._quick_navi_mode = false; // quick_navi mode active (navi timer)
this._quick_navi_word = ""; // word to navigate to
this.labels_array = null; // tree labels array
},
_assertNode: function(elem, msg) {
ASSERT(elem && Element.hasAnyClass(elem, this.classes.node), msg);
},
_assertNodeOrLeaf: function(elem, msg) {
ASSERT(elem && Element.hasAnyClass(elem, this.classes.nodeOrLeaf), msg);
},
_addHandlers: function() {
Event.observe(
this.rootElement, "mouseover",
this._treeMouseOverHandler.bindAsEventListener(this));
Event.observe(
this.rootElement, "mouseout",
this._treeMouseOutHandler.bindAsEventListener(this));
Event.observe(
// observing "mouseup" instead of "click", because "click"
// on MSIE8 only fires when there is a tabindex
this.rootElement, "mouseup",
this._treeClickHandler.bindAsEventListener(this));
Event.observe(
this.rootElement, "dblclick",
this._treeDblClickHandler.bindAsEventListener(this));
},
_removeHandlers: function() {
this.rootElement.stopObserving();
this.rootElement.unregister();
},
//-----------------------------------------------------
// mouse handlers
//-----------------------------------------------------
_treeClickHandler : function(event) {
var target = Event.element(event);
// IE: click on disabled input will fire the event
// with event.srcElement null
if(target.nodeType != 1) return;
// ignore right mousedown
if(!Event.isLeftClick(event)) return;
// button clicked
if(target.hasClassName(this.classes.button)) {
// as not to fire blur_handler
// on treeNode
Event.stop(event);
return this._buttonClicked(target.parentNode);
}
// label (or one of its childElements) clicked
if(label = this.isLabel(target)) {
return this._labelClicked(label.parentNode, event);
}
},
_treeDblClickHandler : function(event) {
var target = Event.element(event);
if(target.nodeType != 1) return;
// only consider doubleclicks on labels
if(!(label = this.isLabel(target))) return;
var event_stop_mode;
// should_ping_on_dblclick was just set within _labelClicked
if (this.should_ping_on_dblclick) {
event_stop_mode = this.fireEvent("Ping", label.parentNode, this.rootElement);
}
// stop the event unless the ping_handler decided otherwise
Event.detailedStop(event, event_stop_mode || Event.stopAll);
},
_treeMouseOverHandler: function(event) {
var target = Event.element(event);
if(target.nodeType != 1) return;
if(label = this.isLabel(target)) {
Element.addClassName(label, this.classes.mouse);
Event.stop(event);
}
},
_treeMouseOutHandler: function(event) {
var target = Event.element(event);
if(target.nodeType != 1) return;
if(label = this.isLabel(target)) {
Element.removeClassName(label, this.classes.mouse);
Event.stop(event);
}
},
_buttonClicked : function(node) {
var method = this.isClosed(node) ? this.open : this.close;
method.call(this, node);
if (this.options.selectOnButtonClick) {
window.setTimeout(function() {
this.select(node);
}.bind(this), 0);
}
},
_labelClicked : function(node, event) {
// situation before the mousedown
var is_selected = (this.selectedNode == node);
var is_first_click = !is_selected;
// select node if it wasn't
if (!is_selected) this.select(node);
// should ping : depends on options.noPingOnFirstClick
var should_ping = (!is_first_click) || !this.options.noPingOnFirstClick;
// do the ping if necessary
var event_stop_mode;
if (should_ping)
event_stop_mode = this.fireEvent("Ping", node, this.rootElement);
// avoid a second ping from the dblclick handler
this.should_ping_on_dblclick = !should_ping;
// stop the event unless the ping_handler decided otherwise
Event.detailedStop(event, event_stop_mode || Event.stopAll);
},
//-----------------------------------------------------
// Keyboard handlers
//-----------------------------------------------------
_addTabbingBehaviour: function() {
if (this.options.tabIndex < 0) return; // no tabbing
var treeNavigator = this; // handlers will be closures on this
// focus handler
var focus_handler = function(e) {
var label = e._target;
label.writeAttribute('hasFocus', 'hasFocus');
var node = Element.navigateDom(label, 'parentNode',
treeNavigator.classes.nodeOrLeaf);
// not yet been selected
if(node && !label.hasClassName(treeNavigator.classes.selected)) {
treeNavigator.select (node);
}
};
// blur handler
var blur_handler = function(e) {
var label = e._target;
label.removeAttribute('hasFocus');
// deselect the previously selected node
treeNavigator.select(null);
};
// focus and blur do not bubble
// workaround per browser
focus_handler = focus_handler.bindAsEventListener(this);
blur_handler = blur_handler.bindAsEventListener(this);
this.rootElement.register('.'+this.classes.label, 'focus', focus_handler);
this.rootElement.register('.'+this.classes.label, 'blur', blur_handler );
},
//-----------------------------------------------------
// timeout handler for firing Select/Deselect events
//-----------------------------------------------------
_selectionTimeoutHandler: function(previousNode) {
this._selectionTimeoutId = null;
var newNode = this.selectedNode;
// fire events
if (previousNode != newNode) {
if (previousNode) {
this.fireEvent("Deselect", previousNode, this.rootElement);
}
if (newNode) {
this.fireEvent("Select", newNode, this.rootElement);
}
}
},
//-----------------------------------------------------
// Key handlers
//-----------------------------------------------------
_charHandler: function (event) {
var selectedNode = this.selectedNode;
if(! selectedNode) return;
// stop firefox quick search if enabled
// via "accessibility.typeaheadfind" => 'true'
Event.stop(event);
this._quick_navi_word += event.keyName; // always uppercase
var is_quick_navi_mode = (this._quick_navi_mode !== false);
// drop the previous timer
if(is_quick_navi_mode) {
window.clearTimeout(this._quick_navi_mode);
}
// initialize labels_array on start of quick-search
// (mandate of dynamic trees)
else {
this.labels_array = this.rootElement.select('.'+this.classes.label);
}
// activate a new timer
this._quick_navi_mode = window.setTimeout(function() {
this._clear_quick_navi();
}.bind(this), 800);
var selectedLabel = this.label(selectedNode);
var selectedIndex = this.labels_array.indexOf(selectedLabel);
// partitions the labels array into 2 arrays
// 1: preceeding labels & selectedNode if not in quick_navi_mode
// 2: following labels & selectedNode if in quick_navi_mode
var labels = this.labels_array.partition(function(l, index) {
// quick-navi mode
if(is_quick_navi_mode) return index < selectedIndex;
else return index <= selectedIndex;
});
// returns first label found to start with word.
var find_match = function(labels, word) {
var match = labels.find(function(label) {
return label.innerHTML.stripTags() // in case label contains HTML elements
.replace(/\r?\n/g, '') // clear line breaks
.replace(/\ \ /g, '') // clear white-spaces
.toUpperCase().startsWith(word);
});
return match;
}
// first look ahead then look back
var matching_label = find_match(labels[1], this._quick_navi_word)
|| find_match(labels[0], this._quick_navi_word);
// found a match -> make it visible and select it
if(matching_label) {
this.openEnclosingNodes(matching_label);
var znode = this.enclosingNode(matching_label);
this.scrollTo(znode);
this.select (znode);
}
// no match -> flash the selected label
else {
this.label(this.selectedNode).flash();
}
},
_downHandler: function (event) {
var selectedNode = this.selectedNode;
if (selectedNode) {
var nextNode = this.nextDisplayedNode(selectedNode);
if (nextNode) {
this.scrollTo(nextNode);
this.select (nextNode);
}
else this.flash(selectedNode);
Event.stop(event);
}
// otherwise: do nothing and let default behaviour happen
},
_upHandler: function (event) {
var selectedNode = this.selectedNode;
lib/Alien/GvaScript/lib/GvaScript.js view on Meta::CPAN
};
//----------autoCompleter.js
/**
TODO:
- messages : choose language
- multivalue :
- inconsistent variable names
- missing doc
- rajouter option "hierarchicalValues : true/false" (si true, pas besoin de
refaire un appel serveur quand l'utilisateur rajoute des lettres).
- sometimes arrowDown should force Ajax call even if < minChars
- choiceElementHTML
- cache choices. Modes are NOCACHE / CACHE_ON_BIND / CACHE_ON_SETUP
- dependentFields should also work with non-strict autocompleters
**/
//----------------------------------------------------------------------
// CONSTRUCTOR
//----------------------------------------------------------------------
GvaScript.AutoCompleter = function(datasource, options) {
var defaultOptions = {
minimumChars : 1,
labelField : "label",
valueField : "value",
autoSuggest : true, // will dropDown automatically on keypress
autoSuggestDelay : 100, // milliseconds, (OBSOLETE)
checkNewValDelay : 100, // milliseconds
typeAhead : true, // will fill the inputElement on highlight
classes : {}, // see below for default classes
maxHeight : 200, // pixels
minWidth : 200, // pixels
offsetX : 0, // pixels
strict : false, // will complain on illegal values
blankOK : true, // if strict, will also accept blanks
colorIllegal : "red", // background color when illegal values
scrollCount : 5,
multivalued : false,
multivalue_separator : /[;,\s]\s*/,
choiceItemTagName: "div",
htmlWrapper : function(html) {return html;},
observed_scroll : null, // observe the scroll of a given element and
// move the dropdown accordingly (useful in
// case of scrolling windows)
additional_params: null, // additional parameters with optional default
// values (only in the case where the
// datasource is a URL)
http_method : 'get', // method for Ajax requests
dependentFields : {},
deltaTime_tolerance : 50, // added msec. for imprecisions in setTimeout
ignorePrefix : false,
caseSensitive: false
};
// more options for array datasources
if (typeof datasource == "object" && datasource instanceof Array) {
defaultOptions.ignorePrefix = false; // if true, will always display
// the full list
defaultOptions.caseSensitive = true;
}
this.options = Class.checkOptions(defaultOptions, options);
// autoSuggestDelay cannot be smaller than checkNewValueDelay
this.options.autoSuggestDelay = Math.max(this.options.autoSuggestDelay,
this.options.checkNewValDelay);
var defaultClasses = {
loading : "AC_loading",
dropdown : "AC_dropdown",
message : "AC_message"
};
this.classes = Class.checkOptions(defaultClasses, this.options.classes);
if (this.options.multivalued && this.options.strict) {
throw new Error("options 'strict' and 'multivalue' are incompatible");
}
this.dropdownDiv = null;
// array to store running ajax requests
// of same autocompleter but for different input element
this._runningAjax = [];
this.setdatasource(datasource);
// prepare an initial keymap; will be registered at first
// focus() event; then a second set of keymap rules is pushed/popped
// whenever the choice list is visible
var basicHandler = this._keyDownHandler.bindAsEventListener(this);
var detectedKeys = /^(BACKSPACE|DELETE|KP_.*|.)$/;
// catch any single char, plus some editing keys
var basicMap = { DOWN: this._ArrowDownHandler.bindAsEventListener(this),
REGEX: [[null, detectedKeys, basicHandler]] };
this.keymap = new GvaScript.KeyMap(basicMap);
// prepare some stuff to be reused when binding to inputElements
this.reuse = {
onblur : this._blurHandler.bindAsEventListener(this),
onclick : this._clickHandler.bindAsEventListener(this)
};
}
GvaScript.AutoCompleter.prototype = {
//----------------------------------------------------------------------
// PUBLIC METHODS
//----------------------------------------------------------------------
lib/Alien/GvaScript/lib/GvaScript.js view on Meta::CPAN
// - 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);
},
_keyDownHandler: function(event) {
// invalidate previous lists of choices because value may have changed
this.choices = null;
this._removeDropdownDiv();
// cancel pending timeouts because we create a new one
if (this._timeoutId) clearTimeout(this._timeoutId);
this._timeLastKeyDown = (new Date()).getTime();
// if (window.console) console.log('keyDown', this._timeLastKeyDown, event.keyCode);
this._timeoutId = setTimeout(this._checkNewValue.bind(this),
this.options.checkNewValDelay);
// do NOT stop the event here : give back control so that the standard
// 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));
}
},
lib/Alien/GvaScript/lib/GvaScript.js view on Meta::CPAN
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();
return true;
}
else
return false;
},
getFirstPage: function() {
if(this._executing == false && this.hasPrevious()) {
this.index = 1;
this.loadContent();
return true;
}
else
return false;
},
// Core function of the pagination object.
// Get records from url that are in the specified range
loadContent: function() {
if(this._executing == true) return; // still handling a previous request
else this._executing = true;
// Add STEP and INDEX as url parameters
var url = this.url;
this.options.parameters.update({
STEP: this.options.step,
INDEX: this.index,
RESET: this.options.reset
});
this.links_container.hide(); // hide 'em. (one click at a time)
this.list_container.update(new Element('div', {'class': bcss+'-loading'}));
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 + " à " + 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
// size is preset
if(typeof this.options.pagesize == 'number') {
this.limit = this.options.pagesize;
}
// determine dynamically
else {
// set the height of the grid_container
// height is preset
if(typeof this.options.gridheight == 'number') {
this.grid_container.setStyle({height: this.options.gridheight+'px'});
}
// determine dynamically
else {
var parentHeight = this.grid_container.up(0).getHeight();
var sibsHeights = this.grid_container.siblings().collect(function(s) {return s.getHeight()});
var sibsHeight = 0;
sibsHeights.each(function(h) {sibsHeight += h});
this.grid_container.setStyle({height: parentHeight-sibsHeight+'px'});
}
this.limit = Math.floor((this.grid_container.getHeight()-22)/this.options.recordheight);
}
this.grid_container.setStyle({width: this.grid_container.up(0).getWidth()+'px'});
this.toolbar_container.addClassName(bcss+'-grid-toolbar');
this.toolbar_container.update();
this.paginatorbar_container = new Element('div', {'class': bcss+'-paginatorbar'});
this.actionsbar_container = new Element('div', {'class': bcss+'-grid-actionsbar'});
this.toolbar_container.insert(this.paginatorbar_container);
this.toolbar_container.insert(this.actionsbar_container);
this.dto = _compileDTO(this.options.dto);
this.paginator = new GvaScript.Paginator(
this.datasource, {
list_container : this.grid_container,
links_container : this.paginatorbar_container,
method : this.options.method,
onSuccess : this.receiveRequest.bind(this),
parameters : this.dto,
step : this.limit,
timeoutAjax : this.options.requestTimeout,
errorMsg : this.options.errorMsg,
lazy : true
}
);
if(! (recycled = this.grid_container.choiceList) ) {
this.choiceList = new GvaScript.ChoiceList([], {
paginator : this.paginator,
mouseovernavi : false,
classes : {'choiceHighlight': "hilite"},
choiceItemTagName : "tr",
grabfocus : false,
htmlWrapper : this.gridWrapper.bind(this)
});
this.choiceList_initialized = false;
}
// recycle the previously created choiceList
else {
this.choiceList = recycled;
this.choiceList.options.htmlWrapper = this.gridWrapper.bind(this);
this.choiceList.options.paginator = this.paginator;
this.choiceList_initialized = true;
}
this.choiceList.onCancel = this.options.onCancel;
this.choiceList.onPing = this.pingWrapper.bind(this);
this.paginator.loadContent();
this.grid_container.addClassName(bcss+'-widget');
this.grid_container.store('widget', this);
GvaScript.Grids.register(this);
},
getId: function() {
return this.id;
},
clearResult: function(msg) {
this.grid_container.update(msg || '');
},
clearToolbar: function() {
this.toolbar_container.update('');
},
clearActionButtons: function() {
this.actionsbar_container.update('');
},
clear: function(msg) {
this.clearResult(msg);
this.clearToolbar();
},
pingWrapper: function(event) {
this.options.onPing(this.records[event.index]);
},
( run in 1.691 second using v1.01-cache-2.11-cpan-119454b85a5 )