--- a/web/data/cubicweb.widgets.js Mon Jan 04 18:40:30 2016 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,555 +0,0 @@
-/**
- * Functions dedicated to widgets.
- *
- * :organization: Logilab
- * :copyright: 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
- * :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
- *
- *
- */
-
-// widget namespace
-Widgets = {};
-
-/**
- * .. function:: buildWidget(wdgnode)
- *
- * this function takes a DOM node defining a widget and
- * instantiates / builds the appropriate widget class
- */
-function buildWidget(wdgnode) {
- var wdgclass = Widgets[wdgnode.getAttribute('cubicweb:wdgtype')];
- if (wdgclass) {
- return new wdgclass(wdgnode);
- }
- return null;
-}
-
-function renderJQueryDatePicker(subject, button_image, date_format, min_date, max_date){
- $widget = cw.jqNode(subject);
- $widget.datepicker({buttonImage: button_image, dateFormat: date_format,
- firstDay: 1, showOn: "button", buttonImageOnly: true,
- minDate: min_date, maxDate: max_date});
- $widget.change(function(ev) {
- maxOfId = $(this).data('max-of');
- if (maxOfId) {
- cw.jqNode(maxOfId).datepicker("option", "maxDate", this.value);
- }
- minOfId = $(this).data('min-of');
- if (minOfId) {
- cw.jqNode(minOfId).datepicker("option", "minDate", this.value);
- }
- });
-}
-
-/**
- * .. function:: buildWidgets(root)
- *
- * This function is called on load and is in charge to build
- * JS widgets according to DOM nodes found in the page
- */
-function buildWidgets(root) {
- root = root || document;
- jQuery(root).find('.widget').each(function() {
- if (this.getAttribute('cubicweb:loadtype') == 'auto') {
- buildWidget(this);
- }
- });
-}
-
-jQuery(document).ready(function() {
- buildWidgets();
-});
-
-function postJSON(url, data, callback) {
- return jQuery.post(url, data, callback, AJAX_BASE_URL);
-}
-
-function getJSON(url, data, callback) {
- return jQuery.get(url, data, callback, AJAX_BASE_URL);
-}
-
-
-(function ($) {
- var defaultSettings = {
- initialvalue: '',
- multiple: false,
- mustMatch: false,
- delay: 50,
- limit: 50
- };
- function split(val) { return val.split( /\s*,\s*/ ); }
- function extractLast(term) { return split(term).pop(); }
- function allButLast(val) {
- var terms = split(val);
- terms.pop();
- return terms;
- }
-
- var methods = {
- __init__: function(suggestions, options) {
- return this.each(function() {
- // here, `this` refers to the DOM element (e.g. input) being wrapped
- // by cwautomplete plugin
- var instanceData = $(this).data('cwautocomplete');
- if (instanceData) {
- // already initialized
- return;
- }
- var settings = $.extend({}, defaultSettings, options);
- instanceData = {
- initialvalue: settings.initialvalue,
- userInput: this,
- hiddenInput: null
- };
- var hiHandlers = methods.hiddenInputHandlers;
- $(this).data('cwautocomplete', instanceData);
- // in case of an existing value, the hidden input must be initialized even if
- // the value is not changed
- if (($(instanceData.userInput).attr('cubicweb:initialvalue') !== undefined) && !instanceData.hiddenInput){
- hiHandlers.initializeHiddenInput(instanceData);
- }
- $.ui.autocomplete.prototype._value = methods._value;
- $.data(this, 'settings', settings);
- if (settings.multiple) {
- $.ui.autocomplete.filter = methods.multiple.makeFilter(this);
- $(this).bind({
- autocompleteselect: methods.multiple.select,
- autocompletefocus: methods.multiple.focus,
- keydown: methods.multiple.keydown
- });
- }
- // XXX katia we dont need it if minLength == 0, but by setting minLength = 0
- // we probably break the backward compatibility
- $(this).bind('blur', methods.blur);
- if ($.isArray(suggestions)) { // precomputed list of suggestions
- settings.source = hiHandlers.checkSuggestionsDataFormat(instanceData, suggestions);
- } else { // url to call each time something is typed
- settings.source = function(request, response) {
- var d = loadRemote(suggestions, {q: request.term, limit: settings.limit}, 'POST');
- d.addCallback(function (suggestions) {
- suggestions = hiHandlers.checkSuggestionsDataFormat(instanceData, suggestions);
- response(suggestions);
- if((suggestions.length) == 0){
- methods.resetValues(instanceData);
- }
- });
- };
- }
- $(this).autocomplete(settings);
- if (settings.mustMatch) {
- $(this).keypress(methods.ensureExactMatch);
- }
- });
- },
-
- _value: function() {
- /* We extend the widget with the ability to lookup and
- handle several terms at once ('multiple' option). E.g.:
- toto, titi, tu.... The autocompletion must be
- performed only on the last of such a list of terms.
- */
- var settings = $(this.element).data('settings');
- var value = this.valueMethod.apply( this.element, arguments );
- if (settings.multiple & arguments.length === 0) {
- return extractLast(value);
- }
- return value
- },
-
- multiple: {
- focus: function() {
- // prevent value inserted on focus
- return false;
- },
- select: function(event, ui) {
- var terms = allButLast(this.value);
- // add the selected item
- terms.push(ui.item.value);
- // add placeholder to get the comma-and-space at the end
- terms.push("");
- this.value = terms.join( ", " );
- return false;
- },
- keydown: function(evt) {
- if (evt.keyCode == $.ui.keyCode.TAB) {
- evt.preventDefault();
- }
- },
- makeFilter: function(userInput) {
- return function(array, term) {
- // remove already entered terms from suggestion list
- array = cw.utils.difference(array, allButLast(userInput.value));
- var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
- return $.grep( array, function(value) {
- return matcher.test( value.label || value.value || value );
- });
- };
- }
- },
- blur: function(evt){
- var instanceData = $(this).data('cwautocomplete');
- if($(instanceData.userInput).val().strip().length==0){
- methods.resetValues(instanceData);
- }
- },
-
- ensureExactMatch: function(evt) {
- var instanceData = $(this).data('cwautocomplete');
- if (evt.keyCode == $.ui.keyCode.ENTER || evt.keyCode == $.ui.keyCode.TAB) {
- var validChoices = $.map($('ul.ui-autocomplete li'),
- function(li) {return $(li).text();});
- if ($.inArray($(instanceData.userInput).val(), validChoices) == -1) {
- $(instanceData.userInput).val('');
- $(instanceData.hiddenInput).val(instanceData.initialvalue || '');
- }
- }
- },
-
- resetValues: function(instanceData){
- $(instanceData.userInput).val('');
- $(instanceData.hiddenInput).val('');
- },
-
-
- hiddenInputHandlers: {
- /**
- * `hiddenInputHandlers` defines all methods specific to handle the
- * hidden input created along the standard text input.
- * An hiddenInput is necessary when displayed suggestions are
- * different from actual values to submit.
- * Imagine an autocompletion widget to choose among a list of CWusers.
- * Suggestions would be the list of logins, but actual values would
- * be the corresponding eids.
- * To handle such cases, suggestions list should be a list of JS objects
- * with two `label` and `value` properties.
- **/
- suggestionSelected: function(evt, ui) {
- var instanceData = $(this).data('cwautocomplete');
- instanceData.hiddenInput.value = ui.item.value;
- instanceData.value = ui.item.label;
- return false; // stop propagation
- },
-
- suggestionFocusChanged: function(evt, ui) {
- var instanceData = $(this).data('cwautocomplete');
- instanceData.userInput.value = ui.item.label;
- return false; // stop propagation
- },
-
- needsHiddenInput: function(suggestions) {
- return suggestions[0].label !== undefined;
- },
- initializeHiddenInput: function(instanceData) {
- var userInput = instanceData.userInput;
- var hiddenInput = INPUT({
- type: "hidden",
- name: userInput.name,
- // XXX katia : this must be handeled in .SuggestField widget, but
- // it seems not to be used anymore
- value: $(userInput).attr('cubicweb:initialvalue') || userInput.value
- });
- $(userInput).removeAttr('name').after(hiddenInput);
- instanceData.hiddenInput = hiddenInput;
- $(userInput).bind({
- autocompleteselect: methods.hiddenInputHandlers.suggestionSelected,
- autocompletefocus: methods.hiddenInputHandlers.suggestionFocusChanged
- });
- },
-
- /*
- * internal convenience function: old jquery plugin accepted to be fed
- * with a list of couples (value, label). The new (jquery-ui) autocomplete
- * plugin expects a list of objects with "value" and "label" properties.
- *
- * This function converts the old format to the new one.
- */
- checkSuggestionsDataFormat: function(instanceData, suggestions) {
- // check for old (value, label) format
- if ($.isArray(suggestions) && suggestions.length &&
- $.isArray(suggestions[0])){
- if (suggestions[0].length == 2) {
- cw.log('[3.10] autocomplete init func should return {label,value} dicts instead of lists');
- suggestions = $.map(suggestions, function(sugg) {
- return {value: sugg[0], label: sugg[1]};
- });
- } else {
- if(suggestions[0].length == 1){
- suggestions = $.map(suggestions, function(sugg) {
- return {value: sugg[0], label: sugg[0]};
- });
- }
- }
- }
- var hiHandlers = methods.hiddenInputHandlers;
- if (suggestions.length && hiHandlers.needsHiddenInput(suggestions)
- && !instanceData.hiddenInput) {
- hiHandlers.initializeHiddenInput(instanceData);
- hiHandlers.fixUserInputInitialValue(instanceData, suggestions);
- }
- // otherwise, assume data shape is correct
- return suggestions;
- },
-
- fixUserInputInitialValue: function(instanceData, suggestions) {
- // called when the data is loaded to reset the correct displayed
- // value in the visible input field (typically replacing an eid
- // by a displayable value)
- var curvalue = instanceData.userInput.value;
- if (!curvalue) {
- return;
- }
- for (var i=0, length=suggestions.length; i < length; i++) {
- var sugg = suggestions[i];
- if (sugg.value == curvalue) {
- instanceData.userInput.value = sugg.label;
- return;
- }
- }
- }
- }
- };
-
- $.fn.cwautocomplete = function(data, options) {
- return methods.__init__.apply(this, [data, options]);
- };
-})(jQuery);
-
-
-Widgets.SuggestField = defclass('SuggestField', null, {
- __init__: function(node, options) {
- options = options || {};
- var multi = node.getAttribute('cubicweb:multi');
- options.multiple = (multi == "yes") ? true: false;
- var d = loadRemote(node.getAttribute('cubicweb:dataurl'));
- d.addCallback(function(data) {
- $(node).cwautocomplete(data, options);
- });
- }
-});
-
-Widgets.StaticFileSuggestField = defclass('StaticSuggestField', [Widgets.SuggestField], {
-
- __init__: function(node) {
- Widgets.SuggestField.__init__(this, node, {
- method: 'get' // XXX
- });
- }
-
-});
-
-Widgets.RestrictedSuggestField = defclass('RestrictedSuggestField', [Widgets.SuggestField], {
- __init__: function(node) {
- Widgets.SuggestField.__init__(this, node, {
- mustMatch: true
- });
- }
-});
-
-//remote version of RestrictedSuggestField
-Widgets.LazySuggestField = defclass('LazySuggestField', [Widgets.SuggestField], {
- __init__: function(node, options) {
- var self = this;
- options = options || {};
- options.delay = 50;
- // multiple selection not supported yet (still need to formalize correctly
- // initial values / display values)
- var initialvalue = cw.evalJSON(node.getAttribute('cubicweb:initialvalue') || 'null');
- if (!initialvalue) {
- initialvalue = node.value;
- }
- options.initialvalue = initialvalue;
- Widgets.SuggestField.__init__(this, node, options);
- }
-});
-
-/**
- * .. function:: toggleTree(event)
- *
- * called when the use clicks on a tree node
- * - if the node has a `cubicweb:loadurl` attribute, replace the content of the node
- * by the url's content.
- * - else, there's nothing to do, let the jquery plugin handle it.
- */
-function toggleTree(event) {
- var linode = jQuery(this);
- var url = linode.attr('cubicweb:loadurl');
- if (url) {
- linode.find('ul.placeholder').remove();
- var d = linode.loadxhtml(url, null, 'post', 'append');
- d.addCallback(function(domnode) {
- linode.removeAttr('cubicweb:loadurl');
- linode.find('> ul.treeview').treeview({
- toggle: toggleTree,
- prerendered: true
- });
- return null;
- }
- );
- }
-}
-
-Widgets.TemplateTextField = defclass("TemplateTextField", null, {
-
- __init__: function(wdgnode) {
- this.variables = jQuery(wdgnode).attr('cubicweb:variables').split(',');
- this.options = {
- name: wdgnode.getAttribute('cubicweb:inputid'),
- rows: wdgnode.getAttribute('cubicweb:rows') || 40,
- cols: wdgnode.getAttribute('cubicweb:cols') || 80
- };
- // this.variableRegexp = /%\((\w+)\)s/;
- this.errorField = DIV({
- 'class': "errorMessage"
- });
- this.textField = TEXTAREA(this.options);
- jQuery(this.textField).bind('keyup', {
- 'self': this
- },
- this.highlightInvalidVariables);
- jQuery('#substitutions').prepend(this.errorField);
- jQuery('#substitutions .errorMessage').hide();
- wdgnode.appendChild(this.textField);
- },
-
- /* signal callbacks */
-
- highlightInvalidVariables: function(event) {
- var self = event.data.self;
- var text = self.textField.value;
- var unknownVariables = [];
- var it = 0;
- var group = null;
- var variableRegexp = /%\((\w+)\)s/g;
- // emulates rgx.findAll()
- while ( (group = variableRegexp.exec(text)) ) {
- if ($.inArray(group[1], self.variables) == -1) {
- unknownVariables.push(group[1]);
- }
- it++;
- if (it > 5) {
- break;
- }
- }
- var errText = '';
- if (unknownVariables.length) {
- errText = "Detected invalid variables : " + unknownVariables.join(', ');
- jQuery('#substitutions .errorMessage').show();
- } else {
- jQuery('#substitutions .errorMessage').hide();
- }
- self.errorField.innerHTML = errText;
- }
-
-});
-
-cw.widgets = {
- /**
- * .. function:: insertText(text, areaId)
- *
- * inspects textarea with id `areaId` and replaces the current selected text
- * with `text`. Cursor is then set at the end of the inserted text.
- */
- insertText: function (text, areaId) {
- var textarea = jQuery('#' + areaId);
- if (document.selection) { // IE
- var selLength;
- textarea.focus();
- var sel = document.selection.createRange();
- selLength = sel.text.length;
- sel.text = text;
- sel.moveStart('character', selLength - text.length);
- sel.select();
- } else if (textarea.selectionStart || textarea.selectionStart == '0') { // mozilla
- var startPos = textarea.selectionStart;
- var endPos = textarea.selectionEnd;
- // insert text so that it replaces the [startPos, endPos] part
- textarea.value = textarea.value.substring(0, startPos) + text + textarea.value.substring(endPos, textarea.value.length);
- // set cursor pos at the end of the inserted text
- textarea.selectionStart = textarea.selectionEnd = startPos + text.length;
- textarea.focus();
- } else { // safety belt for other browsers
- textarea.value += text;
- }
- }
-};
-
-
-// InOutWidget This contains specific InOutnWidget javascript
-// IE things can not handle hide/show options on select, this cloned list solition (should propably have 2 widgets)
-
-(function ($) {
-
- var methods = {
- __init__: function(fromSelect, toSelect) {
- // closed over state
- var state = {'$fromNode' : $(cw.escape('#' + fromSelect)),
- '$toNode' : $(cw.escape('#' + toSelect)),
- 'name' : this.attr('id')};
-
- function sortoptions($optionlist) {
- var $sorted = $optionlist.find('option').sort(function(opt1, opt2) {
- return $(opt1).text() > $(opt2).text() ? 1 : -1;
- });
- // this somehow translates to an inplace sort
- $optionlist.append($sorted);
- };
- sortoptions(state.$fromNode);
- sortoptions(state.$toNode);
-
- // will move selected options from one list to the other
- // and call an option handler on each option
- function moveoptions ($fromlist, $tolist, opthandler) {
- $fromlist.find('option:selected').each(function(index, option) {
- var $option = $(option);
- // add a new option to the target list
- $tolist.append(OPTION({'value' : $option.val()},
- $option.text()));
- // process callback on the option
- opthandler.call(null, $option);
- // remove option from the source list
- $option.remove();
- });
- // re-sort both lists
- sortoptions($fromlist);
- sortoptions($tolist);
- };
-
- function addvalues () {
- moveoptions(state.$fromNode, state.$toNode, function ($option) {
- // add an hidden input for the edit controller
- var hiddenInput = INPUT({
- type: 'hidden', name: state.name,
- value : $option.val()
- });
- state.$toNode.parent().append(hiddenInput);
- });
- };
-
- function removevalues () {
- moveoptions(state.$toNode, state.$fromNode, function($option) {
- // remove hidden inputs for the edit controller
- var selector = 'input[name=' + cw.escape(state.name) + ']'
- state.$toNode.parent().find(selector).each(function(index, input) {
- if ($(input).val() == $option.val()) {
- $(input).remove();
- }
- });
- });
- };
-
- var $this = $(this);
- $this.find('.cwinoutadd').bind( // 'add >>>' symbol
- 'click', {'state' : state}, addvalues);
- $this.find('.cwinoutremove').bind( // 'remove <<<' symbol
- 'click', {'state' : state}, removevalues);
-
- state.$fromNode.bind('dblclick', {'state': state}, addvalues);
- state.$toNode.bind('dblclick', {'state': state}, removevalues);
-
- }
- };
- $.fn.cwinoutwidget = function(fromSelect, toSelect) {
- return methods.__init__.apply(this, [fromSelect, toSelect]);
- };
-})(jQuery);