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