diff options
| author | Igor Minar | 2012-03-08 15:00:38 -0800 | 
|---|---|---|
| committer | Igor Minar | 2012-03-08 22:29:34 -0800 | 
| commit | f54db2ccda399f2677e4ca7588018cb31545a2b4 (patch) | |
| tree | 29ef2f8f834544c84cea1a82e3d08679358fb992 /src/widget/select.js | |
| parent | dd7b0f56fcd9785f7fccae8c4f088a8f3e7b125e (diff) | |
| download | angular.js-f54db2ccda399f2677e4ca7588018cb31545a2b4.tar.bz2 | |
chore(directives,widgets): reorg the code under directive/ dir
Diffstat (limited to 'src/widget/select.js')
| -rw-r--r-- | src/widget/select.js | 444 | 
1 files changed, 0 insertions, 444 deletions
| diff --git a/src/widget/select.js b/src/widget/select.js deleted file mode 100644 index 5ed1367f..00000000 --- a/src/widget/select.js +++ /dev/null @@ -1,444 +0,0 @@ -'use strict'; - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.select - * - * @description - * HTML `SELECT` element with angular data-binding. - * - * # `ng:options` - * - * Optionally `ng:options` attribute can be used to dynamically generate a list of `<option>` - * elements for a `<select>` element using an array or an object obtained by evaluating the - * `ng:options` expression. - *˝˝ - * When an item in the select menu is select, the value of array element or object property - * represented by the selected option will be bound to the model identified by the `ng:model` attribute - * of the parent select element. - * - * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can - * be nested into the `<select>` element. This element will then represent `null` or "not selected" - * option. See example below for demonstration. - * - * Note: `ng:options` provides iterator facility for `<option>` element which must be used instead - * of {@link angular.module.ng.$compileProvider.directive.ng:repeat ng:repeat}. `ng:repeat` is not suitable for use with - * `<option>` element because of the following reasons: - * - *   * value attribute of the option element that we need to bind to requires a string, but the - *     source of data for the iteration might be in a form of array containing objects instead of - *     strings - *   * {@link angular.module.ng.$compileProvider.directive.ng:repeat ng:repeat} unrolls after the select binds causing - *     incorect rendering on most browsers. - *   * binding to a value not in list confuses most browsers. - * - * @param {string} name assignable expression to data-bind to. - * @param {string=} required The widget is considered valid only if value is entered. - * @param {comprehension_expression=} ng:options in one of the following forms: - * - *   * for array data sources: - *     * `label` **`for`** `value` **`in`** `array` - *     * `select` **`as`** `label` **`for`** `value` **`in`** `array` - *     * `label`  **`group by`** `group` **`for`** `value` **`in`** `array` - *     * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array` - *   * for object data sources: - *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object` - *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object` - *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object` - *     * `select` **`as`** `label` **`group by`** `group` - *         **`for` `(`**`key`**`,`** `value`**`) in`** `object` - * - * Where: - * - *   * `array` / `object`: an expression which evaluates to an array / object to iterate over. - *   * `value`: local variable which will refer to each item in the `array` or each property value - *      of `object` during iteration. - *   * `key`: local variable which will refer to a property name in `object` during iteration. - *   * `label`: The result of this expression will be the label for `<option>` element. The - *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`). - *   * `select`: The result of this expression will be bound to the model of the parent `<select>` - *      element. If not specified, `select` expression will default to `value`. - *   * `group`: The result of this expression will be used to group options using the `<optgroup>` - *      DOM element. - * - * @example -    <doc:example> -      <doc:source> -        <script> -        function MyCntrl($scope) { -          $scope.colors = [ -            {name:'black', shade:'dark'}, -            {name:'white', shade:'light'}, -            {name:'red', shade:'dark'}, -            {name:'blue', shade:'dark'}, -            {name:'yellow', shade:'light'} -          ]; -          $scope.color = $scope.colors[2]; // red -        } -        </script> -        <div ng:controller="MyCntrl"> -          <ul> -            <li ng:repeat="color in colors"> -              Name: <input ng:model="color.name"> -              [<a href ng:click="colors.$remove(color)">X</a>] -            </li> -            <li> -              [<a href ng:click="colors.push({})">add</a>] -            </li> -          </ul> -          <hr/> -          Color (null not allowed): -          <select ng:model="color" ng:options="c.name for c in colors"></select><br> - -          Color (null allowed): -          <div  class="nullable"> -            <select ng:model="color" ng:options="c.name for c in colors"> -              <option value="">-- chose color --</option> -            </select> -          </div><br/> - -          Color grouped by shade: -          <select ng:model="color" ng:options="c.name group by c.shade for c in colors"> -          </select><br/> - - -          Select <a href ng:click="color={name:'not in list'}">bogus</a>.<br> -          <hr/> -          Currently selected: {{ {selected_color:color}  }} -          <div style="border:solid 1px black; height:20px" -               ng:style="{'background-color':color.name}"> -          </div> -        </div> -      </doc:source> -      <doc:scenario> -         it('should check ng:options', function() { -           expect(binding('{selected_color:color}')).toMatch('red'); -           select('color').option('0'); -           expect(binding('{selected_color:color}')).toMatch('black'); -           using('.nullable').select('color').option(''); -           expect(binding('{selected_color:color}')).toMatch('null'); -         }); -      </doc:scenario> -    </doc:example> - */ - -var ngOptionsDirective = valueFn({ terminal: true }); -var selectDirective = ['$compile', '$parse', function($compile,   $parse) { -                         //00001111100000000000222200000000000000000000003333000000000000044444444444444444000000000555555555555555550000000666666666666666660000000000000007777 -  var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/; - -  return { -    restrict: 'E', -    require: '?ngModel', -    link: function(scope, element, attr, ctrl) { -      if (!ctrl) return; - -      var multiple = attr.multiple, -          optionsExp = attr.ngOptions; - -      // required validator -      if (multiple && (attr.required || attr.ngRequired)) { -        var requiredValidator = function(value) { -          ctrl.setValidity('REQUIRED', !attr.required || (value && value.length)); -          return value; -        }; - -        ctrl.parsers.push(requiredValidator); -        ctrl.formatters.unshift(requiredValidator); - -        attr.$observe('required', function() { -          requiredValidator(ctrl.viewValue); -        }); -      } - -      if (optionsExp) Options(scope, element, ctrl); -      else if (multiple) Multiple(scope, element, ctrl); -      else Single(scope, element, ctrl); - - -      //////////////////////////// - - - -      function Single(scope, selectElement, ctrl) { -        ctrl.render = function() { -          selectElement.val(ctrl.viewValue); -        }; - -        selectElement.bind('change', function() { -          scope.$apply(function() { -            ctrl.touch(); -            ctrl.read(selectElement.val()); -          }); -        }); -      } - -      function Multiple(scope, selectElement, ctrl) { -        ctrl.render = function() { -          var items = new HashMap(ctrl.viewValue); -          forEach(selectElement.children(), function(option) { -            option.selected = isDefined(items.get(option.value)); -          }); -        }; - -        selectElement.bind('change', function() { -          scope.$apply(function() { -            var array = []; -            forEach(selectElement.children(), function(option) { -              if (option.selected) { -                array.push(option.value); -              } -            }); -            ctrl.touch(); -            ctrl.read(array); -          }); -        }); -      } - -      function Options(scope, selectElement, ctrl) { -        var match; - -        if (! (match = optionsExp.match(NG_OPTIONS_REGEXP))) { -          throw Error( -            "Expected ng:options in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" + -            " but got '" + optionsExp + "'."); -        } - -        var displayFn = $parse(match[2] || match[1]), -            valueName = match[4] || match[6], -            keyName = match[5], -            groupByFn = $parse(match[3] || ''), -            valueFn = $parse(match[2] ? match[1] : valueName), -            valuesFn = $parse(match[7]), -            // we can't just jqLite('<option>') since jqLite is not smart enough -            // to create it in <select> and IE barfs otherwise. -            optionTemplate = jqLite(document.createElement('option')), -            optGroupTemplate = jqLite(document.createElement('optgroup')), -            nullOption = false, // if false then user will not be able to select it -            // This is an array of array of existing option groups in DOM. We try to reuse these if possible -            // optionGroupsCache[0] is the options with no option group -            // optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element -            optionGroupsCache = [[{element: selectElement, label:''}]]; - -        // find existing special options -        forEach(selectElement.children(), function(option) { -          if (option.value == '') { -            // developer declared null option, so user should be able to select it -            nullOption = jqLite(option).remove(); -            // compile the element since there might be bindings in it -            $compile(nullOption)(scope); -          } -        }); -        selectElement.html(''); // clear contents - -        selectElement.bind('change', function() { -          scope.$apply(function() { -            var optionGroup, -                collection = valuesFn(scope) || [], -                locals = {}, -                key, value, optionElement, index, groupIndex, length, groupLength; - -            if (multiple) { -              value = []; -              for (groupIndex = 0, groupLength = optionGroupsCache.length; -              groupIndex < groupLength; -              groupIndex++) { -                // list of options for that group. (first item has the parent) -                optionGroup = optionGroupsCache[groupIndex]; - -                for(index = 1, length = optionGroup.length; index < length; index++) { -                  if ((optionElement = optionGroup[index].element)[0].selected) { -                    key = optionElement.val(); -                    if (keyName) locals[keyName] = key; -                    locals[valueName] = collection[key]; -                    value.push(valueFn(scope, locals)); -                  } -                } -              } -            } else { -              key = selectElement.val(); -              if (key == '?') { -                value = undefined; -              } else if (key == ''){ -                value = null; -              } else { -                locals[valueName] = collection[key]; -                if (keyName) locals[keyName] = key; -                value = valueFn(scope, locals); -              } -            } -            ctrl.touch(); - -            if (ctrl.viewValue !== value) { -              ctrl.read(value); -            } -          }); -        }); - -        ctrl.render = render; - -        // TODO(vojta): can't we optimize this ? -        scope.$watch(render); - -        function render() { -          var optionGroups = {'':[]}, // Temporary location for the option groups before we render them -              optionGroupNames = [''], -              optionGroupName, -              optionGroup, -              option, -              existingParent, existingOptions, existingOption, -              modelValue = ctrl.modelValue, -              values = valuesFn(scope) || [], -              keys = keyName ? sortedKeys(values) : values, -              groupLength, length, -              groupIndex, index, -              locals = {}, -              selected, -              selectedSet = false, // nothing is selected yet -              lastElement, -              element; - -          if (multiple) { -            selectedSet = new HashMap(modelValue); -          } else if (modelValue === null || nullOption) { -            // if we are not multiselect, and we are null then we have to add the nullOption -            optionGroups[''].push({selected:modelValue === null, id:'', label:''}); -            selectedSet = true; -          } - -          // We now build up the list of options we need (we merge later) -          for (index = 0; length = keys.length, index < length; index++) { -               locals[valueName] = values[keyName ? locals[keyName]=keys[index]:index]; -               optionGroupName = groupByFn(scope, locals) || ''; -            if (!(optionGroup = optionGroups[optionGroupName])) { -              optionGroup = optionGroups[optionGroupName] = []; -              optionGroupNames.push(optionGroupName); -            } -            if (multiple) { -              selected = selectedSet.remove(valueFn(scope, locals)) != undefined; -            } else { -              selected = modelValue === valueFn(scope, locals); -              selectedSet = selectedSet || selected; // see if at least one item is selected -            } -            optionGroup.push({ -              id: keyName ? keys[index] : index,   // either the index into array or key from object -              label: displayFn(scope, locals) || '', // what will be seen by the user -              selected: selected                   // determine if we should be selected -            }); -          } -          if (!multiple && !selectedSet) { -            // nothing was selected, we have to insert the undefined item -            optionGroups[''].unshift({id:'?', label:'', selected:true}); -          } - -          // Now we need to update the list of DOM nodes to match the optionGroups we computed above -          for (groupIndex = 0, groupLength = optionGroupNames.length; -               groupIndex < groupLength; -               groupIndex++) { -            // current option group name or '' if no group -            optionGroupName = optionGroupNames[groupIndex]; - -            // list of options for that group. (first item has the parent) -            optionGroup = optionGroups[optionGroupName]; - -            if (optionGroupsCache.length <= groupIndex) { -              // we need to grow the optionGroups -              existingParent = { -                element: optGroupTemplate.clone().attr('label', optionGroupName), -                label: optionGroup.label -              }; -              existingOptions = [existingParent]; -              optionGroupsCache.push(existingOptions); -              selectElement.append(existingParent.element); -            } else { -              existingOptions = optionGroupsCache[groupIndex]; -              existingParent = existingOptions[0];  // either SELECT (no group) or OPTGROUP element - -              // update the OPTGROUP label if not the same. -              if (existingParent.label != optionGroupName) { -                existingParent.element.attr('label', existingParent.label = optionGroupName); -              } -            } - -            lastElement = null;  // start at the begining -            for(index = 0, length = optionGroup.length; index < length; index++) { -              option = optionGroup[index]; -              if ((existingOption = existingOptions[index+1])) { -                // reuse elements -                lastElement = existingOption.element; -                if (existingOption.label !== option.label) { -                  lastElement.text(existingOption.label = option.label); -                } -                if (existingOption.id !== option.id) { -                  lastElement.val(existingOption.id = option.id); -                } -                if (existingOption.element.selected !== option.selected) { -                  lastElement.prop('selected', (existingOption.selected = option.selected)); -                } -              } else { -                // grow elements - -                // if it's a null option -                if (option.id === '' && nullOption) { -                  // put back the pre-compiled element -                  element = nullOption; -                } else { -                  // jQuery(v1.4.2) Bug: We should be able to chain the method calls, but -                  // in this version of jQuery on some browser the .text() returns a string -                  // rather then the element. -                  (element = optionTemplate.clone()) -                      .val(option.id) -                      .attr('selected', option.selected) -                      .text(option.label); -                } - -                existingOptions.push(existingOption = { -                    element: element, -                    label: option.label, -                    id: option.id, -                    selected: option.selected -                }); -                if (lastElement) { -                  lastElement.after(element); -                } else { -                  existingParent.element.append(element); -                } -                lastElement = element; -              } -            } -            // remove any excessive OPTIONs in a group -            index++; // increment since the existingOptions[0] is parent element not OPTION -            while(existingOptions.length > index) { -              existingOptions.pop().element.remove(); -            } -          } -          // remove any excessive OPTGROUPs from select -          while(optionGroupsCache.length > groupIndex) { -            optionGroupsCache.pop()[0].element.remove(); -          } -        }; -      } -    } -  } -}]; - -var optionDirective = ['$interpolate', function($interpolate) { -  return { -    restrict: 'E', -    priority: 100, -    compile: function(element, attr) { -      if (isUndefined(attr.value)) { -        var interpolateFn = $interpolate(element.text(), true); -        if (interpolateFn) { -          return function (scope, element, attr) { -            scope.$watch(interpolateFn, function(value) { -              attr.$set('value', value); -            }); -          } -        } else { -          attr.$set('value', element.text()); -        } -      } -    } -  } -}]; | 
