arnaucube
arnaucube/socketioMEANseedPublic Self-hosted repository
socketioMEANseed/www/bower_components/angular-material/modules/js/list/list.js
www/bower_components/angular-material/modules/js/list/list.jsRaw
  1/*!
  2 * Angular Material Design
  3 * https://github.com/angular/material
  4 * @license MIT
  5 * v1.1.3
  6 */
  7(function( window, angular, undefined ){
  8"use strict";
  9
 10/**
 11 * @ngdoc module
 12 * @name material.components.list
 13 * @description
 14 * List module
 15 */
 16MdListController['$inject'] = ["$scope", "$element", "$mdListInkRipple"];
 17mdListDirective['$inject'] = ["$mdTheming"];
 18mdListItemDirective['$inject'] = ["$mdAria", "$mdConstant", "$mdUtil", "$timeout"];
 19angular.module('material.components.list', [
 20  'material.core'
 21])
 22  .controller('MdListController', MdListController)
 23  .directive('mdList', mdListDirective)
 24  .directive('mdListItem', mdListItemDirective);
 25
 26/**
 27 * @ngdoc directive
 28 * @name mdList
 29 * @module material.components.list
 30 *
 31 * @restrict E
 32 *
 33 * @description
 34 * The `<md-list>` directive is a list container for 1..n `<md-list-item>` tags.
 35 *
 36 * @usage
 37 * <hljs lang="html">
 38 * <md-list>
 39 *   <md-list-item class="md-2-line" ng-repeat="item in todos">
 40 *     <md-checkbox ng-model="item.done"></md-checkbox>
 41 *     <div class="md-list-item-text">
 42 *       <h3>{{item.title}}</h3>
 43 *       <p>{{item.description}}</p>
 44 *     </div>
 45 *   </md-list-item>
 46 * </md-list>
 47 * </hljs>
 48 */
 49
 50function mdListDirective($mdTheming) {
 51  return {
 52    restrict: 'E',
 53    compile: function(tEl) {
 54      tEl[0].setAttribute('role', 'list');
 55      return $mdTheming;
 56    }
 57  };
 58}
 59/**
 60 * @ngdoc directive
 61 * @name mdListItem
 62 * @module material.components.list
 63 *
 64 * @restrict E
 65 *
 66 * @description
 67 * A `md-list-item` element can be used to represent some information in a row.<br/>
 68 *
 69 * @usage
 70 * ### Single Row Item
 71 * <hljs lang="html">
 72 *   <md-list-item>
 73 *     <span>Single Row Item</span>
 74 *   </md-list-item>
 75 * </hljs>
 76 *
 77 * ### Multiple Lines
 78 * By using the following markup, you will be able to have two lines inside of one `md-list-item`.
 79 *
 80 * <hljs lang="html">
 81 *   <md-list-item class="md-2-line">
 82 *     <div class="md-list-item-text" layout="column">
 83 *       <p>First Line</p>
 84 *       <p>Second Line</p>
 85 *     </div>
 86 *   </md-list-item>
 87 * </hljs>
 88 *
 89 * It is also possible to have three lines inside of one list item.
 90 *
 91 * <hljs lang="html">
 92 *   <md-list-item class="md-3-line">
 93 *     <div class="md-list-item-text" layout="column">
 94 *       <p>First Line</p>
 95 *       <p>Second Line</p>
 96 *       <p>Third Line</p>
 97 *     </div>
 98 *   </md-list-item>
 99 * </hljs>
100 *
101 * ### Secondary Items
102 * Secondary items are elements which will be aligned at the end of the `md-list-item`.
103 *
104 * <hljs lang="html">
105 *   <md-list-item>
106 *     <span>Single Row Item</span>
107 *     <md-button class="md-secondary">
108 *       Secondary Button
109 *     </md-button>
110 *   </md-list-item>
111 * </hljs>
112 *
113 * It also possible to have multiple secondary items inside of one `md-list-item`.
114 *
115 * <hljs lang="html">
116 *   <md-list-item>
117 *     <span>Single Row Item</span>
118 *     <md-button class="md-secondary">First Button</md-button>
119 *     <md-button class="md-secondary">Second Button</md-button>
120 *   </md-list-item>
121 * </hljs>
122 *
123 * ### Proxy Item
124 * Proxies are elements, which will execute their specific action on click<br/>
125 * Currently supported proxy items are
126 * - `md-checkbox` (Toggle)
127 * - `md-switch` (Toggle)
128 * - `md-menu` (Open)
129 *
130 * This means, when using a supported proxy item inside of `md-list-item`, the list item will
131 * automatically become clickable and executes the associated action of the proxy element on click.
132 *
133 * It is possible to disable this behavior by applying the `md-no-proxy` class to the list item.
134 *
135 * <hljs lang="html">
136 *   <md-list-item class="md-no-proxy">
137 *     <span>No Proxy List</span>
138 *     <md-checkbox class="md-secondary"></md-checkbox>
139 *   </md-list-item>
140 * </hljs>
141 *
142 * Here are a few examples of proxy elements inside of a list item.
143 *
144 * <hljs lang="html">
145 *   <md-list-item>
146 *     <span>First Line</span>
147 *     <md-checkbox class="md-secondary"></md-checkbox>
148 *   </md-list-item>
149 * </hljs>
150 *
151 * The `md-checkbox` element will be automatically detected as a proxy element and will toggle on click.
152 *
153 * <hljs lang="html">
154 *   <md-list-item>
155 *     <span>First Line</span>
156 *     <md-switch class="md-secondary"></md-switch>
157 *   </md-list-item>
158 * </hljs>
159 *
160 * The recognized `md-switch` will toggle its state, when the user clicks on the `md-list-item`.
161 *
162 * It is also possible to have a `md-menu` inside of a `md-list-item`.
163 * <hljs lang="html">
164 *   <md-list-item>
165 *     <p>Click anywhere to fire the secondary action</p>
166 *     <md-menu class="md-secondary">
167 *       <md-button class="md-icon-button">
168 *         <md-icon md-svg-icon="communication:message"></md-icon>
169 *       </md-button>
170 *       <md-menu-content width="4">
171 *         <md-menu-item>
172 *           <md-button>
173 *             Redial
174 *           </md-button>
175 *         </md-menu-item>
176 *         <md-menu-item>
177 *           <md-button>
178 *             Check voicemail
179 *           </md-button>
180 *         </md-menu-item>
181 *         <md-menu-divider></md-menu-divider>
182 *         <md-menu-item>
183 *           <md-button>
184 *             Notifications
185 *           </md-button>
186 *         </md-menu-item>
187 *       </md-menu-content>
188 *     </md-menu>
189 *   </md-list-item>
190 * </hljs>
191 *
192 * The menu will automatically open, when the users clicks on the `md-list-item`.<br/>
193 *
194 * If the developer didn't specify any position mode on the menu, the `md-list-item` will automatically detect the
195 * position mode and applies it to the `md-menu`.
196 *
197 * ### Avatars
198 * Sometimes you may want to have some avatars inside of the `md-list-item `.<br/>
199 * You are able to create a optimized icon for the list item, by applying the `.md-avatar` class on the `<img>` element.
200 *
201 * <hljs lang="html">
202 *   <md-list-item>
203 *     <img src="my-avatar.png" class="md-avatar">
204 *     <span>Alan Turing</span>
205 * </hljs>
206 *
207 * When using `<md-icon>` for an avatar, you have to use the `.md-avatar-icon` class.
208 * <hljs lang="html">
209 *   <md-list-item>
210 *     <md-icon class="md-avatar-icon" md-svg-icon="avatars:timothy"></md-icon>
211 *     <span>Timothy Kopra</span>
212 *   </md-list-item>
213 * </hljs>
214 *
215 * In cases, you have a `md-list-item`, which doesn't have any avatar,
216 * but you want to align it with the other avatar items, you have to use the `.md-offset` class.
217 *
218 * <hljs lang="html">
219 *   <md-list-item class="md-offset">
220 *     <span>Jon Doe</span>
221 *   </md-list-item>
222 * </hljs>
223 *
224 * ### DOM modification
225 * The `md-list-item` component automatically detects if the list item should be clickable.
226 *
227 * ---
228 * If the `md-list-item` is clickable, we wrap all content inside of a `<div>` and create
229 * an overlaying button, which will will execute the given actions (like `ng-href`, `ng-click`)
230 *
231 * We create an overlaying button, instead of wrapping all content inside of the button,
232 * because otherwise some elements may not be clickable inside of the button.
233 *
234 * ---
235 * When using a secondary item inside of your list item, the `md-list-item` component will automatically create
236 * a secondary container at the end of the `md-list-item`, which contains all secondary items.
237 *
238 * The secondary item container is not static, because otherwise the overflow will not work properly on the
239 * list item.
240 *
241 */
242function mdListItemDirective($mdAria, $mdConstant, $mdUtil, $timeout) {
243  var proxiedTypes = ['md-checkbox', 'md-switch', 'md-menu'];
244  return {
245    restrict: 'E',
246    controller: 'MdListController',
247    compile: function(tEl, tAttrs) {
248
249      // Check for proxy controls (no ng-click on parent, and a control inside)
250      var secondaryItems = tEl[0].querySelectorAll('.md-secondary');
251      var hasProxiedElement;
252      var proxyElement;
253      var itemContainer = tEl;
254
255      tEl[0].setAttribute('role', 'listitem');
256
257      if (tAttrs.ngClick || tAttrs.ngDblclick ||  tAttrs.ngHref || tAttrs.href || tAttrs.uiSref || tAttrs.ngAttrUiSref) {
258        wrapIn('button');
259      } else if (!tEl.hasClass('md-no-proxy')) {
260
261        for (var i = 0, type; type = proxiedTypes[i]; ++i) {
262          if (proxyElement = tEl[0].querySelector(type)) {
263            hasProxiedElement = true;
264            break;
265          }
266        }
267
268        if (hasProxiedElement) {
269          wrapIn('div');
270        } else {
271          tEl.addClass('md-no-proxy');
272        }
273
274      }
275
276      wrapSecondaryItems();
277      setupToggleAria();
278
279      if (hasProxiedElement && proxyElement.nodeName === "MD-MENU") {
280        setupProxiedMenu();
281      }
282
283      function setupToggleAria() {
284        var toggleTypes = ['md-switch', 'md-checkbox'];
285        var toggle;
286
287        for (var i = 0, toggleType; toggleType = toggleTypes[i]; ++i) {
288          if (toggle = tEl.find(toggleType)[0]) {
289            if (!toggle.hasAttribute('aria-label')) {
290              var p = tEl.find('p')[0];
291              if (!p) return;
292              toggle.setAttribute('aria-label', 'Toggle ' + p.textContent);
293            }
294          }
295        }
296      }
297
298      function setupProxiedMenu() {
299        var menuEl = angular.element(proxyElement);
300
301        var isEndAligned = menuEl.parent().hasClass('md-secondary-container') ||
302                           proxyElement.parentNode.firstElementChild !== proxyElement;
303
304        var xAxisPosition = 'left';
305
306        if (isEndAligned) {
307          // When the proxy item is aligned at the end of the list, we have to set the origin to the end.
308          xAxisPosition = 'right';
309        }
310
311        // Set the position mode / origin of the proxied menu.
312        if (!menuEl.attr('md-position-mode')) {
313          menuEl.attr('md-position-mode', xAxisPosition + ' target');
314        }
315
316        // Apply menu open binding to menu button
317        var menuOpenButton = menuEl.children().eq(0);
318        if (!hasClickEvent(menuOpenButton[0])) {
319          menuOpenButton.attr('ng-click', '$mdMenu.open($event)');
320        }
321
322        if (!menuOpenButton.attr('aria-label')) {
323          menuOpenButton.attr('aria-label', 'Open List Menu');
324        }
325      }
326
327      function wrapIn(type) {
328        if (type == 'div') {
329          itemContainer = angular.element('<div class="md-no-style md-list-item-inner">');
330          itemContainer.append(tEl.contents());
331          tEl.addClass('md-proxy-focus');
332        } else {
333          // Element which holds the default list-item content.
334          itemContainer = angular.element(
335            '<div class="md-button md-no-style">'+
336            '   <div class="md-list-item-inner"></div>'+
337            '</div>'
338          );
339
340          // Button which shows ripple and executes primary action.
341          var buttonWrap = angular.element(
342            '<md-button class="md-no-style"></md-button>'
343          );
344
345          copyAttributes(tEl[0], buttonWrap[0]);
346
347          // If there is no aria-label set on the button (previously copied over if present)
348          // we determine the label from the content and copy it to the button.
349          if (!buttonWrap.attr('aria-label')) {
350            buttonWrap.attr('aria-label', $mdAria.getText(tEl));
351          }
352
353          // We allow developers to specify the `md-no-focus` class, to disable the focus style
354          // on the button executor. Once more classes should be forwarded, we should probably make the
355          // class forward more generic.
356          if (tEl.hasClass('md-no-focus')) {
357            buttonWrap.addClass('md-no-focus');
358          }
359
360          // Append the button wrap before our list-item content, because it will overlay in relative.
361          itemContainer.prepend(buttonWrap);
362          itemContainer.children().eq(1).append(tEl.contents());
363
364          tEl.addClass('_md-button-wrap');
365        }
366
367        tEl[0].setAttribute('tabindex', '-1');
368        tEl.append(itemContainer);
369      }
370
371      function wrapSecondaryItems() {
372        var secondaryItemsWrapper = angular.element('<div class="md-secondary-container">');
373
374        angular.forEach(secondaryItems, function(secondaryItem) {
375          wrapSecondaryItem(secondaryItem, secondaryItemsWrapper);
376        });
377
378        itemContainer.append(secondaryItemsWrapper);
379      }
380
381      function wrapSecondaryItem(secondaryItem, container) {
382        // If the current secondary item is not a button, but contains a ng-click attribute,
383        // the secondary item will be automatically wrapped inside of a button.
384        if (secondaryItem && !isButton(secondaryItem) && secondaryItem.hasAttribute('ng-click')) {
385
386          $mdAria.expect(secondaryItem, 'aria-label');
387          var buttonWrapper = angular.element('<md-button class="md-secondary md-icon-button">');
388
389          // Copy the attributes from the secondary item to the generated button.
390          // We also support some additional attributes from the secondary item,
391          // because some developers may use a ngIf, ngHide, ngShow on their item.
392          copyAttributes(secondaryItem, buttonWrapper[0], ['ng-if', 'ng-hide', 'ng-show']);
393
394          secondaryItem.setAttribute('tabindex', '-1');
395          buttonWrapper.append(secondaryItem);
396
397          secondaryItem = buttonWrapper[0];
398        }
399
400        if (secondaryItem && (!hasClickEvent(secondaryItem) || (!tAttrs.ngClick && isProxiedElement(secondaryItem)))) {
401          // In this case we remove the secondary class, so we can identify it later, when we searching for the
402          // proxy items.
403          angular.element(secondaryItem).removeClass('md-secondary');
404        }
405
406        tEl.addClass('md-with-secondary');
407        container.append(secondaryItem);
408      }
409
410      /**
411       * Copies attributes from a source element to the destination element
412       * By default the function will copy the most necessary attributes, supported
413       * by the button executor for clickable list items.
414       * @param source Element with the specified attributes
415       * @param destination Element which will retrieve the attributes
416       * @param extraAttrs Additional attributes, which will be copied over.
417       */
418      function copyAttributes(source, destination, extraAttrs) {
419        var copiedAttrs = $mdUtil.prefixer([
420          'ng-if', 'ng-click', 'ng-dblclick', 'aria-label', 'ng-disabled', 'ui-sref',
421          'href', 'ng-href', 'target', 'ng-attr-ui-sref', 'ui-sref-opts'
422        ]);
423
424        if (extraAttrs) {
425          copiedAttrs = copiedAttrs.concat($mdUtil.prefixer(extraAttrs));
426        }
427
428        angular.forEach(copiedAttrs, function(attr) {
429          if (source.hasAttribute(attr)) {
430            destination.setAttribute(attr, source.getAttribute(attr));
431            source.removeAttribute(attr);
432          }
433        });
434      }
435
436      function isProxiedElement(el) {
437        return proxiedTypes.indexOf(el.nodeName.toLowerCase()) != -1;
438      }
439
440      function isButton(el) {
441        var nodeName = el.nodeName.toUpperCase();
442
443        return nodeName == "MD-BUTTON" || nodeName == "BUTTON";
444      }
445
446      function hasClickEvent (element) {
447        var attr = element.attributes;
448        for (var i = 0; i < attr.length; i++) {
449          if (tAttrs.$normalize(attr[i].name) === 'ngClick') return true;
450        }
451        return false;
452      }
453
454      return postLink;
455
456      function postLink($scope, $element, $attr, ctrl) {
457        $element.addClass('_md');     // private md component indicator for styling
458
459        var proxies       = [],
460            firstElement  = $element[0].firstElementChild,
461            isButtonWrap  = $element.hasClass('_md-button-wrap'),
462            clickChild    = isButtonWrap ? firstElement.firstElementChild : firstElement,
463            hasClick      = clickChild && hasClickEvent(clickChild),
464            noProxies     = $element.hasClass('md-no-proxy');
465
466        computeProxies();
467        computeClickable();
468
469        if (proxies.length) {
470          angular.forEach(proxies, function(proxy) {
471            proxy = angular.element(proxy);
472
473            $scope.mouseActive = false;
474            proxy.on('mousedown', function() {
475              $scope.mouseActive = true;
476              $timeout(function(){
477                $scope.mouseActive = false;
478              }, 100);
479            })
480            .on('focus', function() {
481              if ($scope.mouseActive === false) { $element.addClass('md-focused'); }
482              proxy.on('blur', function proxyOnBlur() {
483                $element.removeClass('md-focused');
484                proxy.off('blur', proxyOnBlur);
485              });
486            });
487          });
488        }
489
490
491        function computeProxies() {
492
493          if (firstElement && firstElement.children && !hasClick && !noProxies) {
494
495            angular.forEach(proxiedTypes, function(type) {
496
497              // All elements which are not capable for being used a proxy have the .md-secondary class
498              // applied. These items had been sorted out in the secondary wrap function.
499              angular.forEach(firstElement.querySelectorAll(type + ':not(.md-secondary)'), function(child) {
500                proxies.push(child);
501              });
502            });
503
504          }
505        }
506
507        function computeClickable() {
508          if (proxies.length == 1 || hasClick) {
509            $element.addClass('md-clickable');
510
511            if (!hasClick) {
512              ctrl.attachRipple($scope, angular.element($element[0].querySelector('.md-no-style')));
513            }
514          }
515        }
516
517        function isEventFromControl(event) {
518          var forbiddenControls = ['md-slider'];
519
520          // If there is no path property in the event, then we can assume that the event was not bubbled.
521          if (!event.path) {
522            return forbiddenControls.indexOf(event.target.tagName.toLowerCase()) !== -1;
523          }
524
525          // We iterate the event path up and check for a possible component.
526          // Our maximum index to search, is the list item root.
527          var maxPath = event.path.indexOf($element.children()[0]);
528
529          for (var i = 0; i < maxPath; i++) {
530            if (forbiddenControls.indexOf(event.path[i].tagName.toLowerCase()) !== -1) {
531              return true;
532            }
533          }
534        }
535
536        var clickChildKeypressListener = function(e) {
537          if (e.target.nodeName != 'INPUT' && e.target.nodeName != 'TEXTAREA' && !e.target.isContentEditable) {
538            var keyCode = e.which || e.keyCode;
539            if (keyCode == $mdConstant.KEY_CODE.SPACE) {
540              if (clickChild) {
541                clickChild.click();
542                e.preventDefault();
543                e.stopPropagation();
544              }
545            }
546          }
547        };
548
549        if (!hasClick && !proxies.length) {
550          clickChild && clickChild.addEventListener('keypress', clickChildKeypressListener);
551        }
552
553        $element.off('click');
554        $element.off('keypress');
555
556        if (proxies.length == 1 && clickChild) {
557          $element.children().eq(0).on('click', function(e) {
558            // When the event is coming from an control and it should not trigger the proxied element
559            // then we are skipping.
560            if (isEventFromControl(e)) return;
561
562            var parentButton = $mdUtil.getClosest(e.target, 'BUTTON');
563            if (!parentButton && clickChild.contains(e.target)) {
564              angular.forEach(proxies, function(proxy) {
565                if (e.target !== proxy && !proxy.contains(e.target)) {
566                  if (proxy.nodeName === 'MD-MENU') {
567                    proxy = proxy.children[0];
568                  }
569                  angular.element(proxy).triggerHandler('click');
570                }
571              });
572            }
573          });
574        }
575
576        $scope.$on('$destroy', function () {
577          clickChild && clickChild.removeEventListener('keypress', clickChildKeypressListener);
578        });
579      }
580    }
581  };
582}
583
584/*
585 * @private
586 * @ngdoc controller
587 * @name MdListController
588 * @module material.components.list
589 *
590 */
591function MdListController($scope, $element, $mdListInkRipple) {
592  var ctrl = this;
593  ctrl.attachRipple = attachRipple;
594
595  function attachRipple (scope, element) {
596    var options = {};
597    $mdListInkRipple.attach(scope, element, options);
598  }
599}
600
601})(window, window.angular);