Jatin has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/143598

Change subject: Added new input type - tokens
......................................................................

Added new input type - tokens

Added a new input type - tokens into SF. This input type can
be used wherever multiple values are allowed.
Also, changed structure of select2 files by introducing a new base
class that has some basic properties which can be inherited and extended
by combobox and tokens.

Change-Id: I51f2029e0f70f4aa1d99faadb9fd27b334e6ea3d
---
M SemanticForms.php
M i18n/en.json
M includes/SF_FormPrinter.php
M includes/forminputs/SF_TextWithAutocompleteInput.php
A includes/forminputs/SF_TokensInput.php
M libs/SemanticForms.js
A libs/ext.sf.select2.base.js
A libs/ext.sf.select2.combobox.js
A libs/ext.sf.select2.tokens.js
9 files changed, 778 insertions(+), 3 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/SemanticForms 
refs/changes/98/143598/1

diff --git a/SemanticForms.php b/SemanticForms.php
index 738f2ba..6e999ea 100644
--- a/SemanticForms.php
+++ b/SemanticForms.php
@@ -194,6 +194,7 @@
 $GLOBALS['wgAutoloadClasses']['SFTreeInput'] = __DIR__ . 
'/includes/forminputs/SF_TreeInput.php';
 $GLOBALS['wgAutoloadClasses']['SFCategoryInput'] = __DIR__ . 
'/includes/forminputs/SF_CategoryInput.php';
 $GLOBALS['wgAutoloadClasses']['SFCategoriesInput'] = __DIR__ . 
'/includes/forminputs/SF_CategoriesInput.php';
+$GLOBALS['wgAutoloadClasses']['SFTokensInput'] = __DIR__ . 
'/includes/forminputs/SF_TokensInput.php';
 
 $GLOBALS['wgJobClasses']['createPage'] = 'SFCreatePageJob';
 $GLOBALS['wgAutoloadClasses']['SFCreatePageJob'] = __DIR__ . 
'/includes/SF_CreatePageJob.php';
@@ -316,7 +317,9 @@
                'ext.semanticforms.select2' => $sfgResourceTemplate + array(
                        'scripts' => array(
                                'libs/select2.js',
-                               'libs/ext.sf.select2.js',
+                               'libs/ext.sf.select2.base.js',
+                               'libs/ext.sf.select2.combobox.js',
+                               'libs/ext.sf.select2.tokens.js',
                        ),
                        'styles' => array(
                                'skins/select2/select2.css',
diff --git a/i18n/en.json b/i18n/en.json
index e99a926..145274b 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -75,6 +75,7 @@
     "sf_forminputs_width": "The width of this input, in pixels",
     "sf_forminputs_checkboxes_select_all": "Select all",
     "sf_forminputs_checkboxes_select_none": "Select none",
+    "sf_forminputs_maxtokens": "Maximum number of values that can be tokenized 
for a field",
     "createform": "Create a form",
     "sf-createform-with-name": "Create form: $1",
     "sf_createform_nameinput": "Form name",
@@ -204,6 +205,6 @@
     "sf-select2-searching": "Searching...",
     "sf-select2-input-too-short": "Please enter $1 or more characters",
     "sf-select2-input-too-long": "Search input too long",
-    "sf-select2-selection-too-big": "You cannot select any more choices",
+    "sf-select2-selection-too-big": "You can select only $1 choices",
     "sf-select2-load-more": "Loading more results..."
 }
\ No newline at end of file
diff --git a/includes/SF_FormPrinter.php b/includes/SF_FormPrinter.php
index 8618929..3afd029 100644
--- a/includes/SF_FormPrinter.php
+++ b/includes/SF_FormPrinter.php
@@ -48,6 +48,7 @@
                $this->registerInputType( 'SFTreeInput' );
                $this->registerInputType( 'SFCategoryInput' );
                $this->registerInputType( 'SFCategoriesInput' );
+               $this->registerInputType( 'SFTokensInput' );
 
                // All-purpose setup hook.
                wfRunHooks( 'sfFormPrinterSetup', array( $this ) );
diff --git a/includes/forminputs/SF_TextWithAutocompleteInput.php 
b/includes/forminputs/SF_TextWithAutocompleteInput.php
index 712a4d7..acfdd29 100644
--- a/includes/forminputs/SF_TextWithAutocompleteInput.php
+++ b/includes/forminputs/SF_TextWithAutocompleteInput.php
@@ -88,7 +88,7 @@
        }
 
        public static function setAutocompleteValues( $field_args ) {
-               global $sfgAutocompleteValues;
+               global $sfgAutocompleteValues, $sfgMaxLocalAutocompleteValues;
 
                // Get all autocomplete-related values, plus delimiter value
                // (it's needed also for the 'uploadable' link, if there is 
one).
@@ -122,6 +122,10 @@
                        } else {
                                $autocompleteValues = 
SFUtils::getAutocompleteValues( $autocompletionSource, $autocompleteFieldType );
                        }
+                       if( count($autocompleteValues) > 
$sfgMaxLocalAutocompleteValues &&
+                       $autocompleteFieldType != 'values' && 
!array_key_exists( 'values dependent on', $field_args ) ) {
+                               $remoteDataType = $autocompleteFieldType;
+                       }
                        $sfgAutocompleteValues[$autocompleteSettings] = 
$autocompleteValues;
                }
                return array( $autocompleteSettings, $remoteDataType, 
$delimiter );
diff --git a/includes/forminputs/SF_TokensInput.php 
b/includes/forminputs/SF_TokensInput.php
new file mode 100644
index 0000000..2ba93a2
--- /dev/null
+++ b/includes/forminputs/SF_TokensInput.php
@@ -0,0 +1,146 @@
+<?php
+/**
+ * File holding the SFTokensInput class
+ *
+ * @file
+ * @ingroup SF
+ */
+
+/**
+ * The SFTokensInput class.
+ *
+ * @ingroup SFFormInput
+ */
+class SFTokensInput extends SFFormInput {
+       public static function getName() {
+               return 'tokens';
+       }
+
+       public static function getDefaultPropTypes() {
+               return array();
+       }
+
+       public static function getOtherPropTypesHandled() {
+               $otherPropTypesHandled = array( '_wpg' );
+               if ( defined( 'SMWDataItem::TYPE_STRING' ) ) {
+                       // SMW < 1.9
+                       $otherPropTypesHandled[] = '_str';
+               } else {
+                       $otherPropTypesHandled[] = '_txt';
+               }
+               return $otherPropTypesHandled;
+       }
+
+       public static function getDefaultPropTypeLists() {
+               return array();
+       }
+
+       public static function getOtherPropTypeListsHandled() {
+               $otherPropTypesListsHandled = array( '_wpg' );
+               if ( defined( 'SMWDataItem::TYPE_STRING' ) ) {
+                       // SMW < 1.9
+                       $otherPropTypesListsHandled[] = '_str';
+               } else {
+                       $otherPropTypesListsHandled[] = '_txt';
+               }
+               return $otherPropTypesListsHandled;
+       }
+
+       public static function getHTML( $cur_value, $input_name, $is_mandatory, 
$is_disabled, $other_args ) {
+               global $sfgTabIndex, $sfgFieldNum;
+
+               list( $autocompleteSettings, $remoteDataType, $delimiter ) = 
SFTextWithAutocompleteInput::setAutocompleteValues( $other_args );
+
+               $className = 'sfTokens ';
+               $className .= ( $is_mandatory ) ? 'mandatoryField' : 
'createboxInput';
+               if ( array_key_exists( 'class', $other_args ) ) {
+                       $className .= ' ' . $other_args['class'];
+               }
+               $input_id = 'input_' . $sfgFieldNum;
+
+               if ( array_key_exists( 'size', $other_args ) ) {
+                       $size = $other_args['size'];
+               } else {
+                       $size = '35';
+               }
+
+               $inputAttrs = array(
+                       'id' => $input_id,
+                       'size' => $size,
+                       'class' => $className,
+                       'tabindex' => $sfgTabIndex,
+                       'autocompletesettings' => $autocompleteSettings,
+               );
+               if ( array_key_exists( 'origName', $other_args ) ) {
+                       $inputAttrs['origName'] = $other_args['origName'];
+               }
+               if ( array_key_exists( 'existing values only', $other_args ) ) {
+                       $inputAttrs['existingvaluesonly'] = 'true';
+               }
+               if ( !is_null( $remoteDataType ) ) {
+                       $inputAttrs['autocompletedatatype'] = $remoteDataType;
+               }
+               if ( $is_disabled ) {
+                       $inputAttrs['disabled'] = true;
+               }
+               if ( array_key_exists( 'maxlength', $other_args ) ) {
+                       $inputAttrs['maxlength'] = $other_args['maxlength'];
+               }
+               if ( array_key_exists( 'placeholder', $other_args ) ) {
+                       $inputAttrs['placeholder'] = $other_args['placeholder'];
+               }
+               if ( array_key_exists( 'max tokens', $other_args ) ) {
+                       $inputAttrs['maxtokens'] = $other_args['max tokens'];
+               }
+               $text = "\n\t" . Html::input( $input_name, $cur_value, 'text', 
$inputAttrs ) . "\n";
+
+               $spanClass = 'inputSpan';
+               if ( $is_mandatory ) {
+                       $spanClass .= ' mandatoryFieldSpan';
+               }
+               $text = "\n" . Html::rawElement( 'span', array( 'class' => 
$spanClass ), $text );
+
+               return $text;
+       }
+
+
+       public static function getParameters() {
+               $params = parent::getParameters();
+               $params[] = array(
+                       'name' => 'size',
+                       'type' => 'int',
+                       'description' => wfMessage( 'sf_forminputs_size' 
)->text()
+               );
+               $params[] = array(
+                       'name' => 'placeholder',
+                       'type' => 'string',
+                       'description' => wfMessage( 'sf_forminputs_placeholder' 
)->text()
+               );
+               $params[] = array(
+                       'name' => 'existing values only',
+                       'type' => 'boolean',
+                       'description' => wfMessage( 
'sf_forminputs_existingvaluesonly' )->text()
+               );
+               $params[] = array(
+                       'name' => 'max tokens',
+                       'type' => 'int',
+                       'description' => wfMessage( 'sf_forminputs_maxtokens' 
)->text()
+               );
+               $params = array_merge( $params, 
SFTextWithAutocompleteInput::getAutocompletionParameters() );
+
+               return $params;
+       }
+
+       /**
+        * Returns the HTML code to be included in the output page for this 
input.
+        */
+       public function getHtmlText() {
+               return self::getHTML(
+                       $this->mCurrentValue,
+                       $this->mInputName,
+                       $this->mIsMandatory,
+                       $this->mIsDisabled,
+                       $this->mOtherArgs
+               );
+       }
+}
diff --git a/libs/SemanticForms.js b/libs/SemanticForms.js
index df3e462..5e6fc5b 100644
--- a/libs/SemanticForms.js
+++ b/libs/SemanticForms.js
@@ -983,6 +983,10 @@
        this.find('.sfComboBox').not('#semantic_property_starter, 
.multipleTemplateStarter .sfComboBox, .select2-container').each( function() {
                combobox.apply($(this));
        });
+       var tokens = new sf.select2.tokens();
+       this.find('.sfTokens').not('#semantic_property_starter, 
.multipleTemplateStarter .sfComboBox, .select2-container').each( function() {
+               tokens.apply($(this));
+       });
 
        this.find('.autoGrow').autoGrow();
        this.find('.sfFancyBox').fancybox({
diff --git a/libs/ext.sf.select2.base.js b/libs/ext.sf.select2.base.js
new file mode 100644
index 0000000..f3e4ca3
--- /dev/null
+++ b/libs/ext.sf.select2.base.js
@@ -0,0 +1,252 @@
+/*
+* ext.sf.select2.base.js
+*
+* Base class to handle autocomplete
+* for various input types using Select2 JS library
+*
+* @file
+*
+*
+* @licence GNU GPL v2+
+* @author Jatin Mehta
+*
+*/
+
+( function ( $, mw, sf ) {
+       'use strict';
+       /**
+        * Inheritance class for the sf.select2 constructor
+        *
+        *
+        * @class
+        */
+       sf.select2 = sf.select2 = sf.select2 || {};
+
+       /**
+        * Class constructor
+        *
+        *
+        * @class
+        * @constructor
+        */
+       sf.select2.base = function() {
+
+       };
+
+       sf.select2.base.prototype = {
+               /*
+                * Applies select2 to the HTML element
+                *
+                * @param {HTMLElement} element
+                *
+                */
+               apply: function( element ) {
+                       this.id = element.attr( "id" );
+                       var opts = this.setOptions();
+
+                       element.select2(opts);
+                       element.on( "change", this.onChange );
+               },
+               /*
+                * Used to remove the select2 applied to the HTML element,
+                * the selected value will remain preserved.
+                *
+                * @param {HTMLElement} element
+                *
+                */
+               destroy: function( element ) {
+                       element.select2( "destroy" );
+               },
+               /*
+                * Returns options to be set by select2
+                *
+                * @return {object} opts
+                *
+                */
+               setOptions: function() {
+                       var input_id = this.id;
+                       var opts = {};
+                       var input_id = "#" + input_id;
+                       var input_tagname = $(input_id).prop( "tagName" );
+                       var autocomplete_opts = this.getAutocompleteOpts();
+
+                       if ( autocomplete_opts.autocompletedatatype != 
undefined ) {
+                               opts.ajax = this.getAjaxOpts();
+                               opts.minimumInputLength = 1;
+                               opts.formatInputTooShort = mw.msg( 
"sf-select2-input-too-short", opts.minimumInputLength );
+                               opts.formatResult = this.formatResult;
+                               opts.formatSelection = this.formatSelection;
+                               opts.escapeMarkup = function (m) { return m; };
+                       } else if ( input_tagname == "INPUT" ) {
+                               opts.data = this.getData( 
autocomplete_opts.autocompletesettings );
+                       }
+                       var sfgAutocompleteOnAllChars = mw.config.get( 
'sfgAutocompleteOnAllChars' );
+                       if ( !sfgAutocompleteOnAllChars ) {
+                               opts.matcher = function( term, text ) { return 
text.toUpperCase().indexOf(term.toUpperCase())==0; };
+                       }
+                       opts.formatSearching = mw.msg( "sf-select2-searching" );
+                       opts.formatNoMatches = mw.msg( "sf-select2-no-matches" 
);
+                       opts.placeholder = $(input_id).attr( "placeholder" );
+                       if ( $(input_id).attr( "existingvaluesonly" ) !== 
"true" && input_tagname == "INPUT" ) {
+                               opts.createSearchChoice = function( term, data 
) { if ( $(data).filter(function() { return this.text.localeCompare( term 
)===0; }).length===0 ) {return { id:term, text:term };} };
+                       }
+                       if ( $(input_id).val() != "" && input_tagname == 
"INPUT" ) {
+                               opts.initSelection = function ( element, 
callback ) { var data = {id: element.val(), text: element.val()}; 
callback(data); };
+                       }
+                       opts.allowClear = true;
+                       var size = $(input_id).attr("size");
+                       if ( size == undefined ) {
+                               size = 35; //default value
+                       }
+                       opts.containerCss = { 'min-width': size * 6 };
+                       opts.containerCssClass = 'sf-select2-container';
+                       opts.dropdownCssClass = 'sf-select2-dropdown';
+
+                       return opts;
+               },
+               /*
+                * Returns HTML text to be used by select2 for
+                * showing remote data retrieved
+                *
+                * @param {object} value
+                * @param {object} container
+                * @param {object} query
+                *
+                * @return {string} markup
+                *
+                */
+               formatResult: function(value, container, query) {
+                       var term = query.term.toLowerCase();
+                       var result = value.text;
+                       var start = result.toLowerCase().indexOf(term);
+                       var end = start + term.length - 1;
+                       var markup = result.substr(0, start) +
+                               '<span class="select2-match">' +
+                                       result.substr(start, end - start + 1) +
+                               '</span>' +
+                               result.substr(end + 1);
+
+                       return markup;
+               },
+               /*
+                * Returns string/HTML text to be used by select2 to
+                * show selection
+                *
+                * @param {object} value (The selected result object)
+                *
+                * @return {string}
+                *
+                */
+               formatSelection: function(value) {
+                       return value.text;
+               },
+               /*
+                * If a field is dependent on some other field in the form
+                * then it returns its name.
+                *
+                * @return {string}
+                *
+                */
+               dependentOn: function() {
+                       var input_id = "#" + this.id;
+                       var name_attr = this.nameAttr( $(input_id) );
+                       var name = $(input_id).attr( name_attr );
+                       var dependent_on_me = [];
+
+                       var sfgDependentFields = mw.config.get( 
'sfgDependentFields' );
+                       for ( var i = 0; i < sfgDependentFields.length; i++ ) {
+                               var dependentFieldPair = sfgDependentFields[i];
+                               if ( dependentFieldPair[1] == name ) {
+                                        return dependentFieldPair[0];
+                               }
+                       }
+                       return null;
+               },
+               /*
+                * Returns the array of names of fields in the form which are 
dependent
+                * on the field passed as a param to this function,
+                *
+                * @param {HTMLElement} element
+                *
+                * @return {associative array} dependent_on_me
+                *
+                */
+               dependentOnMe: function( element ) {
+                       var name_attr = this.nameAttr(element);
+                       var name = element.attr( name_attr );
+                       var dependent_on_me = [];
+
+                       var sfgDependentFields = mw.config.get( 
'sfgDependentFields' );
+                       for ( var i = 0; i < sfgDependentFields.length; i++ ) {
+                               var dependentFieldPair = sfgDependentFields[i];
+                               if ( dependentFieldPair[0] == name ) {
+                                       
dependent_on_me.push(dependentFieldPair[1]);
+                               }
+                       }
+
+                       return dependent_on_me;
+               },
+               /*
+                * Returns the name attribute of the field depending on
+                * whether it is a part of multiple instance template or not
+                *
+                * @param {HTMLElement} element
+                *
+                * @return {string}
+                *
+                */
+               nameAttr: function( element ) {
+                       return  this.partOfMultiple( element ) ? "origname" : 
"name";
+               },
+               /*
+                * Checks whether the field is part of multiple instance 
template or not
+                *
+                * @param {HTMLElement} element
+                *
+                * @return {boolean}
+                *
+                */
+               partOfMultiple: function( element ) {
+                       return element.attr( "origname" ) != undefined ? true : 
false;
+               },
+               /*
+                * Gives dependent field options which include
+                * property, base property and base value
+                *
+                * @param {string} dep_on
+                *
+                * @return {object} dep_field_opts
+                *
+                */
+               getDependentFieldOpts: function( dep_on ) {
+                       var input_id = "#" + this.id;
+                       var dep_field_opts = {};
+                       if ( this.partOfMultiple($(input_id)) ) {
+                               var base_element = $(input_id).closest( 
".multipleTemplateInstance" )
+                                                               .find( 
'[origname ="' + dep_on + '" ]' );
+                       } else {
+                               var base_element = $('[name ="' + dep_on + '" 
]');
+                       }
+                       dep_field_opts.base_value = base_element.val();
+                       dep_field_opts.base_prop = base_element.attr( 
"autocompletesettings" );
+                       dep_field_opts.prop = $(input_id).attr( 
"autocompletesettings" );
+
+                       return dep_field_opts;
+               },
+               /*
+                * Gives autocomplete options for a field
+                *
+                *
+                * @return {object} autocomplete_opts
+                *
+                */
+               getAutocompleteOpts: function() {
+                       var input_id = "#" + this.id;
+                       var autocomplete_opts = {};
+                       autocomplete_opts.autocompletedatatype = 
$(input_id).attr( "autocompletedatatype" );
+                       autocomplete_opts.autocompletesettings = 
$(input_id).attr( "autocompletesettings" );
+
+                       return autocomplete_opts;
+               },
+       };
+} )( jQuery, mediaWiki, semanticforms );
\ No newline at end of file
diff --git a/libs/ext.sf.select2.combobox.js b/libs/ext.sf.select2.combobox.js
new file mode 100644
index 0000000..7665355
--- /dev/null
+++ b/libs/ext.sf.select2.combobox.js
@@ -0,0 +1,149 @@
+/*
+ * ext.sf.select2.js
+ *
+ * Javascript utility class to handle autocomplete
+ * for combobox input type using Select2 JS library
+ *
+ * @file
+ *
+ * @licence GNU GPL v2+
+ * @author Jatin Mehta
+ */
+
+( function( $, mw, sf ) {
+       'use strict';
+
+       /**
+        * Inheritance class for the sf.select2 constructor
+        *
+        *
+        * @class
+        */
+       sf.select2 = sf.select2 = sf.select2 || {};
+
+       /**
+        * Class constructor
+        *
+        *
+        * @class
+        * @constructor
+        */
+       sf.select2.combobox = function() {
+
+       };
+
+       var combobox_proto = new sf.select2.base();
+       /*
+        * Returns data to be used by select2 for combobox autocompletion
+        *
+        * @param {string} autocompletesettings
+        * @return {associative array} values
+        *
+        */
+       combobox_proto.getData = function( autocompletesettings ) {
+               var values = [{id: 0, text: ""}];
+               var dep_on = this.dependentOn();
+               if ( dep_on == null ) {
+                       var sfgAutocompleteValues = mw.config.get( 
'sfgAutocompleteValues' );
+                       var data = sfgAutocompleteValues[autocompletesettings];
+                       var i = 0;
+                       //Convert data into the format accepted by Select2
+                       if (data != undefined) {
+                               data.forEach(function()
+                               {
+                                   values.push({
+                                       id: i + 1, text: data[i]
+                                   });
+                                   i++;
+                               });
+                       }
+               } else { //Dependent field autocompletion
+                       var dep_field_opts = this.getDependentFieldOpts( dep_on 
);
+                       var my_server = mw.config.get( 'wgScriptPath' ) + 
"/api.php";
+                       my_server += 
"?action=sfautocomplete&format=json&property=" + dep_field_opts.prop + 
"&baseprop=" + dep_field_opts.base_prop + "&basevalue=" + 
dep_field_opts.base_value;
+                       $.ajax({
+                               url: my_server,
+                               dataType: 'json',
+                               async: false,
+                               success: function(data) {
+                                       var id = 1;
+                                       //Convert data into the format accepted 
by Select2
+                                       data.sfautocomplete.forEach( 
function(item) {
+                                               values.push({
+                                                       id: id++, text: 
item.title
+                                               });
+                                       });
+                                       return values;
+                               }
+                       });
+               }
+
+               return values;
+       };
+       /*
+        * Returns ajax options to be used by select2 for
+        * remote autocompletion of combobox
+        *
+        * @return {object} ajaxOpts
+        *
+        */
+       combobox_proto.getAjaxOpts = function() {
+               var input_id = "#" + this.id;
+               var autocomplete_opts = this.getAutocompleteOpts();
+               var my_server = mw.util.wikiScript( 'api' );
+               my_server += "?action=sfautocomplete&format=json&" + 
autocomplete_opts.autocompletedatatype + "=" + 
autocomplete_opts.autocompletesettings;
+
+               var ajaxOpts = {
+                       url: my_server,
+                       dataType: 'jsonp',
+                       data: function (term) {
+                               return {
+                                       substr: term, // search term
+                               };
+                       },
+                       results: function (data, page, query) { // parse the 
results into the format expected by Select2.
+                               var id = 0;
+                               data.sfautocomplete.forEach( function(item) {
+                                       item.id = id++;
+                                       item.text = item.title;
+                               });
+                               return {results: data.sfautocomplete};
+                       }
+               };
+
+               return ajaxOpts;
+       };
+       /*
+        * Used to set the value of the HTMLInputElement
+        * when there is a change in the select2 value
+        *
+        */
+       combobox_proto.onChange = function() {
+               var self = this;
+               var data = $(this).select2( "data" );
+               if (data != null) {
+                       $(this).val( data.text );
+               } else {
+                       $(this).val( '' );
+               }
+
+               // Set the corresponding values for any other field
+               // in the form which is deoendent on this element
+               var cmbox = new sf.select2.combobox();
+               var dep_on_me = cmbox.dependentOnMe( $(this) );
+               dep_on_me.forEach( function( dependent_field_name ) {
+                       if ( cmbox.partOfMultiple( $(self) ) ) {
+                               var dependent_field = $(self).closest( 
".multipleTemplateInstance" )
+                                       .find( '[origname ="' + 
dependent_field_name + '" ]' );
+                       } else {
+                               var dependent_field = $('[name ="' + 
dependent_field_name + '" ]');
+                       }
+                       cmbox.destroy($(dependent_field));
+                       $(dependent_field).val( '' );
+                       cmbox.apply($(dependent_field));
+               });
+       };
+
+       sf.select2.combobox.prototype = combobox_proto;
+
+} )( jQuery, mediaWiki, semanticforms );
\ No newline at end of file
diff --git a/libs/ext.sf.select2.tokens.js b/libs/ext.sf.select2.tokens.js
new file mode 100644
index 0000000..f29f704
--- /dev/null
+++ b/libs/ext.sf.select2.tokens.js
@@ -0,0 +1,215 @@
+/*
+ * ext.sf.select2.js
+ *
+ * Javascript utility class to handle autocomplete
+ * for tokens input type using Select2 JS library
+ *
+ * @file
+ *
+ * @licence GNU GPL v2+
+ * @author Jatin Mehta
+ */
+
+( function( $, mw, sf ) {
+       'use strict';
+
+       /**
+        * Inheritance class for the sf.select2 constructor
+        *
+        *
+        * @class
+        */
+       sf.select2 = sf.select2 = sf.select2 || {};
+
+       /**
+        * Class constructor
+        *
+        *
+        * @class
+        * @constructor
+        */
+       sf.select2.tokens = function() {
+
+       };
+
+       var tokens_proto = new sf.select2.base();
+       /*
+        * Returns options to be set by select2
+        *
+        * @return {object} opts
+        *
+        */
+       tokens_proto.setOptions = function() {
+               var input_id = this.id;
+               var opts = {};
+               var input_id = "#" + input_id;
+               var input_tagname = $(input_id).prop( "tagName" );
+               var autocomplete_opts = this.getAutocompleteOpts();
+
+               if ( autocomplete_opts.autocompletedatatype != undefined ) {
+                       opts.ajax = this.getAjaxOpts();
+                       opts.minimumInputLength = 1;
+                       opts.formatInputTooShort = mw.msg( 
"sf-select2-input-too-short", opts.minimumInputLength );
+                       opts.formatResult = this.formatResult;
+                       opts.formatSelection = this.formatSelection;
+                       opts.escapeMarkup = function (m) { return m; };
+               } else if ( input_tagname == "INPUT" ) {
+                       opts.data = this.getData( 
autocomplete_opts.autocompletesettings );
+               }
+               var sfgAutocompleteOnAllChars = mw.config.get( 
'sfgAutocompleteOnAllChars' );
+               if ( !sfgAutocompleteOnAllChars ) {
+                       opts.matcher = function( term, text ) { return 
text.toUpperCase().indexOf(term.toUpperCase())==0; };
+               }
+               opts.formatSearching = mw.msg( "sf-select2-searching" );
+               opts.formatNoMatches = mw.msg( "sf-select2-no-matches" );
+               opts.placeholder = $(input_id).attr( "placeholder" );
+               if ( $(input_id).attr( "existingvaluesonly" ) !== "true" && 
input_tagname == "INPUT" ) {
+                       opts.createSearchChoice = function( term, data ) { if ( 
$(data).filter(function() { return this.text.localeCompare( term )===0; 
}).length===0 ) {return { id:term, text:term };} };
+               }
+               if ( $(input_id).val() != "" && input_tagname == "INPUT" ) {
+                       opts.initSelection = function ( element, callback ) { 
var data = {id: element.val(), text: element.val()}; callback(data); };
+               }
+               opts.allowClear = true;
+               var size = $(input_id).attr("size");
+               if ( size == undefined ) {
+                       size = 35; //default value
+               }
+               opts.containerCss = { 'min-width': size * 6 };
+               opts.containerCssClass = 'sf-select2-container';
+               opts.dropdownCssClass = 'sf-select2-dropdown';
+
+               opts.multiple = true;
+               var maxtokens = $(input_id).attr( "maxtokens" );
+               if ( maxtokens != undefined ) {
+                       opts.maximumSelectionSize = maxtokens;
+                       opts.formatSelectionTooBig = mw.msg( 
"sf-select2-selection-too-big", maxtokens );
+               }
+               opts.adaptContainerCssClass = function( clazz ) { if (clazz == 
"mandatoryField") return ""; };
+
+               return opts;
+       };
+       /*
+        * Returns data to be used by select2 for tokens autocompletion
+        *
+        * @param {string} autocompletesettings
+        * @return {associative array} values
+        *
+        */
+       tokens_proto.getData = function( autocompletesettings ) {
+               var values = [{id: 0, text: ""}];
+               var dep_on = this.dependentOn();
+               if ( dep_on == null ) {
+                       var sfgAutocompleteValues = mw.config.get( 
'sfgAutocompleteValues' );
+                       var data = sfgAutocompleteValues[autocompletesettings];
+                       var i = 0;
+                       //Convert data into the format accepted by Select2
+                       if (data != undefined) {
+                               data.forEach(function()
+                               {
+                                   values.push({
+                                       id: i + 1, text: data[i]
+                                   });
+                                   i++;
+                               });
+                       }
+               } else { //Dependent field autocompletion
+                       var dep_field_opts = this.getDependentFieldOpts( dep_on 
);
+                       var my_server = mw.config.get( 'wgScriptPath' ) + 
"/api.php";
+                       my_server += 
"?action=sfautocomplete&format=json&property=" + dep_field_opts.prop + 
"&baseprop=" + dep_field_opts.base_prop + "&basevalue=" + 
dep_field_opts.base_value;
+                       $.ajax({
+                               url: my_server,
+                               dataType: 'json',
+                               async: false,
+                               success: function(data) {
+                                       var id = 1;
+                                       //Convert data into the format accepted 
by Select2
+                                       data.sfautocomplete.forEach( 
function(item) {
+                                               values.push({
+                                                       id: id++, text: 
item.title
+                                               });
+                                       });
+                                       return values;
+                               }
+                       });
+               }
+
+               return values;
+       };
+       /*
+        * Returns ajax options to be used by select2 for
+        * remote autocompletion of tokens
+        *
+        * @return {object} ajaxOpts
+        *
+        */
+       tokens_proto.getAjaxOpts = function() {
+               var input_id = "#" + this.id;
+               var autocomplete_opts = this.getAutocompleteOpts();
+               var data_source = 
autocomplete_opts.autocompletesettings.split(',')[0];
+               var my_server = mw.util.wikiScript( 'api' );
+               my_server += "?action=sfautocomplete&format=json&" + 
autocomplete_opts.autocompletedatatype + "=" + data_source;
+
+               var ajaxOpts = {
+                       url: my_server,
+                       dataType: 'jsonp',
+                       data: function (term) {
+                               return {
+                                       substr: term, // search term
+                               };
+                       },
+                       results: function (data, page, query) { // parse the 
results into the format expected by Select2.
+                               var id = 0;
+                               data.sfautocomplete.forEach( function(item) {
+                                       item.id = id++;
+                                       item.text = item.title;
+                               });
+                               return {results: data.sfautocomplete};
+                       }
+               };
+
+               return ajaxOpts;
+       };
+       /*
+        * Used to set the value of the HTMLInputElement
+        * when there is a change in the select2 value
+        *
+        */
+       tokens_proto.onChange = function() {
+               var self = this;
+               var data = $(this).select2( "data" );
+
+               var tokens = new sf.select2.tokens();
+               var delim = tokens.getDelimiter( $(this) );
+
+               if (data != null) {
+                       var tokens_value = "";
+                       data.forEach( function( token ) {
+                               tokens_value += token.text + delim;
+                       });
+                       $(this).val( tokens_value );
+               } else {
+                       $(this).val( '' );
+               }
+       };
+       /*
+        * Returns delimiter for the token field
+        *
+        * @return {string} delimiter
+        *
+        */
+       tokens_proto.getDelimiter = function ( element ) {
+               var field_values = element.attr('autocompletesettings').split( 
',' );
+               var delimiter = null;
+               if (field_values[1] == 'list') {
+                       delimiter = ",";
+                       if (field_values[2] != null) {
+                               delimiter = field_values[2];
+                       }
+               }
+
+               return delimiter;
+       };
+
+       sf.select2.tokens.prototype = tokens_proto;
+
+} )( jQuery, mediaWiki, semanticforms );
\ No newline at end of file

-- 
To view, visit https://gerrit.wikimedia.org/r/143598
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I51f2029e0f70f4aa1d99faadb9fd27b334e6ea3d
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/SemanticForms
Gerrit-Branch: master
Gerrit-Owner: Jatin <mehtajati...@gmail.com>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to