From a563c62d334c7fcfff154d9a9382ae2d42226980 Mon Sep 17 00:00:00 2001 From: Thomas Schreiber Date: Wed, 10 Sep 2014 00:16:25 +0200 Subject: [PATCH 1/7] Add django_select2 to the global space. When javascript output is wrapped django_select2 can not be found, adding it to the window instead of a var fixes this: https://django-pipeline.readthedocs.org/en/latest/configuration.html#wrapped-javascript-output --- django_select2/static/js/heavy_data.js | 10 +++++----- django_select2/static/js/heavy_data.min.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/django_select2/static/js/heavy_data.js b/django_select2/static/js/heavy_data.js index 188505c..d23a46e 100644 --- a/django_select2/static/js/heavy_data.js +++ b/django_select2/static/js/heavy_data.js @@ -1,7 +1,7 @@ if (!window['django_select2']) { // This JS file can be included multiple times. So, as not to overwrite previous states, we run this only once. - var django_select2 = { + window.django_select2 = { MULTISEPARATOR: String.fromCharCode(31), // We use this unprintable char as separator, // since this can't be entered by user. get_url_params: function (term, page, context) { @@ -59,14 +59,14 @@ if (!window['django_select2']) { updateText: function ($e) { var val = $e.select2('val'), data = $e.select2('data'), txt = $e.txt(), isMultiple = !!$e.attr('multiple'), diff; - + if (val || val === 0) { // Means value is set. A numerical 0 is also a valid value. if (isMultiple) { if (val.length !== txt.length) { txt = []; jQuery(val).each(function (idx) { var i, value = this, id; - + for (i in data) { id = data [i].id; if (id instanceof String) { @@ -109,12 +109,12 @@ if (!window['django_select2']) { return [val, txt]; } } - + if (res) { txt = []; jQuery(val).each(function (idx) { var i, value = this; - + for (i in res) { if (res[i].id == value) { val[idx] = res[i].id; // To set it to correct data type. diff --git a/django_select2/static/js/heavy_data.min.js b/django_select2/static/js/heavy_data.min.js index 083db23..3d326f9 100644 --- a/django_select2/static/js/heavy_data.min.js +++ b/django_select2/static/js/heavy_data.min.js @@ -1 +1 @@ -if(!window.django_select2){var django_select2={MULTISEPARATOR:String.fromCharCode(31),get_url_params:function(c,e,b){var d=$(this).data("field_id"),a={term:c,page:e,context:b};if(d){a.field_id=d}return a},process_results:function(d,c,b){var a;if(d.err&&d.err.toLowerCase()==="nil"){a={results:d.results};if(b){a.context=b}if(d.more===true||d.more===false){a.more=d.more}}else{a={results:[]}}if(a.results){$(this).data("results",a.results)}else{$(this).removeData("results")}return a},onValChange:function(){django_select2.updateText($(this))},prepareValText:function(d,a,c){var b=[];$(d).each(function(e){b.push({id:this,text:a[e]})});if(c){return b}else{if(b.length>0){return b[0]}else{return null}}},updateText:function(b){var f=b.select2("val"),d=b.select2("data"),a=b.txt(),c=!!b.attr("multiple"),e;if(f||f===0){if(c){if(f.length!==a.length){a=[];$(f).each(function(g){var h,j=this,k;for(h in d){k=d[h].id;if(k instanceof String){k=k.valueOf()}if(k==j){a.push(d[h].text)}}})}}else{a=d.text}b.txt(a)}else{b.txt("")}},getValText:function(b){var g=b.select2("val"),c=b.data("results"),a=b.txt(),e=!!b.attr("multiple"),d,h=b.attr("id");if(g||g===0){if(!e){g=[g];if(a||a===0){a=[a]}}if(a===0||(a&&g.length===a.length)){return[g,a]}d=b.data("userGetValText");if(d){a=d(b,g,e);if(a||a===0){return[g,a]}}if(c){a=[];$(g).each(function(f){var j,k=this;for(j in c){if(c[j].id==k){g[f]=c[j].id;a.push(c[j].text)}}});if(a||a===0){return[g,a]}}}return null},onInit:function(b,f){b=$(b);var d=b.attr("id"),a=null,c=b.select2("val");if(!c&&c!==0){c=b.data("initVal")}if(c||c===0){a=django_select2.getValText(b);if(a&&a[0]){a=django_select2.prepareValText(a[0],a[1],!!b.attr("multiple"))}}if(!a){b.val(null)}f(a);django_select2.updateText(b)},createSearchChoice:function(a,b){if(!b||$(b).filter(function(){return this.text.localeCompare(a)===0}).length===0){return{id:a,text:a}}},onMultipleHiddenChange:function(){var b=$(this),d=b.data("valContainer"),a=b.data("name"),c=b.val();d.empty();if(c){c=c.split(django_select2.MULTISEPARATOR);$(c).each(function(){var e=$('').appendTo(d);e.attr("name",a);e.val(this)})}},initMultipleHidden:function(a){var b;a.data("name",a.attr("name"));a.attr("name","");b=$("
").insertAfter(a).css({display:"none"});a.data("valContainer",b);a.change(django_select2.onMultipleHiddenChange);if(a.val()){a.change()}},convertArrToStr:function(a){return a.join(django_select2.MULTISEPARATOR)},runInContextHelper:function(a,b){return function(){var c=Array.prototype.slice.call(arguments,0);return a.apply($("#"+b).get(0),c)}},logErr:function(){if(console&&console.error){args=Array.prototype.slice.call(arguments);console.error.apply(console,args)}}};(function(b){if(b){for(var a in django_select2){var c=django_select2[a];if(typeof(c)=="function"){django_select2[a]=(function(d,e){return function(){console.log("Function "+d+" called for object: ",this);return e.apply(this,arguments)}}(a,c))}}}}(false));(function(a){a.fn.txt=function(b){if(typeof(b)!=="undefined"){if(b){if(b instanceof Array){if(this.attr("multiple")){b=django_select2.convertArrToStr(b)}else{b=b[0]}}this.attr("txt",b)}else{this.removeAttr("txt")}return this}else{b=this.attr("txt");if(this.attr("multiple")){if(b){b=b.split(django_select2.MULTISEPARATOR)}else{b=[]}}return b}}})(jQuery)}; \ No newline at end of file +if(!window.django_select2){window.django_select2={MULTISEPARATOR:String.fromCharCode(31),get_url_params:function(c,e,b){var d=$(this).data("field_id"),a={term:c,page:e,context:b};if(d){a.field_id=d}return a},process_results:function(d,c,b){var a;if(d.err&&d.err.toLowerCase()==="nil"){a={results:d.results};if(b){a.context=b}if(d.more===true||d.more===false){a.more=d.more}}else{a={results:[]}}if(a.results){$(this).data("results",a.results)}else{$(this).removeData("results")}return a},onValChange:function(){django_select2.updateText($(this))},prepareValText:function(d,a,c){var b=[];$(d).each(function(e){b.push({id:this,text:a[e]})});if(c){return b}else{if(b.length>0){return b[0]}else{return null}}},updateText:function(b){var f=b.select2("val"),d=b.select2("data"),a=b.txt(),c=!!b.attr("multiple"),e;if(f||f===0){if(c){if(f.length!==a.length){a=[];$(f).each(function(g){var h,j=this,k;for(h in d){k=d[h].id;if(k instanceof String){k=k.valueOf()}if(k==j){a.push(d[h].text)}}})}}else{a=d.text}b.txt(a)}else{b.txt("")}},getValText:function(b){var g=b.select2("val"),c=b.data("results"),a=b.txt(),e=!!b.attr("multiple"),d,h=b.attr("id");if(g||g===0){if(!e){g=[g];if(a||a===0){a=[a]}}if(a===0||(a&&g.length===a.length)){return[g,a]}d=b.data("userGetValText");if(d){a=d(b,g,e);if(a||a===0){return[g,a]}}if(c){a=[];$(g).each(function(f){var j,k=this;for(j in c){if(c[j].id==k){g[f]=c[j].id;a.push(c[j].text)}}});if(a||a===0){return[g,a]}}}return null},onInit:function(b,f){b=$(b);var d=b.attr("id"),a=null,c=b.select2("val");if(!c&&c!==0){c=b.data("initVal")}if(c||c===0){a=django_select2.getValText(b);if(a&&a[0]){a=django_select2.prepareValText(a[0],a[1],!!b.attr("multiple"))}}if(!a){b.val(null)}f(a);django_select2.updateText(b)},createSearchChoice:function(a,b){if(!b||$(b).filter(function(){return this.text.localeCompare(a)===0}).length===0){return{id:a,text:a}}},onMultipleHiddenChange:function(){var b=$(this),d=b.data("valContainer"),a=b.data("name"),c=b.val();d.empty();if(c){c=c.split(django_select2.MULTISEPARATOR);$(c).each(function(){var e=$('').appendTo(d);e.attr("name",a);e.val(this)})}},initMultipleHidden:function(a){var b;a.data("name",a.attr("name"));a.attr("name","");b=$("
").insertAfter(a).css({display:"none"});a.data("valContainer",b);a.change(django_select2.onMultipleHiddenChange);if(a.val()){a.change()}},convertArrToStr:function(a){return a.join(django_select2.MULTISEPARATOR)},runInContextHelper:function(a,b){return function(){var c=Array.prototype.slice.call(arguments,0);return a.apply($("#"+b).get(0),c)}},logErr:function(){if(console&&console.error){args=Array.prototype.slice.call(arguments);console.error.apply(console,args)}}};(function(b){if(b){for(var a in django_select2){var c=django_select2[a];if(typeof(c)=="function"){django_select2[a]=(function(d,e){return function(){console.log("Function "+d+" called for object: ",this);return e.apply(this,arguments)}}(a,c))}}}}(false));(function(a){a.fn.txt=function(b){if(typeof(b)!=="undefined"){if(b){if(b instanceof Array){if(this.attr("multiple")){b=django_select2.convertArrToStr(b)}else{b=b[0]}}this.attr("txt",b)}else{this.removeAttr("txt")}return this}else{b=this.attr("txt");if(this.attr("multiple")){if(b){b=b.split(django_select2.MULTISEPARATOR)}else{b=[]}}return b}}})(jQuery)}; From a9df6ad39054612b511d9c3e43459d71ae50371e Mon Sep 17 00:00:00 2001 From: Thomas Schreiber Date: Thu, 11 Sep 2014 20:34:31 +0200 Subject: [PATCH 2/7] convert to unix filetype Conflicts: django_select2/widgets.py --- django_select2/widgets.py | 1272 ++++++++++++++++++------------------- 1 file changed, 634 insertions(+), 638 deletions(-) diff --git a/django_select2/widgets.py b/django_select2/widgets.py index 75a7e5e..ea0d717 100644 --- a/django_select2/widgets.py +++ b/django_select2/widgets.py @@ -1,638 +1,634 @@ -""" -Contains all the Django widgets for Select2. -""" - -import logging -from itertools import chain -import util - -from django import forms -from django.core.validators import EMPTY_VALUES -from django.utils.encoding import force_unicode -from django.utils.safestring import mark_safe -from django.core.urlresolvers import reverse -from django.utils.datastructures import MultiValueDict, MergeDict - -from .util import render_js_script, convert_to_js_string_arr, JSVar, JSFunction, JSFunctionInContext, \ - convert_dict_to_js_map, convert_to_js_arr - -from . import __RENDER_SELECT2_STATICS as RENDER_SELECT2_STATICS - -logger = logging.getLogger(__name__) - - -def get_select2_js_libs(): - from django.conf import settings - if settings.configured and settings.DEBUG: - return ('js/select2.js', ) - else: - return ('js/select2.min.js', ) - -def get_select2_heavy_js_libs(): - libs = get_select2_js_libs() - - from django.conf import settings - if settings.configured and settings.DEBUG: - return libs + ('js/heavy_data.js', ) - else: - return libs + ('js/heavy_data.min.js', ) - -def get_select2_css_libs(light=False): - from django.conf import settings - from . import __BOOTSTRAP - if __BOOTSTRAP: - if settings.configured and settings.DEBUG: - if light: - return ('css/select2.css', 'css/select2-bootstrap.css') - else: - return ('css/select2.css', 'css/extra.css', 'css/select2-bootstrap.css') - else: - if light: - return ('css/select2-bootstrapped.min.css',) - else: - return ('css/all-bootstrapped.min.css',) - else: - if settings.configured and settings.DEBUG: - if light: - return ('css/select2.css',) - else: - return ('css/select2.css', 'css/extra.css') - else: - if light: - return ('css/select2.min.css',) - else: - return ('css/all.min.css',) - -### Light mixin and widgets ### - -class Select2Mixin(object): - """ - The base mixin of all Select2 widgets. - - This mixin is responsible for rendering the necessary JavaScript and CSS codes which turns normal `` - 'closeOnSelect': False, - } - """ - The options listed here are rendered as JS map and passed to Select2 JS code. - Complete description of these options are available in Select2_ JS' site. - - .. _Select2: http://ivaynberg.github.com/select2/#documentation. - """ - - def __init__(self, **kwargs): - """ - Constructor of the class. - - The following additional kwarg is allowed:- - - :param select2_options: This is similar to standard Django way to pass extra attributes to widgets. - This is meant to override values of existing :py:attr:`.options`. - - Example:: - - class MyForm(ModelForm): - class Meta: - model = MyModel - widgets = { - 'name': Select2WidgetName(select2_options={ - 'minimumResultsForSearch': 10, - 'closeOnSelect': True, - }) - } - - .. tip:: You cannot introduce new options using this. For that you should sub-class and override - :py:meth:`.init_options`. The reason for this is, few options are not compatible with each other - or are not applicable in some scenarios. For example, when Select2 is attached to ```` list. Otherwise the field will be rendered without a placeholder and the clear feature - will stay disabled. - - - :type select2_options: :py:obj:`dict` or None - - """ - # Making an instance specific copy - self.options = dict(self.options) - select2_options = kwargs.pop('select2_options', None) - if select2_options: - for name, value in select2_options.items(): - self.options[name] = value - self.init_options() - - super(Select2Mixin, self).__init__(**kwargs) - - def init_options(self): - """ - Sub-classes can use this to suppress or override options passed to Select2 JS library. - - Example:: - - def init_options(self): - self.options['createSearchChoice'] = JSFunction('Your_js_function') - - In the above example we are setting ``Your_js_function`` as Select2's ``createSearchChoice`` - function. - - .. tip:: If you want to run ``Your_js_function`` in the context of the Select2 DOM element, - i.e. ``this`` inside your JS function should point to the component instead of ``window``, then - use :py:class:`~.util.JSFunctionInContext` instead of :py:class:`~.util.JSFunction`. - """ - pass - - def set_placeholder(self, val): - """ - Placeholder is a value which Select2 JS library shows when nothing is selected. This should be string. - - :return: None - """ - self.options['placeholder'] = val - - def get_options(self): - """ - :return: Dictionary of options to be passed to Select2 JS. - - :rtype: :py:obj:`dict` - """ - options = dict(self.options) - if options.get('allowClear', None) is not None: - options['allowClear'] = not self.is_required - return options - - def render_select2_options_code(self, options, id_): - """ - Renders options for Select2 JS. - - :return: The rendered JS code. - :rtype: :py:obj:`unicode` - """ - return convert_dict_to_js_map(options, id_) - - def render_js_code(self, id_, *args): - """ - Renders the `` - - :rtype: :py:obj:`unicode` - """ - return u""" - """ % inner_code - - def extract_some_key_val(dct, keys): """ Gets a sub-set of a :py:obj:`dict`. @@ -86,110 +30,6 @@ def extract_some_key_val(dct, keys): return edct -def convert_to_js_str(val): - val = force_unicode(val).replace('\'', '\\\'') - return u"'%s'" % val - -def convert_py_to_js_data(val, id_): - """ - Converts Python data type to JS data type. - - Practically what this means is, convert ``False`` to ``false``, ``True`` to ``true`` and so on. - It also takes care of the conversion of :py:class:`.JSVar`, :py:class:`.JSFunction` - and :py:class:`.JSFunctionInContext`. It takes care of recursively converting lists and dictionaries - too. - - :param val: The Python data to convert. - :type val: Any - - :param id_: The DOM id of the element in which context :py:class:`.JSFunctionInContext` functions - should run. (This is not needed if ``val`` contains no :py:class:`.JSFunctionInContext`) - :type id_: :py:obj:`str` - - :rtype: :py:obj:`unicode` - """ - if type(val) == types.BooleanType: - return u'true' if val else u'false' - elif type(val) in [types.IntType, types.LongType, types.FloatType]: - return force_unicode(val) - elif isinstance(val, JSFunctionInContext): - return u"django_select2.runInContextHelper(%s, '%s')" % (val, id_) - elif isinstance(val, JSVar): - return val # No quotes here - elif isinstance(val, dict): - return convert_dict_to_js_map(val, id_) - elif isinstance(val, list): - return convert_to_js_arr(val, id_) - else: - return convert_to_js_str(val) - - -def convert_dict_to_js_map(dct, id_): - """ - Converts a Python dictionary to JS map. - - :param dct: The Python dictionary to convert. - :type dct: :py:obj:`dict` - - :param id_: The DOM id of the element in which context :py:class:`.JSFunctionInContext` functions - should run. (This is not needed if ``dct`` contains no :py:class:`.JSFunctionInContext`) - :type id_: :py:obj:`str` - - :rtype: :py:obj:`unicode` - """ - out = u'{' - is_first = True - for name in dct: - if not is_first: - out += u", " - else: - is_first = False - - out += u"%s: " % convert_to_js_str(name) - out += convert_py_to_js_data(dct[name], id_) - - return out + u'}' - - -def convert_to_js_arr(lst, id_): - """ - Converts a Python list (or any iterable) to JS array. - - :param lst: The Python iterable to convert. - :type lst: :py:obj:`list` or Any iterable - - :param id_: The DOM id of the element in which context :py:class:`.JSFunctionInContext` functions - should run. (This is not needed if ``lst`` contains no :py:class:`.JSFunctionInContext`) - :type id_: :py:obj:`str` - - :rtype: :py:obj:`unicode` - """ - out = u'[' - is_first = True - for val in lst: - if not is_first: - out += u", " - else: - is_first = False - - out += convert_py_to_js_data(val, id_) - - return out + u']' - - -def convert_to_js_string_arr(lst): - """ - Converts a Python list (or any iterable) of strings to JS array. - - :py:func:`convert_to_js_arr` can always be used instead of this. However, since it - knows that it only contains strings, it cuts down on unnecessary computations. - - :rtype: :py:obj:`unicode` - """ - lst = [convert_to_js_str(l) for l in lst] - return u"[%s]" % (",".join(lst)) - - ### Auto view helper utils ### from . import __ENABLE_MULTI_PROCESS_SUPPORT as ENABLE_MULTI_PROCESS_SUPPORT, \ diff --git a/django_select2/widgets.py b/django_select2/widgets.py index ea0d717..52dea5b 100644 --- a/django_select2/widgets.py +++ b/django_select2/widgets.py @@ -1,9 +1,10 @@ """ Contains all the Django widgets for Select2. """ - +import json import logging from itertools import chain +import re import util from django import forms @@ -13,9 +14,6 @@ from django.utils.safestring import mark_safe from django.core.urlresolvers import reverse from django.utils.datastructures import MultiValueDict, MergeDict -from .util import render_js_script, convert_to_js_string_arr, JSVar, JSFunction, JSFunctionInContext, \ - convert_dict_to_js_map, convert_to_js_arr - from . import __RENDER_SELECT2_STATICS as RENDER_SELECT2_STATICS logger = logging.getLogger(__name__) @@ -154,14 +152,10 @@ class Select2Mixin(object): Example:: def init_options(self): - self.options['createSearchChoice'] = JSFunction('Your_js_function') + self.options['createSearchChoice'] = 'Your_js_function' In the above example we are setting ``Your_js_function`` as Select2's ``createSearchChoice`` function. - - .. tip:: If you want to run ``Your_js_function`` in the context of the Select2 DOM element, - i.e. ``this`` inside your JS function should point to the component instead of ``window``, then - use :py:class:`~.util.JSFunctionInContext` instead of :py:class:`~.util.JSFunction`. """ pass @@ -184,15 +178,6 @@ class Select2Mixin(object): options['allowClear'] = not self.is_required return options - def render_select2_options_code(self, options, id_): - """ - Renders options for Select2 JS. - - :return: The rendered JS code. - :rtype: :py:obj:`unicode` - """ - return convert_dict_to_js_map(options, id_) - def render_js_code(self, id_, *args): """ Renders the `` + + :rtype: :py:obj:`unicode` + """ + return u""" + + """ % inner_code + def render_inner_js_code(self, id_, *args): """ Renders all the JS code required for this widget. @@ -211,10 +216,10 @@ class Select2Mixin(object): :return: The rendered JS code which will be later enclosed inside ``