Merge "Fix the docker push"
[clamp.git] / src / main / resources / META-INF / resources / designer / lib / ui-grid.js
1 /*! ui-grid - v2.0.7-g7aeb576 - 2013-12-18
2 * Copyright (c) 2013 ; Licensed MIT */
3 (function(){
4 'use strict';
5
6 var app = angular.module('ui.grid.body', []);
7
8 app.directive('uiGridBody', ['$log', 'GridUtil', function($log, GridUtil) {
9   return {
10     replace: true,
11     priority: 1000,
12     templateUrl: 'ui-grid/ui-grid-body',
13     require: '?^uiGrid',
14     scope: {
15       tableClass: '=uiGridTableClass'
16     },
17     link: function(scope, elm, attrs, uiGridCtrl) {
18       $log.debug('body postlink scope', scope.$id);
19
20       if (uiGridCtrl === undefined) {
21         $log.warn('[ui-grid-body] uiGridCtrl is undefined!');
22       }
23
24       if (uiGridCtrl && typeof(uiGridCtrl.columns) !== 'undefined' && uiGridCtrl.columns) {
25         scope.columns = uiGridCtrl.columns;
26       }
27       if (uiGridCtrl && typeof(uiGridCtrl.gridData) !== 'undefined' && uiGridCtrl.gridData) {
28         scope.gridData = uiGridCtrl.gridData;
29       }
30     }
31   };
32 }]);
33
34 })();
35 (function(){
36 'use strict';
37
38 var app = angular.module('ui.grid.header', ['ui.grid.util']);
39
40 app.directive('uiGridHeader', ['$log', '$templateCache', '$compile', 'GridUtil', function($log, $templateCache, $compile, GridUtil) {
41   return {
42     restrict: 'EA',
43     // templateUrl: 'ui-grid/ui-grid-header',
44     // replace: true,
45     priority: 1000,
46     require: '?^uiGrid',
47     scope: {
48       tableClass: '=uiGridTableClass'
49     },
50     compile: function (elm, attrs) {
51       $log.debug('header compile');
52
53       // If the contents of the grid element are empty, use the default grid template
54       var tmpl;
55       if (elm.html() === '' || /^\s*$/.test(elm.html())) {
56         tmpl = $templateCache.get('ui-grid/ui-grid-header');
57       }
58
59       var preLink = function (scope, elm, attrs) {
60         $log.debug('header prelink scope', scope.$id);
61
62         if (tmpl) {
63           elm.append(tmpl);
64         }
65         $compile(elm.contents())(scope);
66       };
67
68       var postLink = function(scope, elm, attrs, uiGridCtrl) {
69         $log.debug('header postlink scope', scope.$id);
70
71         if (uiGridCtrl === undefined) {
72           $log.warn('[ui-grid-header] uiGridCtrl is undefined!');
73         }
74
75         // Get the column defs from the parent grid controller
76         if (uiGridCtrl && typeof(uiGridCtrl.columns) !== 'undefined' && uiGridCtrl.columns) {
77           scope.columns = uiGridCtrl.columns;
78         }
79
80         // if (tmpl) {
81         //   elm.append(tmpl);
82         //   $compile(elm.contents())(scope);
83         // }
84
85         // scope.$watch('columns', function(n, o) {
86         //    $log.debug('columns change', n, o);
87         //    var contents = elm.contents();
88         //    $compile(contents)(scope);
89         // });
90       };
91
92       return {
93         pre: preLink,
94         post: postLink
95       };
96     }
97   };
98 }]);
99
100 })();
101 (function(){
102 // 'use strict';
103
104 /**
105  * @ngdoc directive
106  * @name ui.grid.style.directive:uiGridStyle
107  * @element style
108  * @restrict A
109  *
110  * @description
111  * Allows us to interpolate expressions in `<style>` elements. Angular doesn't do this by default as it can/will/might? break in IE8.
112  *
113  * @example
114    <example module="app">
115      <file name="app.js">
116        var app = angular.module('app', ['ui.grid']);
117        
118        app.controller('MainCtrl', ['$scope', function ($scope) {
119          $scope.myStyle = '.blah { color: red }';
120        }]);
121      </file>
122      <file name="index.html">
123        <div ng-controller="MainCtrl">
124          <style ui-grid-style>{{ myStyle }}</style>
125          <span class="blah">I am red.</span>
126        </div>
127      </file>
128    </example>
129  */
130
131 var app = angular.module('ui.grid.style', []);
132
133 app.directive('uiGridStyle', ['$interpolate', '$sce', function($interpolate, $sce) {
134   return {
135     // restrict: 'A',
136     priority: 1000,
137     link: function(scope, element) {
138       var interpolateFn = $interpolate(element.text(), true);
139
140       if (interpolateFn) {
141         scope.$watch(interpolateFn, function(value) {
142           element.text(value);
143         });
144       }
145     }
146   };
147 }]);
148
149 })();
150 (function(){
151 'use strict';
152
153 var app = angular.module('ui.grid', ['ui.grid.header', 'ui.grid.body', 'ui.grid.style', 'ui.virtual-repeat']);
154
155 /**
156  *  @ngdoc directive
157  *  @name ui.grid.directive:uiGrid
158  *  @element div
159  *  @restrict EA
160  *  @param {array} uiGrid Array of rows to display in the grid
161  *  
162  *  @description Create a very basic grid.
163  *
164  *  @example
165     <example module="app">
166       <file name="app.js">
167         var app = angular.module('app', ['ui.grid']);
168
169         app.controller('MainCtrl', ['$scope', function ($scope) {
170           $scope.data = [
171             { name: 'Bob', title: 'CEO' },
172             { name: 'Frank', title: 'Lowly Developer' }
173           ];
174         }]);
175       </file>
176       <file name="index.html">
177         <div ng-controller="MainCtrl">
178           <div ui-grid="data"></div>
179         </div>
180       </file>
181     </example>
182  */
183 app.directive('uiGrid',
184   [
185     '$compile',
186     '$templateCache',
187     '$log',
188     'GridUtil',
189   function(
190     $compile,
191     $templateCache,
192     $log,
193     GridUtil
194   ) {
195
196     function preLink(scope, elm, attrs) {
197       var options = scope.uiGrid;
198
199       // Create an ID for this grid
200       scope.gridId = GridUtil.newId();
201
202       // Get the grid dimensions from the element
203
204       // Initialize the grid
205
206       // Get the column definitions
207         // Put a watch on them
208
209       console.log('gridId', scope.gridId);
210
211       elm.on('$destroy', function() {
212         // Remove columnDefs watch
213       });
214     }
215     
216     return {
217       templateUrl: 'ui-grid/ui-grid',
218       scope: {
219         uiGrid: '='
220       },
221       compile: function () {
222         return {
223           pre: preLink
224         };
225       },
226       controller: function ($scope, $element, $attrs) {
227         
228       }
229     };
230   }
231 ]);
232
233 })();
234 (function(){
235 'use strict';
236   
237 // (part of the sf.virtualScroll module).
238 var mod = angular.module('ui.virtual-repeat', []);
239
240 var DONT_WORK_AS_VIEWPORTS = ['TABLE', 'TBODY', 'THEAD', 'TR', 'TFOOT'];
241 var DONT_WORK_AS_CONTENT = ['TABLE', 'TBODY', 'THEAD', 'TR', 'TFOOT'];
242 var DONT_SET_DISPLAY_BLOCK = ['TABLE', 'TBODY', 'THEAD', 'TR', 'TFOOT'];
243
244 // Utility to clip to range
245 function clip(value, min, max){
246   if (angular.isArray(value)) {
247     return angular.forEach(value, function(v) {
248       return clip(v, min, max);
249     });
250   }
251
252   return Math.max(min, Math.min(value, max));
253 }
254
255 mod.directive('uiVirtualRepeat', ['$log', '$rootElement', function($log, $rootElement){
256
257   // Turn the expression supplied to the directive:
258   //
259   //     a in b
260   //
261   // into `{ value: "a", collection: "b" }`
262   function parseRepeatExpression(expression) {
263     var match = expression.match(/^\s*([\$\w]+)\s+in\s+([\S\s]*)$/);
264     if (! match) {
265       throw new Error("Expected uiVirtualRepeat in form of '_item_ in _collection_' but got '" + expression + "'.");
266     }
267     return {
268       value: match[1],
269       collection: match[2]
270     };
271   }
272
273   // Utility to filter out elements by tag name
274   function isTagNameInList(element, list) {
275     var t,
276         tag = element.tagName.toUpperCase();
277
278     for (t = 0; t < list.length; t++) {
279       if (list[t] === tag) {
280         return true;
281       }
282     }
283     return false;
284   }
285
286
287   // Utility to find the viewport/content elements given the start element:
288   function findViewportAndContent(startElement) {
289     /*jshint eqeqeq:false, curly:false */
290     var root = $rootElement[0];
291     var e, n;
292
293     // Somewhere between the grandparent and the root node
294     for (e = startElement.parent().parent()[0]; e !== root; e = e.parentNode) {
295       // is an element
296       if (e.nodeType != 1) break;
297       // that isn't in the blacklist (tables etc.),
298       if (isTagNameInList(e, DONT_WORK_AS_VIEWPORTS)) continue;
299       // has a single child element (the content),
300       if (e.childElementCount != 1) continue;
301       // which is not in the blacklist
302       if (isTagNameInList(e.firstElementChild, DONT_WORK_AS_CONTENT)) continue;
303       // and no text.
304       for (n = e.firstChild; n; n = n.nextSibling) {
305         if (n.nodeType == 3 && /\S/g.test(n.textContent)) {
306           break;
307         }
308       }
309
310       if (n == null) {
311         // That element should work as a viewport.
312         return {
313           viewport: angular.element(e),
314           content: angular.element(e.firstElementChild)
315         };
316       }
317     }
318
319     throw new Error("No suitable viewport element");
320   }
321
322   // Apply explicit height and overflow styles to the viewport element.
323   //
324   // If the viewport has a max-height (inherited or otherwise), set max-height.
325   // Otherwise, set height from the current computed value or use
326   // window.innerHeight as a fallback
327   //
328   function setViewportCSS(viewport) {
329     var viewportCSS = {'overflow': 'auto'};
330
331     var style = window.getComputedStyle ?
332                 window.getComputedStyle(viewport[0]) :
333                 viewport[0].currentStyle;
334
335     var maxHeight = style && style.getPropertyValue('max-height');
336     var height = style && style.getPropertyValue('height');
337
338     if (maxHeight && maxHeight !== '0px') {
339       viewportCSS.maxHeight = maxHeight;
340     }
341     else if (height && height !== '0px') {
342       viewportCSS.height = height;
343     }
344     else {
345       viewportCSS.height = window.innerHeight;
346     }
347
348     viewport.css(viewportCSS);
349   }
350
351   // Apply explicit styles to the content element to prevent pesky padding
352   // or borders messing with our calculations:
353   function setContentCSS(content) {
354     var contentCSS = {
355       margin: 0,
356       padding: 0,
357       border: 0,
358       'box-sizing': 'border-box'
359     };
360
361     content.css(contentCSS);
362   }
363
364   // TODO: compute outerHeight (padding + border unless box-sizing is border)
365   function computeRowHeight(element) {
366     var style = window.getComputedStyle ?
367                 window.getComputedStyle(element) :
368                 element.currentStyle;
369
370     var maxHeight = style && style.getPropertyValue('max-height');
371     var height = style && style.getPropertyValue('height');
372
373     if (height && height !== '0px' && height !== 'auto') {
374       // $log.info('Row height is "%s" from css height', height);
375     }
376     else if (maxHeight && maxHeight !== '0px' && maxHeight !== 'none') {
377       height = maxHeight;
378       // $log.info('Row height is "%s" from css max-height', height);
379     }
380     else if (element.clientHeight) {
381       height = element.clientHeight + 'px';
382       // $log.info('Row height is "%s" from client height', height);
383     }
384     else {
385       //throw new Error("Unable to compute height of row");
386       return;
387     }
388
389     angular.element(element).css('height', height);
390
391     return parseInt(height, 10);
392   }
393
394   // The compile gathers information about the declaration. There's not much
395   // else we could do in the compile step as we need a viewport parent that
396   // is exculsively ours - this is only available at link time.
397   function uiVirtualRepeatCompile(element, attr, linker) {
398     var ident = parseRepeatExpression(attr.uiVirtualRepeat);
399     
400     // ----
401
402     // Set up the initial value for our watch expression (which is just the
403     // start and length of the active rows and the collection length) and
404     // adds a listener to handle child scopes based on the active rows.
405     function sfVirtualRepeatPostLink(scope, iterStartElement, attrs) {
406
407       var rendered = [];
408           scope.rendered = rendered;
409
410       var rowHeight = 0;
411       var sticky = false;
412       var dom = findViewportAndContent(iterStartElement);
413       // The list structure is controlled by a few simple (visible) variables:
414       var state = 'ngModel' in attrs ? scope.$eval(attrs.ngModel) : {};
415       //  - The index of the first active element
416       state.firstActive = 0;
417       //  - The index of the first visible element
418       state.firstVisible = 0;
419       //  - The number of elements visible in the viewport.
420       state.visible = 0;
421       // - The number of active elements
422       state.active = 0;
423       // - The total number of elements
424       state.total = 0;
425       // - The point at which we add new elements
426       state.lowWater = state.lowWater || 10;
427       // - The point at which we remove old elements
428       state.highWater = state.highWater || 20;
429       // TODO: now watch the water marks
430
431       setContentCSS(dom.content);
432       setViewportCSS(dom.viewport);
433       // When the user scrolls, we move the `state.firstActive`
434       dom.viewport.bind('scroll', sfVirtualRepeatOnScroll);
435
436       // The watch on the collection is just a watch on the length of the
437       // collection. We don't care if the content changes.
438       scope.$watch(sfVirtualRepeatWatchExpression, sfVirtualRepeatListener, true);
439
440       // and that's the link done! All the action is in the handlers...
441       
442       // ----
443
444       // Apply explicit styles to the item element
445       function setElementCSS(element) {
446         var elementCSS = {
447           // no margin or it'll screw up the height calculations.
448           margin: '0'
449         };
450
451         if (!isTagNameInList(element[0], DONT_SET_DISPLAY_BLOCK)) {
452           // display: block if it's safe to do so
453           elementCSS.display = 'block';
454         }
455
456         if (rowHeight) {
457           elementCSS.height = rowHeight + 'px';
458         }
459
460         element.css(elementCSS);
461       }
462
463       function makeNewScope (idx, collection, containerScope) {
464         var childScope = containerScope.$new();
465         childScope[ident.value] = collection[idx];
466         childScope.$index = idx;
467         childScope.$first = (idx === 0);
468         childScope.$last = (idx === (collection.length - 1));
469         childScope.$middle = !(childScope.$first || childScope.$last);
470         childScope.$watch(function updateChildScopeItem(){
471           childScope[ident.value] = collection[idx];
472         });
473         return childScope;
474       }
475
476       function addElements (start, end, collection, containerScope, insPoint) {
477         var frag = document.createDocumentFragment();
478         var newElements = [], element, idx, childScope;
479
480         for (idx = start; idx !== end; idx++) {
481           childScope = makeNewScope(idx, collection, containerScope);
482           element = linker(childScope, angular.noop);
483           setElementCSS(element);
484           newElements.push(element);
485           frag.appendChild(element[0]);
486         }
487
488         insPoint.after(frag);
489         return newElements;
490       }
491
492       function recomputeActive() {
493         // We want to set the start to the low water mark unless the current
494         // start is already between the low and high water marks.
495         var start = clip(state.firstActive, state.firstVisible - state.lowWater, state.firstVisible - state.highWater);
496         // Similarly for the end
497         var end = clip(state.firstActive + state.active,
498                        state.firstVisible + state.visible + state.lowWater,
499                        state.firstVisible + state.visible + state.highWater );
500         state.firstActive = Math.max(0, start);
501         state.active = Math.min(end, state.total) - state.firstActive;
502       }
503
504       function sfVirtualRepeatOnScroll(evt) {
505         if (!rowHeight) {
506           return;
507         }
508
509         // Enter the angular world for the state change to take effect.
510         scope.$apply(function() {
511           state.firstVisible = Math.floor(evt.target.scrollTop / rowHeight);
512           state.visible = Math.ceil(dom.viewport[0].clientHeight / rowHeight);
513
514           // $log.log('scroll to row %o', state.firstVisible);
515           sticky = evt.target.scrollTop + evt.target.clientHeight >= evt.target.scrollHeight;
516
517           recomputeActive();
518           // $log.log(' state is now %o', state);
519           // $log.log(' sticky = %o', sticky);
520         });
521       }
522
523       function sfVirtualRepeatWatchExpression(scope) {
524         var coll = scope.$eval(ident.collection);
525
526         if (coll.length !== state.total) {
527           state.total = coll.length;
528           recomputeActive();
529         }
530
531         return {
532           start: state.firstActive,
533           active: state.active,
534           len: coll.length
535         };
536       }
537
538       function destroyActiveElements (action, count) {
539         var dead,
540             ii,
541             remover = Array.prototype[action];
542
543         for (ii = 0; ii < count; ii++) {
544           dead = remover.call(rendered);
545           dead.scope().$destroy();
546           dead.remove();
547         }
548       }
549
550       // When the watch expression for the repeat changes, we may need to add
551       // and remove scopes and elements
552       function sfVirtualRepeatListener(newValue, oldValue, scope) {
553         var oldEnd = oldValue.start + oldValue.active,
554             collection = scope.$eval(ident.collection),
555             newElements;
556
557         if (newValue === oldValue) {
558           // $log.info('initial listen');
559           newElements = addElements(newValue.start, oldEnd, collection, scope, iterStartElement);
560           rendered = newElements;
561
562           if (rendered.length) {
563             rowHeight = computeRowHeight(newElements[0][0]);
564           }
565         }
566         else {
567           var newEnd = newValue.start + newValue.active;
568           var forward = newValue.start >= oldValue.start;
569           var delta = forward ? newValue.start - oldValue.start
570                               : oldValue.start - newValue.start;
571           var endDelta = newEnd >= oldEnd ? newEnd - oldEnd : oldEnd - newEnd;
572           var contiguous = delta < (forward ? oldValue.active : newValue.active);
573           // $log.info('change by %o,%o rows %s', delta, endDelta, forward ? 'forward' : 'backward');
574
575           if (!contiguous) {
576             // $log.info('non-contiguous change');
577             destroyActiveElements('pop', rendered.length);
578             rendered = addElements(newValue.start, newEnd, collection, scope, iterStartElement);
579           }
580           else {
581             if (forward) {
582               // $log.info('need to remove from the top');
583               destroyActiveElements('shift', delta);
584             }
585             else if (delta) {
586               // $log.info('need to add at the top');
587               newElements = addElements(
588                 newValue.start,
589                 oldValue.start,
590                 collection, scope, iterStartElement);
591               rendered = newElements.concat(rendered);
592             }
593
594             if (newEnd < oldEnd) {
595               // $log.info('need to remove from the bottom');
596               destroyActiveElements('pop', oldEnd - newEnd);
597             }
598             else if (endDelta) {
599               var lastElement = rendered[rendered.length-1];
600               // $log.info('need to add to the bottom');
601               newElements = addElements(
602                 oldEnd,
603                 newEnd,
604                 collection, scope, lastElement);
605
606               rendered = rendered.concat(newElements);
607             }
608           }
609
610           if (!rowHeight && rendered.length) {
611             rowHeight = computeRowHeight(rendered[0][0]);
612           }
613
614           dom.content.css({'padding-top': newValue.start * rowHeight + 'px'});
615         }
616
617         dom.content.css({'height': newValue.len * rowHeight + 'px'});
618
619         if (sticky) {
620           dom.viewport[0].scrollTop = dom.viewport[0].clientHeight + dom.viewport[0].scrollHeight;
621         }
622
623         scope.rendered = rendered;
624       }
625
626       return;
627     }
628
629     return {
630       post: sfVirtualRepeatPostLink
631     };
632   }
633
634   var directive =  {
635     require: '?ngModel',
636     transclude: 'element',
637     priority: 1000,
638     // terminal: true,
639     compile: uiVirtualRepeatCompile,
640     controller: ['$scope', function ($scope) {
641       this.visibleRows = 0;
642       this.visibleRows = this.visibleRows + 1;
643     }]
644   };
645
646   return directive;
647 }]);
648
649 }());
650 (function() {
651
652 var app = angular.module('ui.grid.util', []);
653
654 /**
655  *  @ngdoc service
656  *  @name ui.grid.util.service:GridUtil
657  *  
658  *  @description Grid utility functions
659  */
660 app.service('GridUtil', function () {
661
662   var s = {
663
664     /**
665      * @ngdoc method
666      * @name readableColumnName
667      * @methodOf ui.grid.util.service:GridUtil
668      *
669      * @param {string} columnName Column name as a string
670      * @returns {string} Column name appropriately capitalized and split apart
671      *
672        @example
673        <example module="app">
674         <file name="app.js">
675           var app = angular.module('app', ['ui.grid.util']);
676
677           app.controller('MainCtrl', ['$scope', 'GridUtil', function ($scope, GridUtil) {
678             $scope.name = 'firstName';
679             $scope.columnName = function(name) {
680               return GridUtil.readableColumnName(name);
681             };
682           }]);
683         </file>
684         <file name="index.html">
685           <div ng-controller="MainCtrl">
686             <strong>Column name:</strong> <input ng-model="name" />
687             <br>
688             <strong>Output:</strong> <span ng-bind="columnName(name)"></span>
689           </div>
690         </file>
691       </example>
692      */
693     readableColumnName: function (columnName) {
694       // Convert underscores to spaces
695       if (typeof(columnName) === 'undefined' || columnName === undefined || columnName === null) { return columnName; }
696
697       if (typeof(columnName) !== 'string') {
698         columnName = String(columnName);
699       }
700
701       return columnName.replace(/_+/g, ' ')
702         // Replace a completely all-capsed word with a first-letter-capitalized version
703         .replace(/^[A-Z]+$/, function (match) {
704           return angular.lowercase(angular.uppercase(match.charAt(0)) + match.slice(1));
705         })
706         // Capitalize the first letter of words
707         .replace(/(\w+)/g, function (match) {
708           return angular.uppercase(match.charAt(0)) + match.slice(1);
709         })
710         // Put a space in between words that have partial capilizations (i.e. 'firstName' becomes 'First Name')
711         // .replace(/([A-Z]|[A-Z]\w+)([A-Z])/g, "$1 $2");
712         // .replace(/(\w+?|\w)([A-Z])/g, "$1 $2");
713         .replace(/(\w+?(?=[A-Z]))/g, '$1 ');
714     },
715
716     /**
717      * @ngdoc method
718      * @name getColumnsFromData
719      * @methodOf ui.grid.util.service:GridUtil
720      * @description Return a list of column names, given a data set
721      *
722      * @param {string} data Data array for grid
723      * @returns {Object} Column definitions with field accessor and column name
724      *
725      * @example
726        <pre>
727          var data = [
728            { firstName: 'Bob', lastName: 'Jones' },
729            { firstName: 'Frank', lastName: 'Smith' }
730          ];
731
732          var columnDefs = GridUtil.getColumnsFromData(data);
733
734          columnDefs == [
735           {
736             field: 'firstName',
737             name: 'First Name'
738           },
739           {
740             field: 'lastName',
741             name: 'Last Name'
742           }
743          ];
744        </pre>
745      */
746     getColumnsFromData: function (data) {
747       var columnDefs = [];
748
749       var item = data[0];
750       
751       angular.forEach(item, function (prop, propName) {
752         columnDefs.push({
753           field: propName,
754           name: s.readableColumnName(propName)
755         });
756       });
757
758       return columnDefs;
759     },
760
761     /**
762      * @ngdoc method
763      * @name newId
764      * @methodOf ui.grid.util.service:GridUtil
765      * @description Return a unique ID string
766      *
767      * @returns {string} Unique string
768      *
769      * @example
770        <pre>
771         var id = GridUtil.newId();
772
773         # 1387305700482;
774        </pre>
775      */
776     newId: (function() {
777       var seedId = new Date().getTime();
778       return function() {
779           return seedId += 1;
780       };
781     })(),
782   };
783
784   return s;
785 });
786
787 })();