ng-grid-2.0.11.debug.js 131 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979
  1. /***********************************************
  2. * ng-grid JavaScript Library
  3. * Authors: https://github.com/angular-ui/ng-grid/blob/master/README.md
  4. * License: MIT (http://www.opensource.org/licenses/mit-license.php)
  5. * Compiled At: 04/29/2014 10:21
  6. ***********************************************/
  7. (function(window, $) {
  8. 'use strict';
  9. // the # of rows we want to add to the top and bottom of the rendered grid rows
  10. var EXCESS_ROWS = 6;
  11. var SCROLL_THRESHOLD = 4;
  12. var ASC = "asc";
  13. // constant for sorting direction
  14. var DESC = "desc";
  15. // constant for sorting direction
  16. var NG_FIELD = '_ng_field_';
  17. var NG_DEPTH = '_ng_depth_';
  18. var NG_HIDDEN = '_ng_hidden_';
  19. var NG_COLUMN = '_ng_column_';
  20. var CUSTOM_FILTERS = /CUSTOM_FILTERS/g;
  21. var COL_FIELD = /COL_FIELD/g;
  22. var DISPLAY_CELL_TEMPLATE = /DISPLAY_CELL_TEMPLATE/g;
  23. var EDITABLE_CELL_TEMPLATE = /EDITABLE_CELL_TEMPLATE/g;
  24. var CELL_EDITABLE_CONDITION = /CELL_EDITABLE_CONDITION/g;
  25. var TEMPLATE_REGEXP = /<.+>/;
  26. window.ngGrid = {};
  27. window.ngGrid.i18n = {};
  28. // Declare app level module which depends on filters, and services
  29. var ngGridServices = angular.module('ngGrid.services', []);
  30. var ngGridDirectives = angular.module('ngGrid.directives', []);
  31. var ngGridFilters = angular.module('ngGrid.filters', []);
  32. // initialization of services into the main module
  33. angular.module('ngGrid', ['ngGrid.services', 'ngGrid.directives', 'ngGrid.filters']);
  34. //set event binding on the grid so we can select using the up/down keys
  35. var ngMoveSelectionHandler = function($scope, elm, evt, grid) {
  36. if ($scope.selectionProvider.selectedItems === undefined) {
  37. return true;
  38. }
  39. var charCode = evt.which || evt.keyCode,
  40. newColumnIndex,
  41. lastInRow = false,
  42. firstInRow = false,
  43. rowIndex = $scope.selectionProvider.lastClickedRow === undefined ? 1 : $scope.selectionProvider.lastClickedRow.rowIndex,
  44. visibleCols = $scope.columns.filter(function(c) { return c.visible; }),
  45. pinnedCols = $scope.columns.filter(function(c) { return c.pinned; });
  46. if ($scope.col) {
  47. newColumnIndex = visibleCols.indexOf($scope.col);
  48. }
  49. if (charCode !== 37 && charCode !== 38 && charCode !== 39 && charCode !== 40 && (grid.config.noTabInterference || charCode !== 9) && charCode !== 13) {
  50. return true;
  51. }
  52. if ($scope.enableCellSelection) {
  53. if (charCode === 9) { //tab key
  54. evt.preventDefault();
  55. }
  56. var focusedOnFirstColumn = $scope.showSelectionCheckbox ? $scope.col.index === 1 : $scope.col.index === 0;
  57. var focusedOnFirstVisibleColumns = $scope.$index === 1 || $scope.$index === 0;
  58. var focusedOnLastVisibleColumns = $scope.$index === ($scope.renderedColumns.length - 1) || $scope.$index === ($scope.renderedColumns.length - 2);
  59. var focusedOnLastColumn = visibleCols.indexOf($scope.col) === (visibleCols.length - 1);
  60. var focusedOnLastPinnedColumn = pinnedCols.indexOf($scope.col) === (pinnedCols.length - 1);
  61. if (charCode === 37 || charCode === 9 && evt.shiftKey) {
  62. var scrollTo = 0;
  63. if (!focusedOnFirstColumn) {
  64. newColumnIndex -= 1;
  65. }
  66. if (focusedOnFirstVisibleColumns) {
  67. if (focusedOnFirstColumn && charCode === 9 && evt.shiftKey){
  68. scrollTo = grid.$canvas.width();
  69. newColumnIndex = visibleCols.length - 1;
  70. firstInRow = true;
  71. }
  72. else {
  73. scrollTo = grid.$viewport.scrollLeft() - $scope.col.width;
  74. }
  75. }
  76. else if (pinnedCols.length > 0) {
  77. scrollTo = grid.$viewport.scrollLeft() - visibleCols[newColumnIndex].width;
  78. }
  79. grid.$viewport.scrollLeft(scrollTo);
  80. }
  81. else if (charCode === 39 || charCode === 9 && !evt.shiftKey) {
  82. if (focusedOnLastVisibleColumns) {
  83. if (focusedOnLastColumn && charCode === 9 && !evt.shiftKey) {
  84. grid.$viewport.scrollLeft(0);
  85. newColumnIndex = $scope.showSelectionCheckbox ? 1 : 0;
  86. lastInRow = true;
  87. }
  88. else {
  89. grid.$viewport.scrollLeft(grid.$viewport.scrollLeft() + $scope.col.width);
  90. }
  91. }
  92. else if (focusedOnLastPinnedColumn) {
  93. grid.$viewport.scrollLeft(0);
  94. }
  95. if (!focusedOnLastColumn) {
  96. newColumnIndex += 1;
  97. }
  98. }
  99. }
  100. var items;
  101. if ($scope.configGroups.length > 0) {
  102. items = grid.rowFactory.parsedData.filter(function (row) {
  103. return !row.isAggRow;
  104. });
  105. }
  106. else {
  107. items = grid.filteredRows;
  108. }
  109. var offset = 0;
  110. if (rowIndex !== 0 && (charCode === 38 || charCode === 13 && evt.shiftKey || charCode === 9 && evt.shiftKey && firstInRow)) { //arrow key up or shift enter or tab key and first item in row
  111. offset = -1;
  112. }
  113. else if (rowIndex !== items.length - 1 && (charCode === 40 || charCode === 13 && !evt.shiftKey || charCode === 9 && lastInRow)) {//arrow key down, enter, or tab key and last item in row?
  114. offset = 1;
  115. }
  116. if (offset) {
  117. var r = items[rowIndex + offset];
  118. if (r.beforeSelectionChange(r, evt)) {
  119. r.continueSelection(evt);
  120. $scope.$emit('ngGridEventDigestGridParent');
  121. if ($scope.selectionProvider.lastClickedRow.renderedRowIndex >= $scope.renderedRows.length - EXCESS_ROWS - 2) {
  122. grid.$viewport.scrollTop(grid.$viewport.scrollTop() + $scope.rowHeight);
  123. }
  124. else if ($scope.selectionProvider.lastClickedRow.renderedRowIndex <= EXCESS_ROWS + 2) {
  125. grid.$viewport.scrollTop(grid.$viewport.scrollTop() - $scope.rowHeight);
  126. }
  127. }
  128. }
  129. if ($scope.enableCellSelection) {
  130. setTimeout(function(){
  131. $scope.domAccessProvider.focusCellElement($scope, $scope.renderedColumns.indexOf(visibleCols[newColumnIndex]));
  132. }, 3);
  133. }
  134. return false;
  135. };
  136. if (!String.prototype.trim) {
  137. String.prototype.trim = function() {
  138. return this.replace(/^\s+|\s+$/g, '');
  139. };
  140. }
  141. if (!Array.prototype.indexOf) {
  142. Array.prototype.indexOf = function(elt /*, from*/) {
  143. var len = this.length >>> 0;
  144. var from = Number(arguments[1]) || 0;
  145. from = (from < 0) ? Math.ceil(from) : Math.floor(from);
  146. if (from < 0) {
  147. from += len;
  148. }
  149. for (; from < len; from++) {
  150. if (from in this && this[from] === elt) {
  151. return from;
  152. }
  153. }
  154. return -1;
  155. };
  156. }
  157. if (!Array.prototype.filter) {
  158. Array.prototype.filter = function(fun /*, thisp */) {
  159. "use strict";
  160. var t = Object(this);
  161. var len = t.length >>> 0;
  162. if (typeof fun !== "function") {
  163. throw new TypeError();
  164. }
  165. var res = [];
  166. var thisp = arguments[1];
  167. for (var i = 0; i < len; i++) {
  168. if (i in t) {
  169. var val = t[i]; // in case fun mutates this
  170. if (fun.call(thisp, val, i, t)) {
  171. res.push(val);
  172. }
  173. }
  174. }
  175. return res;
  176. };
  177. }
  178. ngGridFilters.filter('checkmark', function() {
  179. return function(input) {
  180. return input ? '\u2714' : '\u2718';
  181. };
  182. });
  183. ngGridFilters.filter('ngColumns', function() {
  184. return function(input) {
  185. return input.filter(function(col) {
  186. return !col.isAggCol;
  187. });
  188. };
  189. });
  190. angular.module('ngGrid.services').factory('$domUtilityService',['$utilityService', '$window', function($utils, $window) {
  191. var domUtilityService = {};
  192. var regexCache = {};
  193. var getWidths = function() {
  194. var $testContainer = $('<div></div>');
  195. $testContainer.appendTo('body');
  196. // 1. Run all the following measurements on startup!
  197. //measure Scroll Bars
  198. $testContainer.height(100).width(100).css("position", "absolute").css("overflow", "scroll");
  199. $testContainer.append('<div style="height: 400px; width: 400px;"></div>');
  200. domUtilityService.ScrollH = ($testContainer.height() - $testContainer[0].clientHeight);
  201. domUtilityService.ScrollW = ($testContainer.width() - $testContainer[0].clientWidth);
  202. $testContainer.empty();
  203. //clear styles
  204. $testContainer.attr('style', '');
  205. //measure letter sizes using a pretty typical font size and fat font-family
  206. $testContainer.append('<span style="font-family: Verdana, Helvetica, Sans-Serif; font-size: 14px;"><strong>M</strong></span>');
  207. domUtilityService.LetterW = $testContainer.children().first().width();
  208. $testContainer.remove();
  209. };
  210. domUtilityService.eventStorage = {};
  211. domUtilityService.AssignGridContainers = function($scope, rootEl, grid) {
  212. grid.$root = $(rootEl);
  213. //Headers
  214. grid.$topPanel = grid.$root.find(".ngTopPanel");
  215. grid.$groupPanel = grid.$root.find(".ngGroupPanel");
  216. grid.$headerContainer = grid.$topPanel.find(".ngHeaderContainer");
  217. $scope.$headerContainer = grid.$headerContainer;
  218. grid.$headerScroller = grid.$topPanel.find(".ngHeaderScroller");
  219. grid.$headers = grid.$headerScroller.children();
  220. //Viewport
  221. grid.$viewport = grid.$root.find(".ngViewport");
  222. //Canvas
  223. grid.$canvas = grid.$viewport.find(".ngCanvas");
  224. //Footers
  225. grid.$footerPanel = grid.$root.find(".ngFooterPanel");
  226. var scopeDereg = $scope.$watch(function () {
  227. return grid.$viewport.scrollLeft();
  228. }, function (newLeft) {
  229. return grid.$headerContainer.scrollLeft(newLeft);
  230. });
  231. $scope.$on('$destroy', function() {
  232. // Remove all references to DOM elements, otherwise we get memory leaks
  233. $(grid.$root.parent()).off('resize.nggrid');
  234. grid.$root = null;
  235. grid.$topPanel = null;
  236. // grid.$groupPanel = null;
  237. grid.$headerContainer = null;
  238. // grid.$headerScroller = null;
  239. grid.$headers = null;
  240. grid.$canvas = null;
  241. grid.$footerPanel = null;
  242. scopeDereg();
  243. });
  244. domUtilityService.UpdateGridLayout($scope, grid);
  245. };
  246. domUtilityService.getRealWidth = function (obj) {
  247. var width = 0;
  248. var props = { visibility: "hidden", display: "block" };
  249. var hiddenParents = obj.parents().andSelf().not(':visible');
  250. $.swap(hiddenParents[0], props, function () {
  251. width = obj.outerWidth();
  252. });
  253. return width;
  254. };
  255. domUtilityService.UpdateGridLayout = function($scope, grid) {
  256. if (!grid.$root){
  257. return;
  258. }
  259. //catch this so we can return the viewer to their original scroll after the resize!
  260. var scrollTop = grid.$viewport.scrollTop();
  261. grid.elementDims.rootMaxW = grid.$root.width();
  262. if (grid.$root.is(':hidden')) {
  263. grid.elementDims.rootMaxW = domUtilityService.getRealWidth(grid.$root);
  264. }
  265. grid.elementDims.rootMaxH = grid.$root.height();
  266. //check to see if anything has changed
  267. grid.refreshDomSizes();
  268. $scope.adjustScrollTop(scrollTop, true); //ensure that the user stays scrolled where they were
  269. };
  270. domUtilityService.numberOfGrids = 0;
  271. domUtilityService.setStyleText = function(grid, css) {
  272. var style = grid.styleSheet,
  273. gridId = grid.gridId,
  274. doc = $window.document;
  275. if (!style) {
  276. style = doc.getElementById(gridId);
  277. }
  278. if (!style) {
  279. style = doc.createElement('style');
  280. style.type = 'text/css';
  281. style.id = gridId;
  282. (doc.head || doc.getElementsByTagName('head')[0]).appendChild(style);
  283. }
  284. if (style.styleSheet && !style.sheet) {
  285. style.styleSheet.cssText = css;
  286. } else {
  287. style.innerHTML = css;
  288. }
  289. grid.styleSheet = style;
  290. grid.styleText = css;
  291. };
  292. domUtilityService.BuildStyles = function($scope, grid, digest) {
  293. var rowHeight = grid.config.rowHeight,
  294. gridId = grid.gridId,
  295. css,
  296. cols = $scope.columns,
  297. sumWidth = 0;
  298. var trw = $scope.totalRowWidth();
  299. css = "." + gridId + " .ngCanvas { width: " + trw + "px; }" +
  300. "." + gridId + " .ngRow { width: " + trw + "px; }" +
  301. "." + gridId + " .ngCanvas { width: " + trw + "px; }" +
  302. "." + gridId + " .ngHeaderScroller { width: " + (trw + domUtilityService.ScrollH) + "px}";
  303. for (var i = 0; i < cols.length; i++) {
  304. var col = cols[i];
  305. if (col.visible !== false) {
  306. css += "." + gridId + " .col" + i + " { width: " + col.width + "px; left: " + sumWidth + "px; height: " + rowHeight + "px }" +
  307. "." + gridId + " .colt" + i + " { width: " + col.width + "px; }";
  308. sumWidth += col.width;
  309. }
  310. }
  311. domUtilityService.setStyleText(grid, css);
  312. $scope.adjustScrollLeft(grid.$viewport.scrollLeft());
  313. if (digest) {
  314. domUtilityService.digest($scope);
  315. }
  316. };
  317. domUtilityService.setColLeft = function(col, colLeft, grid) {
  318. if (grid.styleText) {
  319. var regex = regexCache[col.index];
  320. if (!regex) {
  321. regex = regexCache[col.index] = new RegExp(".col" + col.index + " { width: [0-9]+px; left: [0-9]+px");
  322. }
  323. var css = grid.styleText.replace(regex, ".col" + col.index + " { width: " + col.width + "px; left: " + colLeft + "px");
  324. domUtilityService.setStyleText(grid, css);
  325. }
  326. };
  327. domUtilityService.setColLeft.immediate = 1;
  328. domUtilityService.RebuildGrid = function($scope, grid){
  329. domUtilityService.UpdateGridLayout($scope, grid);
  330. if (grid.config.maintainColumnRatios == null || grid.config.maintainColumnRatios) {
  331. grid.configureColumnWidths();
  332. }
  333. $scope.adjustScrollLeft(grid.$viewport.scrollLeft());
  334. domUtilityService.BuildStyles($scope, grid, true);
  335. };
  336. domUtilityService.digest = function($scope) {
  337. if (!$scope.$root.$$phase) {
  338. $scope.$digest();
  339. }
  340. };
  341. domUtilityService.ScrollH = 17; // default in IE, Chrome, & most browsers
  342. domUtilityService.ScrollW = 17; // default in IE, Chrome, & most browsers
  343. domUtilityService.LetterW = 10;
  344. getWidths();
  345. return domUtilityService;
  346. }]);
  347. angular.module('ngGrid.services').factory('$sortService', ['$parse', function($parse) {
  348. var sortService = {};
  349. sortService.colSortFnCache = {}; // cache of sorting functions. Once we create them, we don't want to keep re-doing it
  350. sortService.isCustomSort = false; // track if we're using an internal sort or a user provided sort
  351. // this takes an piece of data from the cell and tries to determine its type and what sorting
  352. // function to use for it
  353. // @item - the cell data
  354. sortService.guessSortFn = function(item) {
  355. var itemType = typeof(item);
  356. //check for numbers and booleans
  357. switch (itemType) {
  358. case "number":
  359. return sortService.sortNumber;
  360. case "boolean":
  361. return sortService.sortBool;
  362. case "string":
  363. // if number string return number string sort fn. else return the str
  364. return item.match(/^[-+]?[£$¤]?[\d,.]+%?$/) ? sortService.sortNumberStr : sortService.sortAlpha;
  365. default:
  366. //check if the item is a valid Date
  367. if (Object.prototype.toString.call(item) === '[object Date]') {
  368. return sortService.sortDate;
  369. }
  370. else {
  371. //finally just sort the basic sort...
  372. return sortService.basicSort;
  373. }
  374. }
  375. };
  376. //#region Sorting Functions
  377. sortService.basicSort = function(a, b) {
  378. if (a === b) {
  379. return 0;
  380. }
  381. if (a < b) {
  382. return -1;
  383. }
  384. return 1;
  385. };
  386. sortService.sortNumber = function(a, b) {
  387. return a - b;
  388. };
  389. sortService.sortNumberStr = function(a, b) {
  390. var numA, numB, badA = false, badB = false;
  391. numA = parseFloat(a.replace(/[^0-9.-]/g, ''));
  392. if (isNaN(numA)) {
  393. badA = true;
  394. }
  395. numB = parseFloat(b.replace(/[^0-9.-]/g, ''));
  396. if (isNaN(numB)) {
  397. badB = true;
  398. }
  399. // we want bad ones to get pushed to the bottom... which effectively is "greater than"
  400. if (badA && badB) {
  401. return 0;
  402. }
  403. if (badA) {
  404. return 1;
  405. }
  406. if (badB) {
  407. return -1;
  408. }
  409. return numA - numB;
  410. };
  411. sortService.sortAlpha = function(a, b) {
  412. var strA = a.toLowerCase(),
  413. strB = b.toLowerCase();
  414. return strA === strB ? 0 : (strA < strB ? -1 : 1);
  415. };
  416. sortService.sortDate = function(a, b) {
  417. var timeA = a.getTime(),
  418. timeB = b.getTime();
  419. return timeA === timeB ? 0 : (timeA < timeB ? -1 : 1);
  420. };
  421. sortService.sortBool = function(a, b) {
  422. if (a && b) {
  423. return 0;
  424. }
  425. if (!a && !b) {
  426. return 0;
  427. } else {
  428. return a ? 1 : -1;
  429. }
  430. };
  431. //#endregion
  432. // the core sorting logic trigger
  433. sortService.sortData = function(sortInfo, data /*datasource*/) {
  434. // first make sure we are even supposed to do work
  435. if (!data || !sortInfo) {
  436. return;
  437. }
  438. var l = sortInfo.fields.length,
  439. order = sortInfo.fields,
  440. col,
  441. direction,
  442. // IE9 HACK.... omg, I can't reference data array within the sort fn below. has to be a separate reference....!!!!
  443. d = data.slice(0);
  444. //now actually sort the data
  445. data.sort(function (itemA, itemB) {
  446. var tem = 0,
  447. indx = 0,
  448. res,
  449. sortFn;
  450. while (tem === 0 && indx < l) {
  451. // grab the metadata for the rest of the logic
  452. col = sortInfo.columns[indx];
  453. direction = sortInfo.directions[indx];
  454. sortFn = sortService.getSortFn(col, d);
  455. var propA = $parse(order[indx])(itemA);
  456. var propB = $parse(order[indx])(itemB);
  457. // if user provides custom sort, we want them to have full control of the sort
  458. if (sortService.isCustomSort) {
  459. res = sortFn(propA, propB);
  460. tem = direction === ASC ? res : 0 - res;
  461. } else {
  462. // we want to allow zero values to be evaluated in the sort function
  463. if ((!propA && propA !== 0) || (!propB && propB !== 0)) {
  464. // we want to force nulls and such to the bottom when we sort... which effectively is "greater than"
  465. if (!propB && !propA) {
  466. tem = 0;
  467. }
  468. else if (!propA) {
  469. tem = 1;
  470. }
  471. else if (!propB) {
  472. tem = -1;
  473. }
  474. }
  475. else {
  476. // this will keep nulls at the bottom regardless of ordering
  477. res = sortFn(propA, propB);
  478. tem = direction === ASC ? res : 0 - res;
  479. }
  480. }
  481. indx++;
  482. }
  483. return tem;
  484. });
  485. };
  486. sortService.Sort = function(sortInfo, data) {
  487. if (sortService.isSorting) {
  488. return;
  489. }
  490. sortService.isSorting = true;
  491. sortService.sortData(sortInfo, data);
  492. sortService.isSorting = false;
  493. };
  494. sortService.getSortFn = function(col, data) {
  495. var sortFn, item;
  496. //see if we already figured out what to use to sort the column
  497. if (sortService.colSortFnCache[col.field]) {
  498. sortFn = sortService.colSortFnCache[col.field];
  499. }
  500. else if (col.sortingAlgorithm !== undefined) {
  501. sortFn = col.sortingAlgorithm;
  502. sortService.colSortFnCache[col.field] = col.sortingAlgorithm;
  503. sortService.isCustomSort = true;
  504. }
  505. else { // try and guess what sort function to use
  506. item = data[0];
  507. if (!item) {
  508. return sortFn;
  509. }
  510. sortFn = sortService.guessSortFn($parse(col.field)(item));
  511. //cache it
  512. if (sortFn) {
  513. sortService.colSortFnCache[col.field] = sortFn;
  514. } else {
  515. // we assign the alpha sort because anything that is null/undefined will never get passed to
  516. // the actual sorting function. It will get caught in our null check and returned to be sorted
  517. // down to the bottom
  518. sortFn = sortService.sortAlpha;
  519. }
  520. }
  521. return sortFn;
  522. };
  523. return sortService;
  524. }]);
  525. angular.module('ngGrid.services').factory('$utilityService', ['$parse', function ($parse) {
  526. var funcNameRegex = /function (.{1,})\(/;
  527. var utils = {
  528. visualLength: function(node) {
  529. var elem = document.getElementById('testDataLength');
  530. if (!elem) {
  531. elem = document.createElement('SPAN');
  532. elem.id = "testDataLength";
  533. elem.style.visibility = "hidden";
  534. document.body.appendChild(elem);
  535. }
  536. var $node = $(node);
  537. $(elem).css({'font': $node.css('font'),
  538. 'font-size': $node.css('font-size'),
  539. 'font-family': $node.css('font-family')});
  540. elem.innerHTML = $node.text();
  541. var width = elem.offsetWidth;
  542. document.body.removeChild(elem);
  543. return width;
  544. },
  545. forIn: function(obj, action) {
  546. for (var prop in obj) {
  547. if (obj.hasOwnProperty(prop)) {
  548. action(obj[prop], prop);
  549. }
  550. }
  551. },
  552. evalProperty: function (entity, path) {
  553. return $parse("entity." + path)({ entity: entity });
  554. },
  555. endsWith: function(str, suffix) {
  556. if (!str || !suffix || typeof str !== "string") {
  557. return false;
  558. }
  559. return str.indexOf(suffix, str.length - suffix.length) !== -1;
  560. },
  561. isNullOrUndefined: function(obj) {
  562. if (obj === undefined || obj === null) {
  563. return true;
  564. }
  565. return false;
  566. },
  567. getElementsByClassName: function(cl) {
  568. if (document.getElementsByClassName) {
  569. return document.getElementsByClassName(cl);
  570. }
  571. else {
  572. var retnode = [];
  573. var myclass = new RegExp('\\b' + cl + '\\b');
  574. var elem = document.getElementsByTagName('*');
  575. for (var i = 0; i < elem.length; i++) {
  576. var classes = elem[i].className;
  577. if (myclass.test(classes)) {
  578. retnode.push(elem[i]);
  579. }
  580. }
  581. return retnode;
  582. }
  583. },
  584. newId: (function() {
  585. var seedId = new Date().getTime();
  586. return function() {
  587. return seedId += 1;
  588. };
  589. })(),
  590. seti18n: function($scope, language) {
  591. var $langPack = window.ngGrid.i18n[language];
  592. for (var label in $langPack) {
  593. $scope.i18n[label] = $langPack[label];
  594. }
  595. },
  596. getInstanceType: function (o) {
  597. var results = (funcNameRegex).exec(o.constructor.toString());
  598. if (results && results.length > 1) {
  599. var instanceType = results[1].replace(/^\s+|\s+$/g, ""); // Trim surrounding whitespace; IE appears to add a space at the end
  600. return instanceType;
  601. }
  602. else {
  603. return "";
  604. }
  605. }
  606. };
  607. return utils;
  608. }]);
  609. var ngAggregate = function (aggEntity, rowFactory, rowHeight, groupInitState) {
  610. this.rowIndex = 0;
  611. this.offsetTop = this.rowIndex * rowHeight;
  612. this.entity = aggEntity;
  613. this.label = aggEntity.gLabel;
  614. this.field = aggEntity.gField;
  615. this.depth = aggEntity.gDepth;
  616. this.parent = aggEntity.parent;
  617. this.children = aggEntity.children;
  618. this.aggChildren = aggEntity.aggChildren;
  619. this.aggIndex = aggEntity.aggIndex;
  620. this.collapsed = groupInitState;
  621. this.groupInitState = groupInitState;
  622. this.rowFactory = rowFactory;
  623. this.rowHeight = rowHeight;
  624. this.isAggRow = true;
  625. this.offsetLeft = aggEntity.gDepth * 25;
  626. this.aggLabelFilter = aggEntity.aggLabelFilter;
  627. };
  628. ngAggregate.prototype.toggleExpand = function () {
  629. this.collapsed = this.collapsed ? false : true;
  630. if (this.orig) {
  631. this.orig.collapsed = this.collapsed;
  632. }
  633. this.notifyChildren();
  634. };
  635. ngAggregate.prototype.setExpand = function (state) {
  636. this.collapsed = state;
  637. this.notifyChildren();
  638. };
  639. ngAggregate.prototype.notifyChildren = function () {
  640. var longest = Math.max(this.rowFactory.aggCache.length, this.children.length);
  641. for (var i = 0; i < longest; i++) {
  642. if (this.aggChildren[i]) {
  643. this.aggChildren[i].entity[NG_HIDDEN] = this.collapsed;
  644. if (this.collapsed) {
  645. this.aggChildren[i].setExpand(this.collapsed);
  646. }
  647. }
  648. if (this.children[i]) {
  649. this.children[i][NG_HIDDEN] = this.collapsed;
  650. }
  651. if (i > this.aggIndex && this.rowFactory.aggCache[i]) {
  652. var agg = this.rowFactory.aggCache[i];
  653. var offset = (30 * this.children.length);
  654. agg.offsetTop = this.collapsed ? agg.offsetTop - offset : agg.offsetTop + offset;
  655. }
  656. }
  657. this.rowFactory.renderedChange();
  658. };
  659. ngAggregate.prototype.aggClass = function () {
  660. return this.collapsed ? "ngAggArrowCollapsed" : "ngAggArrowExpanded";
  661. };
  662. ngAggregate.prototype.totalChildren = function () {
  663. if (this.aggChildren.length > 0) {
  664. var i = 0;
  665. var recurse = function (cur) {
  666. if (cur.aggChildren.length > 0) {
  667. angular.forEach(cur.aggChildren, function (a) {
  668. recurse(a);
  669. });
  670. } else {
  671. i += cur.children.length;
  672. }
  673. };
  674. recurse(this);
  675. return i;
  676. } else {
  677. return this.children.length;
  678. }
  679. };
  680. ngAggregate.prototype.copy = function () {
  681. var ret = new ngAggregate(this.entity, this.rowFactory, this.rowHeight, this.groupInitState);
  682. ret.orig = this;
  683. return ret;
  684. };
  685. var ngColumn = function (config, $scope, grid, domUtilityService, $templateCache, $utils) {
  686. var self = this,
  687. colDef = config.colDef,
  688. delay = 500,
  689. clicks = 0,
  690. timer = null;
  691. self.colDef = config.colDef;
  692. self.width = colDef.width;
  693. self.groupIndex = 0;
  694. self.isGroupedBy = false;
  695. self.minWidth = !colDef.minWidth ? 50 : colDef.minWidth;
  696. self.maxWidth = !colDef.maxWidth ? 9000 : colDef.maxWidth;
  697. // TODO: Use the column's definition for enabling cell editing
  698. // self.enableCellEdit = config.enableCellEdit || colDef.enableCellEdit;
  699. self.enableCellEdit = colDef.enableCellEdit !== undefined ? colDef.enableCellEdit : (config.enableCellEdit || config.enableCellEditOnFocus);
  700. self.cellEditableCondition = colDef.cellEditableCondition || config.cellEditableCondition || 'true';
  701. self.headerRowHeight = config.headerRowHeight;
  702. // Use colDef.displayName as long as it's not undefined, otherwise default to the field name
  703. self.displayName = (colDef.displayName === undefined) ? colDef.field : colDef.displayName;
  704. self.index = config.index;
  705. self.isAggCol = config.isAggCol;
  706. self.cellClass = colDef.cellClass;
  707. self.sortPriority = undefined;
  708. self.cellFilter = colDef.cellFilter ? colDef.cellFilter : "";
  709. self.field = colDef.field;
  710. self.aggLabelFilter = colDef.aggLabelFilter || colDef.cellFilter;
  711. self.visible = $utils.isNullOrUndefined(colDef.visible) || colDef.visible;
  712. self.sortable = false;
  713. self.resizable = false;
  714. self.pinnable = false;
  715. self.pinned = (config.enablePinning && colDef.pinned);
  716. self.originalIndex = config.originalIndex == null ? self.index : config.originalIndex;
  717. self.groupable = $utils.isNullOrUndefined(colDef.groupable) || colDef.groupable;
  718. if (config.enableSort) {
  719. self.sortable = $utils.isNullOrUndefined(colDef.sortable) || colDef.sortable;
  720. }
  721. if (config.enableResize) {
  722. self.resizable = $utils.isNullOrUndefined(colDef.resizable) || colDef.resizable;
  723. }
  724. if (config.enablePinning) {
  725. self.pinnable = $utils.isNullOrUndefined(colDef.pinnable) || colDef.pinnable;
  726. }
  727. self.sortDirection = undefined;
  728. self.sortingAlgorithm = colDef.sortFn;
  729. self.headerClass = colDef.headerClass;
  730. self.cursor = self.sortable ? 'pointer' : 'default';
  731. self.headerCellTemplate = colDef.headerCellTemplate || $templateCache.get('headerCellTemplate.html');
  732. self.cellTemplate = colDef.cellTemplate || $templateCache.get('cellTemplate.html').replace(CUSTOM_FILTERS, self.cellFilter ? "|" + self.cellFilter : "");
  733. if(self.enableCellEdit) {
  734. self.cellEditTemplate = colDef.cellEditTemplate || $templateCache.get('cellEditTemplate.html');
  735. self.editableCellTemplate = colDef.editableCellTemplate || $templateCache.get('editableCellTemplate.html');
  736. }
  737. if (colDef.cellTemplate && !TEMPLATE_REGEXP.test(colDef.cellTemplate)) {
  738. self.cellTemplate = $templateCache.get(colDef.cellTemplate) || $.ajax({
  739. type: "GET",
  740. url: colDef.cellTemplate,
  741. async: false
  742. }).responseText;
  743. }
  744. if (self.enableCellEdit && colDef.editableCellTemplate && !TEMPLATE_REGEXP.test(colDef.editableCellTemplate)) {
  745. self.editableCellTemplate = $templateCache.get(colDef.editableCellTemplate) || $.ajax({
  746. type: "GET",
  747. url: colDef.editableCellTemplate,
  748. async: false
  749. }).responseText;
  750. }
  751. if (colDef.headerCellTemplate && !TEMPLATE_REGEXP.test(colDef.headerCellTemplate)) {
  752. self.headerCellTemplate = $templateCache.get(colDef.headerCellTemplate) || $.ajax({
  753. type: "GET",
  754. url: colDef.headerCellTemplate,
  755. async: false
  756. }).responseText;
  757. }
  758. self.colIndex = function () {
  759. var classes = self.pinned ? "pinned " : "";
  760. classes += "col" + self.index + " colt" + self.index;
  761. if (self.cellClass) {
  762. classes += " " + self.cellClass;
  763. }
  764. return classes;
  765. };
  766. self.groupedByClass = function() {
  767. return self.isGroupedBy ? "ngGroupedByIcon" : "ngGroupIcon";
  768. };
  769. self.toggleVisible = function() {
  770. self.visible = !self.visible;
  771. };
  772. self.showSortButtonUp = function() {
  773. return self.sortable ? self.sortDirection === DESC : self.sortable;
  774. };
  775. self.showSortButtonDown = function() {
  776. return self.sortable ? self.sortDirection === ASC : self.sortable;
  777. };
  778. self.noSortVisible = function() {
  779. return !self.sortDirection;
  780. };
  781. self.sort = function(evt) {
  782. if (!self.sortable) {
  783. return true; // column sorting is disabled, do nothing
  784. }
  785. var dir = self.sortDirection === ASC ? DESC : ASC;
  786. self.sortDirection = dir;
  787. config.sortCallback(self, evt);
  788. return false;
  789. };
  790. self.gripClick = function() {
  791. clicks++; //count clicks
  792. if (clicks === 1) {
  793. timer = setTimeout(function() {
  794. //Here you can add a single click action.
  795. clicks = 0; //after action performed, reset counter
  796. }, delay);
  797. } else {
  798. clearTimeout(timer); //prevent single-click action
  799. config.resizeOnDataCallback(self); //perform double-click action
  800. clicks = 0; //after action performed, reset counter
  801. }
  802. };
  803. self.gripOnMouseDown = function(event) {
  804. $scope.isColumnResizing = true;
  805. if (event.ctrlKey && !self.pinned) {
  806. self.toggleVisible();
  807. domUtilityService.BuildStyles($scope, grid);
  808. return true;
  809. }
  810. event.target.parentElement.style.cursor = 'col-resize';
  811. self.startMousePosition = event.clientX;
  812. self.origWidth = self.width;
  813. $(document).mousemove(self.onMouseMove);
  814. $(document).mouseup(self.gripOnMouseUp);
  815. return false;
  816. };
  817. self.onMouseMove = function(event) {
  818. var diff = event.clientX - self.startMousePosition;
  819. var newWidth = diff + self.origWidth;
  820. self.width = (newWidth < self.minWidth ? self.minWidth : (newWidth > self.maxWidth ? self.maxWidth : newWidth));
  821. $scope.hasUserChangedGridColumnWidths = true;
  822. domUtilityService.BuildStyles($scope, grid);
  823. return false;
  824. };
  825. self.gripOnMouseUp = function (event) {
  826. $(document).off('mousemove', self.onMouseMove);
  827. $(document).off('mouseup', self.gripOnMouseUp);
  828. event.target.parentElement.style.cursor = 'default';
  829. domUtilityService.digest($scope);
  830. $scope.isColumnResizing = false;
  831. return false;
  832. };
  833. self.copy = function() {
  834. var ret = new ngColumn(config, $scope, grid, domUtilityService, $templateCache, $utils);
  835. ret.isClone = true;
  836. ret.orig = self;
  837. return ret;
  838. };
  839. self.setVars = function (fromCol) {
  840. self.orig = fromCol;
  841. self.width = fromCol.width;
  842. self.groupIndex = fromCol.groupIndex;
  843. self.isGroupedBy = fromCol.isGroupedBy;
  844. self.displayName = fromCol.displayName;
  845. self.index = fromCol.index;
  846. self.isAggCol = fromCol.isAggCol;
  847. self.cellClass = fromCol.cellClass;
  848. self.cellFilter = fromCol.cellFilter;
  849. self.field = fromCol.field;
  850. self.aggLabelFilter = fromCol.aggLabelFilter;
  851. self.visible = fromCol.visible;
  852. self.sortable = fromCol.sortable;
  853. self.resizable = fromCol.resizable;
  854. self.pinnable = fromCol.pinnable;
  855. self.pinned = fromCol.pinned;
  856. self.originalIndex = fromCol.originalIndex;
  857. self.sortDirection = fromCol.sortDirection;
  858. self.sortingAlgorithm = fromCol.sortingAlgorithm;
  859. self.headerClass = fromCol.headerClass;
  860. self.headerCellTemplate = fromCol.headerCellTemplate;
  861. self.cellTemplate = fromCol.cellTemplate;
  862. self.cellEditTemplate = fromCol.cellEditTemplate;
  863. };
  864. };
  865. var ngDimension = function (options) {
  866. this.outerHeight = null;
  867. this.outerWidth = null;
  868. $.extend(this, options);
  869. };
  870. var ngDomAccessProvider = function (grid) {
  871. this.previousColumn = null;
  872. this.grid = grid;
  873. };
  874. ngDomAccessProvider.prototype.changeUserSelect = function (elm, value) {
  875. elm.css({
  876. '-webkit-touch-callout': value,
  877. '-webkit-user-select': value,
  878. '-khtml-user-select': value,
  879. '-moz-user-select': value === 'none' ? '-moz-none' : value,
  880. '-ms-user-select': value,
  881. 'user-select': value
  882. });
  883. };
  884. ngDomAccessProvider.prototype.focusCellElement = function ($scope, index) {
  885. if ($scope.selectionProvider.lastClickedRow) {
  886. var columnIndex = index !== undefined ? index : this.previousColumn;
  887. var elm = $scope.selectionProvider.lastClickedRow.clone ? $scope.selectionProvider.lastClickedRow.clone.elm : $scope.selectionProvider.lastClickedRow.elm;
  888. if (columnIndex !== undefined && elm) {
  889. var columns = angular.element(elm[0].children).filter(function () { return this.nodeType !== 8; }); //Remove html comments for IE8
  890. var i = Math.max(Math.min($scope.renderedColumns.length - 1, columnIndex), 0);
  891. if (this.grid.config.showSelectionCheckbox && angular.element(columns[i]).scope() && angular.element(columns[i]).scope().col.index === 0) {
  892. i = 1; //don't want to focus on checkbox
  893. }
  894. if (columns[i]) {
  895. columns[i].children[1].children[0].focus();
  896. }
  897. this.previousColumn = columnIndex;
  898. }
  899. }
  900. };
  901. ngDomAccessProvider.prototype.selectionHandlers = function ($scope, elm) {
  902. var doingKeyDown = false;
  903. var self = this;
  904. function keydown (evt) {
  905. if (evt.keyCode === 16) { //shift key
  906. self.changeUserSelect(elm, 'none', evt);
  907. return true;
  908. } else if (!doingKeyDown) {
  909. doingKeyDown = true;
  910. var ret = ngMoveSelectionHandler($scope, elm, evt, self.grid);
  911. doingKeyDown = false;
  912. return ret;
  913. }
  914. return true;
  915. }
  916. elm.bind('keydown', keydown);
  917. function keyup (evt) {
  918. if (evt.keyCode === 16) { //shift key
  919. self.changeUserSelect(elm, 'text', evt);
  920. }
  921. return true;
  922. }
  923. elm.bind('keyup', keyup);
  924. elm.on('$destroy', function() {
  925. elm.off('keydown', keydown);
  926. elm.off('keyup', keyup);
  927. });
  928. };
  929. var ngEventProvider = function (grid, $scope, domUtilityService, $timeout) {
  930. var self = this;
  931. // The init method gets called during the ng-grid directive execution.
  932. self.colToMove = undefined;
  933. self.groupToMove = undefined;
  934. self.assignEvents = function() {
  935. // Here we set the onmousedown event handler to the header container.
  936. if (grid.config.jqueryUIDraggable && !grid.config.enablePinning) {
  937. grid.$groupPanel.droppable({
  938. addClasses: false,
  939. drop: function(event) {
  940. self.onGroupDrop(event);
  941. }
  942. });
  943. grid.$groupPanel.on('$destroy', function() {
  944. grid.$groupPanel = null;
  945. });
  946. } else {
  947. grid.$groupPanel.on('mousedown', self.onGroupMouseDown).on('dragover', self.dragOver).on('drop', self.onGroupDrop);
  948. grid.$topPanel.on('mousedown', '.ngHeaderScroller', self.onHeaderMouseDown).on('dragover', '.ngHeaderScroller', self.dragOver);
  949. grid.$groupPanel.on('$destroy', function() {
  950. if (grid.$groupPanel){
  951. grid.$groupPanel.off('mousedown');
  952. }
  953. grid.$groupPanel = null;
  954. });
  955. if (grid.config.enableColumnReordering) {
  956. grid.$topPanel.on('drop', '.ngHeaderScroller', self.onHeaderDrop);
  957. }
  958. grid.$topPanel.on('$destroy', function() {
  959. if (grid.$topPanel){
  960. grid.$topPanel.off('mousedown');
  961. }
  962. if (grid.config.enableColumnReordering && grid.$topPanel) {
  963. grid.$topPanel.off('drop');
  964. }
  965. grid.$topPanel = null;
  966. });
  967. }
  968. $scope.$on('$destroy', $scope.$watch('renderedColumns', function() {
  969. $timeout(self.setDraggables);
  970. }));
  971. };
  972. self.dragStart = function(evt){
  973. //FireFox requires there to be dataTransfer if you want to drag and drop.
  974. evt.dataTransfer.setData('text', ''); //cannot be empty string
  975. };
  976. self.dragOver = function(evt) {
  977. evt.preventDefault();
  978. };
  979. //For JQueryUI
  980. self.setDraggables = function() {
  981. if (!grid.config.jqueryUIDraggable) {
  982. //Fix for FireFox. Instead of using jQuery on('dragstart', function) on find, we have to use addEventListeners for each column.
  983. var columns = grid.$root.find('.ngHeaderSortColumn'); //have to iterate if using addEventListener
  984. angular.forEach(columns, function(col){
  985. if(col.className && col.className.indexOf("ngHeaderSortColumn") !== -1){
  986. col.setAttribute('draggable', 'true');
  987. //jQuery 'on' function doesn't have dataTransfer as part of event in handler unless added to event props, which is not recommended
  988. //See more here: http://api.jquery.com/category/events/event-object/
  989. if (col.addEventListener) { //IE8 doesn't have drag drop or event listeners
  990. col.addEventListener('dragstart', self.dragStart);
  991. angular.element(col).on('$destroy', function() {
  992. angular.element(col).off('dragstart', self.dragStart);
  993. col.removeEventListener('dragstart', self.dragStart);
  994. });
  995. }
  996. }
  997. });
  998. if (navigator.userAgent.indexOf("MSIE") !== -1){
  999. //call native IE dragDrop() to start dragging
  1000. var sortColumn = grid.$root.find('.ngHeaderSortColumn');
  1001. sortColumn.bind('selectstart', function () {
  1002. this.dragDrop();
  1003. return false;
  1004. });
  1005. angular.element(sortColumn).on('$destroy', function() {
  1006. sortColumn.off('selectstart');
  1007. });
  1008. }
  1009. } else {
  1010. if (grid.$root) {
  1011. grid.$root.find('.ngHeaderSortColumn').draggable({
  1012. helper: 'clone',
  1013. appendTo: 'body',
  1014. stack: 'div',
  1015. addClasses: false,
  1016. start: function(event) {
  1017. self.onHeaderMouseDown(event);
  1018. }
  1019. }).droppable({
  1020. drop: function(event) {
  1021. self.onHeaderDrop(event);
  1022. }
  1023. });
  1024. }
  1025. }
  1026. };
  1027. self.onGroupMouseDown = function(event) {
  1028. var groupItem = $(event.target);
  1029. // Get the scope from the header container
  1030. if (groupItem[0].className !== 'ngRemoveGroup') {
  1031. var groupItemScope = angular.element(groupItem).scope();
  1032. if (groupItemScope) {
  1033. // set draggable events
  1034. if (!grid.config.jqueryUIDraggable) {
  1035. groupItem.attr('draggable', 'true');
  1036. if(this.addEventListener){//IE8 doesn't have drag drop or event listeners
  1037. this.addEventListener('dragstart', self.dragStart);
  1038. angular.element(this).on('$destroy', function() {
  1039. this.removeEventListener('dragstart', self.dragStart);
  1040. });
  1041. }
  1042. if (navigator.userAgent.indexOf("MSIE") !== -1){
  1043. //call native IE dragDrop() to start dragging
  1044. groupItem.bind('selectstart', function () {
  1045. this.dragDrop();
  1046. return false;
  1047. });
  1048. groupItem.on('$destroy', function() {
  1049. groupItem.off('selectstart');
  1050. });
  1051. }
  1052. }
  1053. // Save the column for later.
  1054. self.groupToMove = { header: groupItem, groupName: groupItemScope.group, index: groupItemScope.$index };
  1055. }
  1056. } else {
  1057. self.groupToMove = undefined;
  1058. }
  1059. };
  1060. self.onGroupDrop = function(event) {
  1061. event.stopPropagation();
  1062. // clear out the colToMove object
  1063. var groupContainer;
  1064. var groupScope;
  1065. if (self.groupToMove) {
  1066. // Get the closest header to where we dropped
  1067. groupContainer = $(event.target).closest('.ngGroupElement'); // Get the scope from the header.
  1068. if (groupContainer.context.className === 'ngGroupPanel') {
  1069. $scope.configGroups.splice(self.groupToMove.index, 1);
  1070. $scope.configGroups.push(self.groupToMove.groupName);
  1071. } else {
  1072. groupScope = angular.element(groupContainer).scope();
  1073. if (groupScope) {
  1074. // If we have the same column, do nothing.
  1075. if (self.groupToMove.index !== groupScope.$index) {
  1076. // Splice the columns
  1077. $scope.configGroups.splice(self.groupToMove.index, 1);
  1078. $scope.configGroups.splice(groupScope.$index, 0, self.groupToMove.groupName);
  1079. }
  1080. }
  1081. }
  1082. self.groupToMove = undefined;
  1083. grid.fixGroupIndexes();
  1084. } else if (self.colToMove) {
  1085. if ($scope.configGroups.indexOf(self.colToMove.col) === -1) {
  1086. groupContainer = $(event.target).closest('.ngGroupElement'); // Get the scope from the header.
  1087. if (groupContainer.context.className === 'ngGroupPanel' || groupContainer.context.className === 'ngGroupPanelDescription ng-binding') {
  1088. $scope.groupBy(self.colToMove.col);
  1089. } else {
  1090. groupScope = angular.element(groupContainer).scope();
  1091. if (groupScope) {
  1092. // Splice the columns
  1093. $scope.removeGroup(groupScope.$index);
  1094. }
  1095. }
  1096. }
  1097. self.colToMove = undefined;
  1098. }
  1099. if (!$scope.$$phase) {
  1100. $scope.$apply();
  1101. }
  1102. };
  1103. //Header functions
  1104. self.onHeaderMouseDown = function(event) {
  1105. // Get the closest header container from where we clicked.
  1106. var headerContainer = $(event.target).closest('.ngHeaderSortColumn');
  1107. // Get the scope from the header container
  1108. var headerScope = angular.element(headerContainer).scope();
  1109. if (headerScope) {
  1110. // Save the column for later.
  1111. self.colToMove = { header: headerContainer, col: headerScope.col };
  1112. }
  1113. };
  1114. self.onHeaderDrop = function(event) {
  1115. if (!self.colToMove || self.colToMove.col.pinned) {
  1116. return;
  1117. }
  1118. // Get the closest header to where we dropped
  1119. var headerContainer = $(event.target).closest('.ngHeaderSortColumn');
  1120. // Get the scope from the header.
  1121. var headerScope = angular.element(headerContainer).scope();
  1122. if (headerScope) {
  1123. // If we have the same column or the target column is pinned, do nothing.
  1124. if (self.colToMove.col === headerScope.col || headerScope.col.pinned) {
  1125. return;
  1126. }
  1127. // Splice the columns
  1128. $scope.columns.splice(self.colToMove.col.index, 1);
  1129. $scope.columns.splice(headerScope.col.index, 0, self.colToMove.col);
  1130. grid.fixColumnIndexes();
  1131. // clear out the colToMove object
  1132. self.colToMove = undefined;
  1133. domUtilityService.digest($scope);
  1134. }
  1135. };
  1136. self.assignGridEventHandlers = function() {
  1137. //Chrome and firefox both need a tab index so the grid can recieve focus.
  1138. //need to give the grid a tabindex if it doesn't already have one so
  1139. //we'll just give it a tab index of the corresponding gridcache index
  1140. //that way we'll get the same result every time it is run.
  1141. //configurable within the options.
  1142. if (grid.config.tabIndex === -1) {
  1143. grid.$viewport.attr('tabIndex', domUtilityService.numberOfGrids);
  1144. domUtilityService.numberOfGrids++;
  1145. } else {
  1146. grid.$viewport.attr('tabIndex', grid.config.tabIndex);
  1147. }
  1148. // resize on window resize
  1149. var windowThrottle;
  1150. var windowResize = function(){
  1151. clearTimeout(windowThrottle);
  1152. windowThrottle = setTimeout(function() {
  1153. //in function for IE8 compatibility
  1154. domUtilityService.RebuildGrid($scope,grid);
  1155. }, 100);
  1156. };
  1157. $(window).on('resize.nggrid', windowResize);
  1158. // resize on parent resize as well.
  1159. var parentThrottle;
  1160. var parentResize = function() {
  1161. clearTimeout(parentThrottle);
  1162. parentThrottle = setTimeout(function() {
  1163. //in function for IE8 compatibility
  1164. domUtilityService.RebuildGrid($scope,grid);
  1165. }, 100);
  1166. };
  1167. $(grid.$root.parent()).on('resize.nggrid', parentResize);
  1168. $scope.$on('$destroy', function(){
  1169. $(window).off('resize.nggrid', windowResize);
  1170. // $(grid.$root.parent()).off('resize.nggrid', parentResize);
  1171. });
  1172. };
  1173. // In this example we want to assign grid events.
  1174. self.assignGridEventHandlers();
  1175. self.assignEvents();
  1176. };
  1177. var ngFooter = function ($scope, grid) {
  1178. $scope.maxRows = function () {
  1179. var ret = Math.max($scope.totalServerItems, grid.data.length);
  1180. return ret;
  1181. };
  1182. $scope.$on('$destroy', $scope.$watch('totalServerItems',function(n,o){
  1183. $scope.currentMaxPages = $scope.maxPages();
  1184. }));
  1185. $scope.multiSelect = (grid.config.enableRowSelection && grid.config.multiSelect);
  1186. $scope.selectedItemCount = grid.selectedItemCount;
  1187. $scope.maxPages = function () {
  1188. if($scope.maxRows() === 0) {
  1189. return 1;
  1190. }
  1191. return Math.ceil($scope.maxRows() / $scope.pagingOptions.pageSize);
  1192. };
  1193. $scope.pageForward = function() {
  1194. var page = $scope.pagingOptions.currentPage;
  1195. if ($scope.totalServerItems > 0) {
  1196. $scope.pagingOptions.currentPage = Math.min(page + 1, $scope.maxPages());
  1197. } else {
  1198. $scope.pagingOptions.currentPage++;
  1199. }
  1200. };
  1201. $scope.pageBackward = function() {
  1202. var page = $scope.pagingOptions.currentPage;
  1203. $scope.pagingOptions.currentPage = Math.max(page - 1, 1);
  1204. };
  1205. $scope.pageToFirst = function() {
  1206. $scope.pagingOptions.currentPage = 1;
  1207. };
  1208. $scope.pageToLast = function() {
  1209. var maxPages = $scope.maxPages();
  1210. $scope.pagingOptions.currentPage = maxPages;
  1211. };
  1212. $scope.cantPageForward = function() {
  1213. var curPage = $scope.pagingOptions.currentPage;
  1214. var maxPages = $scope.maxPages();
  1215. if ($scope.totalServerItems > 0) {
  1216. return curPage >= maxPages;
  1217. } else {
  1218. return grid.data.length < 1;
  1219. }
  1220. };
  1221. $scope.cantPageToLast = function() {
  1222. if ($scope.totalServerItems > 0) {
  1223. return $scope.cantPageForward();
  1224. } else {
  1225. return true;
  1226. }
  1227. };
  1228. $scope.cantPageBackward = function() {
  1229. var curPage = $scope.pagingOptions.currentPage;
  1230. return curPage <= 1;
  1231. };
  1232. };
  1233. /// <reference path="footer.js" />
  1234. /// <reference path="../services/SortService.js" />
  1235. /// <reference path="../../lib/jquery-1.8.2.min" />
  1236. var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse, $http, $q) {
  1237. var defaults = {
  1238. //Define an aggregate template to customize the rows when grouped. See github wiki for more details.
  1239. aggregateTemplate: undefined,
  1240. //Callback for when you want to validate something after selection.
  1241. afterSelectionChange: function() {
  1242. },
  1243. /* Callback if you want to inspect something before selection,
  1244. return false if you want to cancel the selection. return true otherwise.
  1245. If you need to wait for an async call to proceed with selection you can
  1246. use rowItem.changeSelection(event) method after returning false initially.
  1247. Note: when shift+ Selecting multiple items in the grid this will only get called
  1248. once and the rowItem will be an array of items that are queued to be selected. */
  1249. beforeSelectionChange: function() {
  1250. return true;
  1251. },
  1252. //checkbox templates.
  1253. checkboxCellTemplate: undefined,
  1254. checkboxHeaderTemplate: undefined,
  1255. //definitions of columns as an array [], if not defines columns are auto-generated. See github wiki for more details.
  1256. columnDefs: undefined,
  1257. //*Data being displayed in the grid. Each item in the array is mapped to a row being displayed.
  1258. data: [],
  1259. //Data updated callback, fires every time the data is modified from outside the grid.
  1260. dataUpdated: function() {
  1261. },
  1262. //Enables cell editing.
  1263. enableCellEdit: false,
  1264. //Enables cell editing on focus
  1265. enableCellEditOnFocus: false,
  1266. //Enables cell selection.
  1267. enableCellSelection: false,
  1268. //Enable or disable resizing of columns
  1269. enableColumnResize: false,
  1270. //Enable or disable reordering of columns
  1271. enableColumnReordering: false,
  1272. //Enable or disable HEAVY column virtualization. This turns off selection checkboxes and column pinning and is designed for spreadsheet-like data.
  1273. enableColumnHeavyVirt: false,
  1274. //Enables the server-side paging feature
  1275. enablePaging: false,
  1276. //Enable column pinning
  1277. enablePinning: false,
  1278. //To be able to have selectable rows in grid.
  1279. enableRowSelection: true,
  1280. //Enables or disables sorting in grid.
  1281. enableSorting: true,
  1282. //Enables or disables text highlighting in grid by adding the "unselectable" class (See CSS file)
  1283. enableHighlighting: false,
  1284. // string list of properties to exclude when auto-generating columns.
  1285. excludeProperties: [],
  1286. /* filterOptions -
  1287. filterText: The text bound to the built-in search box.
  1288. useExternalFilter: Bypass internal filtering if you want to roll your own filtering mechanism but want to use builtin search box.
  1289. */
  1290. filterOptions: {
  1291. filterText: "",
  1292. useExternalFilter: false
  1293. },
  1294. //Defining the height of the footer in pixels.
  1295. footerRowHeight: 55,
  1296. // the template for the column menu and filter, including the button.
  1297. footerTemplate: undefined,
  1298. // Enables a trade off between refreshing the contents of the grid continuously while scrolling (behaviour when true)
  1299. // and keeping the scroll bar button responsive at the expense of refreshing grid contents (behaviour when false)
  1300. forceSyncScrolling: true,
  1301. //Initial fields to group data by. Array of field names, not displayName.
  1302. groups: [],
  1303. // set the initial state of aggreagate grouping. "true" means they will be collapsed when grouping changes, "false" means they will be expanded by default.
  1304. groupsCollapsedByDefault: true,
  1305. //The height of the header row in pixels.
  1306. headerRowHeight: 30,
  1307. //Define a header row template for further customization. See github wiki for more details.
  1308. headerRowTemplate: undefined,
  1309. /*Enables the use of jquery UI reaggable/droppable plugin. requires jqueryUI to work if enabled.
  1310. Useful if you want drag + drop but your users insist on crappy browsers. */
  1311. jqueryUIDraggable: false,
  1312. //Enable the use jqueryUIThemes
  1313. jqueryUITheme: false,
  1314. //Prevent unselections when in single selection mode.
  1315. keepLastSelected: true,
  1316. /*Maintains the column widths while resizing.
  1317. Defaults to true when using *'s or undefined widths. Can be ovverriden by setting to false.*/
  1318. maintainColumnRatios: undefined,
  1319. // the template for the column menu and filter, including the button.
  1320. menuTemplate: undefined,
  1321. //Set this to false if you only want one item selected at a time
  1322. multiSelect: true,
  1323. // pagingOptions -
  1324. pagingOptions: {
  1325. // pageSizes: list of available page sizes.
  1326. pageSizes: [250, 500, 1000],
  1327. //pageSize: currently selected page size.
  1328. pageSize: 250,
  1329. //currentPage: the uhm... current page.
  1330. currentPage: 1
  1331. },
  1332. //the selection checkbox is pinned to the left side of the viewport or not.
  1333. pinSelectionCheckbox: false,
  1334. //Array of plugin functions to register in ng-grid
  1335. plugins: [],
  1336. //User defined unique ID field that allows for better handling of selections and for server-side paging
  1337. primaryKey: undefined,
  1338. //Row height of rows in grid.
  1339. rowHeight: 30,
  1340. //Define a row template to customize output. See github wiki for more details.
  1341. rowTemplate: undefined,
  1342. //all of the items selected in the grid. In single select mode there will only be one item in the array.
  1343. selectedItems: [],
  1344. //Disable row selections by clicking on the row and only when the checkbox is clicked.
  1345. selectWithCheckboxOnly: false,
  1346. /*Enables menu to choose which columns to display and group by.
  1347. If both showColumnMenu and showFilter are false the menu button will not display.*/
  1348. showColumnMenu: false,
  1349. /*Enables display of the filterbox in the column menu.
  1350. If both showColumnMenu and showFilter are false the menu button will not display.*/
  1351. showFilter: false,
  1352. //Show or hide the footer alltogether the footer is enabled by default
  1353. showFooter: false,
  1354. //Show the dropzone for drag and drop grouping
  1355. showGroupPanel: false,
  1356. //Row selection check boxes appear as the first column.
  1357. showSelectionCheckbox: false,
  1358. /*Define a sortInfo object to specify a default sorting state.
  1359. You can also observe this variable to utilize server-side sorting (see useExternalSorting).
  1360. Syntax is sortinfo: { fields: ['fieldName1',' fieldName2'], direction: 'ASC'/'asc' || 'desc'/'DESC'}*/
  1361. sortInfo: {fields: [], columns: [], directions: [] },
  1362. //Set the tab index of the Vieport.
  1363. tabIndex: -1,
  1364. //totalServerItems: Total items are on the server.
  1365. totalServerItems: 0,
  1366. /*Prevents the internal sorting from executing.
  1367. The sortInfo object will be updated with the sorting information so you can handle sorting (see sortInfo)*/
  1368. useExternalSorting: false,
  1369. /*i18n language support. choose from the installed or included languages, en, fr, sp, etc...*/
  1370. i18n: 'en',
  1371. //the threshold in rows to force virtualization on
  1372. virtualizationThreshold: 50,
  1373. // Don't handle tabs, so they can be used to navigate between controls.
  1374. noTabInterference: false
  1375. },
  1376. self = this;
  1377. self.maxCanvasHt = 0;
  1378. //self vars
  1379. self.config = $.extend(defaults, window.ngGrid.config, options);
  1380. // override conflicting settings
  1381. self.config.showSelectionCheckbox = (self.config.showSelectionCheckbox && self.config.enableColumnHeavyVirt === false);
  1382. self.config.enablePinning = (self.config.enablePinning && self.config.enableColumnHeavyVirt === false);
  1383. self.config.selectWithCheckboxOnly = (self.config.selectWithCheckboxOnly && self.config.showSelectionCheckbox !== false);
  1384. self.config.pinSelectionCheckbox = self.config.enablePinning;
  1385. if (typeof options.columnDefs === "string") {
  1386. self.config.columnDefs = $scope.$eval(options.columnDefs);
  1387. }
  1388. self.rowCache = [];
  1389. self.rowMap = [];
  1390. self.gridId = "ng" + $utils.newId();
  1391. self.$root = null; //this is the root element that is passed in with the binding handler
  1392. self.$groupPanel = null;
  1393. self.$topPanel = null;
  1394. self.$headerContainer = null;
  1395. self.$headerScroller = null;
  1396. self.$headers = null;
  1397. self.$viewport = null;
  1398. self.$canvas = null;
  1399. self.rootDim = self.config.gridDim;
  1400. self.data = [];
  1401. self.lateBindColumns = false;
  1402. self.filteredRows = [];
  1403. self.initTemplates = function() {
  1404. var templates = ['rowTemplate', 'aggregateTemplate', 'headerRowTemplate', 'checkboxCellTemplate', 'checkboxHeaderTemplate', 'menuTemplate', 'footerTemplate'];
  1405. var promises = [];
  1406. angular.forEach(templates, function(template) {
  1407. promises.push( self.getTemplate(template) );
  1408. });
  1409. return $q.all(promises);
  1410. };
  1411. //Templates
  1412. // test templates for urls and get the tempaltes via synchronous ajax calls
  1413. self.getTemplate = function (key) {
  1414. var t = self.config[key];
  1415. var uKey = self.gridId + key + ".html";
  1416. var p = $q.defer();
  1417. if (t && !TEMPLATE_REGEXP.test(t)) {
  1418. $http.get(t, {
  1419. cache: $templateCache
  1420. })
  1421. .success(function(data){
  1422. $templateCache.put(uKey, data);
  1423. p.resolve();
  1424. })
  1425. .error(function(err){
  1426. p.reject("Could not load template: " + t);
  1427. });
  1428. } else if (t) {
  1429. $templateCache.put(uKey, t);
  1430. p.resolve();
  1431. } else {
  1432. var dKey = key + ".html";
  1433. $templateCache.put(uKey, $templateCache.get(dKey));
  1434. p.resolve();
  1435. }
  1436. return p.promise;
  1437. };
  1438. if (typeof self.config.data === "object") {
  1439. self.data = self.config.data; // we cannot watch for updates if you don't pass the string name
  1440. }
  1441. self.calcMaxCanvasHeight = function() {
  1442. var calculatedHeight;
  1443. if(self.config.groups.length > 0){
  1444. calculatedHeight = self.rowFactory.parsedData.filter(function(e) {
  1445. return !e[NG_HIDDEN];
  1446. }).length * self.config.rowHeight;
  1447. } else {
  1448. calculatedHeight = self.filteredRows.length * self.config.rowHeight;
  1449. }
  1450. return calculatedHeight;
  1451. };
  1452. self.elementDims = {
  1453. scrollW: 0,
  1454. scrollH: 0,
  1455. rowIndexCellW: 25,
  1456. rowSelectedCellW: 25,
  1457. rootMaxW: 0,
  1458. rootMaxH: 0
  1459. };
  1460. //self funcs
  1461. self.setRenderedRows = function (newRows) {
  1462. $scope.renderedRows.length = newRows.length;
  1463. for (var i = 0; i < newRows.length; i++) {
  1464. if (!$scope.renderedRows[i] || (newRows[i].isAggRow || $scope.renderedRows[i].isAggRow)) {
  1465. $scope.renderedRows[i] = newRows[i].copy();
  1466. $scope.renderedRows[i].collapsed = newRows[i].collapsed;
  1467. if (!newRows[i].isAggRow) {
  1468. $scope.renderedRows[i].setVars(newRows[i]);
  1469. }
  1470. } else {
  1471. $scope.renderedRows[i].setVars(newRows[i]);
  1472. }
  1473. $scope.renderedRows[i].rowIndex = newRows[i].rowIndex;
  1474. $scope.renderedRows[i].offsetTop = newRows[i].offsetTop;
  1475. $scope.renderedRows[i].selected = newRows[i].selected;
  1476. newRows[i].renderedRowIndex = i;
  1477. }
  1478. self.refreshDomSizes();
  1479. $scope.$emit('ngGridEventRows', newRows);
  1480. };
  1481. self.minRowsToRender = function() {
  1482. var viewportH = $scope.viewportDimHeight() || 1;
  1483. return Math.floor(viewportH / self.config.rowHeight);
  1484. };
  1485. self.refreshDomSizes = function() {
  1486. var dim = new ngDimension();
  1487. dim.outerWidth = self.elementDims.rootMaxW;
  1488. dim.outerHeight = self.elementDims.rootMaxH;
  1489. self.rootDim = dim;
  1490. self.maxCanvasHt = self.calcMaxCanvasHeight();
  1491. };
  1492. self.buildColumnDefsFromData = function () {
  1493. self.config.columnDefs = [];
  1494. var item = self.data[0];
  1495. if (!item) {
  1496. self.lateBoundColumns = true;
  1497. return;
  1498. }
  1499. $utils.forIn(item, function (prop, propName) {
  1500. if (self.config.excludeProperties.indexOf(propName) === -1) {
  1501. self.config.columnDefs.push({
  1502. field: propName
  1503. });
  1504. }
  1505. });
  1506. };
  1507. self.buildColumns = function() {
  1508. var columnDefs = self.config.columnDefs,
  1509. cols = [];
  1510. if (!columnDefs) {
  1511. self.buildColumnDefsFromData();
  1512. columnDefs = self.config.columnDefs;
  1513. }
  1514. if (self.config.showSelectionCheckbox) {
  1515. cols.push(new ngColumn({
  1516. colDef: {
  1517. field: '\u2714',
  1518. width: self.elementDims.rowSelectedCellW,
  1519. sortable: false,
  1520. resizable: false,
  1521. groupable: false,
  1522. headerCellTemplate: $templateCache.get($scope.gridId + 'checkboxHeaderTemplate.html'),
  1523. cellTemplate: $templateCache.get($scope.gridId + 'checkboxCellTemplate.html'),
  1524. pinned: self.config.pinSelectionCheckbox
  1525. },
  1526. index: 0,
  1527. headerRowHeight: self.config.headerRowHeight,
  1528. sortCallback: self.sortData,
  1529. resizeOnDataCallback: self.resizeOnData,
  1530. enableResize: self.config.enableColumnResize,
  1531. enableSort: self.config.enableSorting,
  1532. enablePinning: self.config.enablePinning
  1533. }, $scope, self, domUtilityService, $templateCache, $utils));
  1534. }
  1535. if (columnDefs.length > 0) {
  1536. var checkboxOffset = self.config.showSelectionCheckbox ? 1 : 0;
  1537. var groupOffset = $scope.configGroups.length;
  1538. $scope.configGroups.length = 0;
  1539. angular.forEach(columnDefs, function(colDef, i) {
  1540. i += checkboxOffset;
  1541. var column = new ngColumn({
  1542. colDef: colDef,
  1543. index: i + groupOffset,
  1544. originalIndex: i,
  1545. headerRowHeight: self.config.headerRowHeight,
  1546. sortCallback: self.sortData,
  1547. resizeOnDataCallback: self.resizeOnData,
  1548. enableResize: self.config.enableColumnResize,
  1549. enableSort: self.config.enableSorting,
  1550. enablePinning: self.config.enablePinning,
  1551. enableCellEdit: self.config.enableCellEdit || self.config.enableCellEditOnFocus,
  1552. cellEditableCondition: self.config.cellEditableCondition
  1553. }, $scope, self, domUtilityService, $templateCache, $utils);
  1554. var indx = self.config.groups.indexOf(colDef.field);
  1555. if (indx !== -1) {
  1556. column.isGroupedBy = true;
  1557. $scope.configGroups.splice(indx, 0, column);
  1558. column.groupIndex = $scope.configGroups.length;
  1559. }
  1560. cols.push(column);
  1561. });
  1562. $scope.columns = cols;
  1563. if (self.config.groups.length > 0) {
  1564. self.rowFactory.getGrouping(self.config.groups);
  1565. }
  1566. }
  1567. };
  1568. self.configureColumnWidths = function() {
  1569. var asterisksArray = [],
  1570. percentArray = [],
  1571. asteriskNum = 0,
  1572. totalWidth = 0;
  1573. // When rearranging columns, their index in $scope.columns will no longer match the original column order from columnDefs causing
  1574. // their width config to be out of sync. We can use "originalIndex" on the ngColumns to get hold of the correct setup from columnDefs, but to
  1575. // avoid O(n) lookups in $scope.columns per column we setup a map.
  1576. var indexMap = {};
  1577. // Build a map of columnDefs column indices -> ngColumn indices (via the "originalIndex" property on ngColumns).
  1578. angular.forEach($scope.columns, function(ngCol, i) {
  1579. // Disregard columns created by grouping (the grouping columns don't match a column from columnDefs)
  1580. if (!$utils.isNullOrUndefined(ngCol.originalIndex)) {
  1581. var origIndex = ngCol.originalIndex;
  1582. if (self.config.showSelectionCheckbox) {
  1583. //if visible, takes up 25 pixels
  1584. if(ngCol.originalIndex === 0 && ngCol.visible){
  1585. totalWidth += 25;
  1586. }
  1587. // The originalIndex will be offset 1 when including the selection column
  1588. origIndex--;
  1589. }
  1590. indexMap[origIndex] = i;
  1591. }
  1592. });
  1593. angular.forEach(self.config.columnDefs, function(colDef, i) {
  1594. // Get the ngColumn that matches the current column from columnDefs
  1595. var ngColumn = $scope.columns[indexMap[i]];
  1596. colDef.index = i;
  1597. var isPercent = false, t;
  1598. //if width is not defined, set it to a single star
  1599. if ($utils.isNullOrUndefined(colDef.width)) {
  1600. colDef.width = "*";
  1601. } else { // get column width
  1602. isPercent = isNaN(colDef.width) ? $utils.endsWith(colDef.width, "%") : false;
  1603. t = isPercent ? colDef.width : parseInt(colDef.width, 10);
  1604. }
  1605. // check if it is a number
  1606. if (isNaN(t) && !$scope.hasUserChangedGridColumnWidths) {
  1607. t = colDef.width;
  1608. // figure out if the width is defined or if we need to calculate it
  1609. if (t === 'auto') { // set it for now until we have data and subscribe when it changes so we can set the width.
  1610. ngColumn.width = ngColumn.minWidth;
  1611. totalWidth += ngColumn.width;
  1612. var temp = ngColumn;
  1613. $scope.$on('$destroy', $scope.$on("ngGridEventData", function () {
  1614. self.resizeOnData(temp);
  1615. }));
  1616. return;
  1617. } else if (t.indexOf("*") !== -1) { // we need to save it until the end to do the calulations on the remaining width.
  1618. if (ngColumn.visible !== false) {
  1619. asteriskNum += t.length;
  1620. }
  1621. asterisksArray.push(colDef);
  1622. return;
  1623. } else if (isPercent) { // If the width is a percentage, save it until the very last.
  1624. percentArray.push(colDef);
  1625. return;
  1626. } else { // we can't parse the width so lets throw an error.
  1627. throw "unable to parse column width, use percentage (\"10%\",\"20%\", etc...) or \"*\" to use remaining width of grid";
  1628. }
  1629. } else if (ngColumn.visible !== false) {
  1630. totalWidth += ngColumn.width = parseInt(ngColumn.width, 10);
  1631. }
  1632. });
  1633. // Now we check if we saved any percentage columns for calculating last
  1634. if (percentArray.length > 0) {
  1635. //If they specificy for maintain column ratios to be false in grid config, then it will remain false. If not specifiied or true, will be true.
  1636. self.config.maintainColumnRatios = self.config.maintainColumnRatios !== false;
  1637. // If any columns with % widths have been hidden, then let other % based columns use their width
  1638. var percentWidth = 0; // The total % value for all columns setting their width using % (will e.g. be 40 for 2 columns with 20% each)
  1639. var hiddenPercent = 0; // The total % value for all columns setting their width using %, but which have been hidden
  1640. angular.forEach(percentArray, function(colDef) {
  1641. // Get the ngColumn that matches the current column from columnDefs
  1642. var ngColumn = $scope.columns[indexMap[colDef.index]];
  1643. var percent = parseFloat(colDef.width) / 100;
  1644. percentWidth += percent;
  1645. if (!ngColumn.visible) {
  1646. hiddenPercent += percent;
  1647. }
  1648. });
  1649. var percentWidthUsed = percentWidth - hiddenPercent;
  1650. // do the math
  1651. angular.forEach(percentArray, function(colDef) {
  1652. // Get the ngColumn that matches the current column from columnDefs
  1653. var ngColumn = $scope.columns[indexMap[colDef.index]];
  1654. // Calc the % relative to the amount of % reserved for the visible columns (that use % based widths)
  1655. var percent = parseFloat(colDef.width) / 100;
  1656. if (hiddenPercent > 0) {
  1657. percent = percent / percentWidthUsed;
  1658. }
  1659. else {
  1660. percent = percent / percentWidth;
  1661. }
  1662. var pixelsForPercentBasedWidth = self.rootDim.outerWidth * percentWidth;
  1663. ngColumn.width = pixelsForPercentBasedWidth * percent;
  1664. totalWidth += ngColumn.width;
  1665. });
  1666. }
  1667. // check if we saved any asterisk columns for calculating later
  1668. if (asterisksArray.length > 0) {
  1669. //If they specificy for maintain column ratios to be false in grid config, then it will remain false. If not specifiied or true, will be true.
  1670. self.config.maintainColumnRatios = self.config.maintainColumnRatios !== false;
  1671. // get the remaining width
  1672. var remainingWidth = self.rootDim.outerWidth - totalWidth;
  1673. // are we overflowing vertically?
  1674. if (self.maxCanvasHt > $scope.viewportDimHeight()) {
  1675. //compensate for scrollbar
  1676. remainingWidth -= domUtilityService.ScrollW;
  1677. }
  1678. // calculate the weight of each asterisk rounded down
  1679. var asteriskVal = Math.floor(remainingWidth / asteriskNum);
  1680. // set the width of each column based on the number of stars
  1681. angular.forEach(asterisksArray, function(colDef, i) {
  1682. // Get the ngColumn that matches the current column from columnDefs
  1683. var ngColumn = $scope.columns[indexMap[colDef.index]];
  1684. ngColumn.width = asteriskVal * colDef.width.length;
  1685. if (ngColumn.visible !== false) {
  1686. totalWidth += ngColumn.width;
  1687. }
  1688. var isLast = (i === (asterisksArray.length - 1));
  1689. //if last asterisk and doesn't fill width of grid, add the difference
  1690. if(isLast && totalWidth < self.rootDim.outerWidth){
  1691. var gridWidthDifference = self.rootDim.outerWidth - totalWidth;
  1692. if(self.maxCanvasHt > $scope.viewportDimHeight()){
  1693. gridWidthDifference -= domUtilityService.ScrollW;
  1694. }
  1695. ngColumn.width += gridWidthDifference;
  1696. }
  1697. });
  1698. }
  1699. };
  1700. self.init = function() {
  1701. return self.initTemplates().then(function(){
  1702. //factories and services
  1703. $scope.selectionProvider = new ngSelectionProvider(self, $scope, $parse);
  1704. $scope.domAccessProvider = new ngDomAccessProvider(self);
  1705. self.rowFactory = new ngRowFactory(self, $scope, domUtilityService, $templateCache, $utils);
  1706. self.searchProvider = new ngSearchProvider($scope, self, $filter);
  1707. self.styleProvider = new ngStyleProvider($scope, self);
  1708. $scope.$on('$destroy', $scope.$watch('configGroups', function(a) {
  1709. var tempArr = [];
  1710. angular.forEach(a, function(item) {
  1711. tempArr.push(item.field || item);
  1712. });
  1713. self.config.groups = tempArr;
  1714. self.rowFactory.filteredRowsChanged();
  1715. $scope.$emit('ngGridEventGroups', a);
  1716. }, true));
  1717. $scope.$on('$destroy', $scope.$watch('columns', function (a) {
  1718. if(!$scope.isColumnResizing){
  1719. domUtilityService.RebuildGrid($scope, self);
  1720. }
  1721. $scope.$emit('ngGridEventColumns', a);
  1722. }, true));
  1723. $scope.$on('$destroy', $scope.$watch(function() {
  1724. return options.i18n;
  1725. }, function(newLang) {
  1726. $utils.seti18n($scope, newLang);
  1727. }));
  1728. self.maxCanvasHt = self.calcMaxCanvasHeight();
  1729. if (self.config.sortInfo.fields && self.config.sortInfo.fields.length > 0) {
  1730. $scope.$on('$destroy', $scope.$watch(function() {
  1731. return self.config.sortInfo;
  1732. }, function(sortInfo){
  1733. if (!sortService.isSorting) {
  1734. self.sortColumnsInit();
  1735. $scope.$emit('ngGridEventSorted', self.config.sortInfo);
  1736. }
  1737. }, true));
  1738. }
  1739. });
  1740. // var p = $q.defer();
  1741. // p.resolve();
  1742. // return p.promise;
  1743. };
  1744. self.resizeOnData = function(col) {
  1745. // we calculate the longest data.
  1746. var longest = col.minWidth;
  1747. var arr = $utils.getElementsByClassName('col' + col.index);
  1748. angular.forEach(arr, function(elem, index) {
  1749. var i;
  1750. if (index === 0) {
  1751. var kgHeaderText = $(elem).find('.ngHeaderText');
  1752. i = $utils.visualLength(kgHeaderText) + 10; // +10 some margin
  1753. } else {
  1754. var ngCellText = $(elem).find('.ngCellText');
  1755. i = $utils.visualLength(ngCellText) + 10; // +10 some margin
  1756. }
  1757. if (i > longest) {
  1758. longest = i;
  1759. }
  1760. });
  1761. col.width = col.longest = Math.min(col.maxWidth, longest + 7); // + 7 px to make it look decent.
  1762. domUtilityService.BuildStyles($scope, self, true);
  1763. };
  1764. self.lastSortedColumns = [];
  1765. self.sortData = function(col, evt) {
  1766. if (evt && evt.shiftKey && self.config.sortInfo) {
  1767. var indx = self.config.sortInfo.columns.indexOf(col);
  1768. if (indx === -1) {
  1769. if (self.config.sortInfo.columns.length === 1) {
  1770. self.config.sortInfo.columns[0].sortPriority = 1;
  1771. }
  1772. self.config.sortInfo.columns.push(col);
  1773. col.sortPriority = self.config.sortInfo.columns.length;
  1774. self.config.sortInfo.fields.push(col.field);
  1775. self.config.sortInfo.directions.push(col.sortDirection);
  1776. self.lastSortedColumns.push(col);
  1777. } else {
  1778. self.config.sortInfo.directions[indx] = col.sortDirection;
  1779. }
  1780. } else if (!self.config.useExternalSorting || (self.config.useExternalSorting && self.config.sortInfo )) {
  1781. var isArr = $.isArray(col);
  1782. self.config.sortInfo.columns.length = 0;
  1783. self.config.sortInfo.fields.length = 0;
  1784. self.config.sortInfo.directions.length = 0;
  1785. var push = function (c) {
  1786. self.config.sortInfo.columns.push(c);
  1787. self.config.sortInfo.fields.push(c.field);
  1788. self.config.sortInfo.directions.push(c.sortDirection);
  1789. self.lastSortedColumns.push(c);
  1790. };
  1791. if (isArr) {
  1792. angular.forEach(col, function (c, i) {
  1793. c.sortPriority = i + 1;
  1794. push(c);
  1795. });
  1796. } else {
  1797. self.clearSortingData(col);
  1798. col.sortPriority = undefined;
  1799. push(col);
  1800. }
  1801. self.sortActual();
  1802. self.searchProvider.evalFilter();
  1803. $scope.$emit('ngGridEventSorted', self.config.sortInfo);
  1804. }
  1805. };
  1806. self.sortColumnsInit = function() {
  1807. if (self.config.sortInfo.columns) {
  1808. self.config.sortInfo.columns.length = 0;
  1809. } else {
  1810. self.config.sortInfo.columns = [];
  1811. }
  1812. var cols = [];
  1813. angular.forEach($scope.columns, function(c) {
  1814. var i = self.config.sortInfo.fields.indexOf(c.field);
  1815. if (i !== -1) {
  1816. c.sortDirection = self.config.sortInfo.directions[i] || 'asc';
  1817. cols[i] = c;
  1818. }
  1819. });
  1820. if(cols.length === 1){
  1821. self.sortData(cols[0]);
  1822. }else{
  1823. self.sortData(cols);
  1824. }
  1825. };
  1826. self.sortActual = function() {
  1827. if (!self.config.useExternalSorting) {
  1828. var tempData = self.data.slice(0);
  1829. angular.forEach(tempData, function(item, i) {
  1830. var e = self.rowMap[i];
  1831. if (e !== undefined) {
  1832. var v = self.rowCache[e];
  1833. if (v !== undefined) {
  1834. item.preSortSelected = v.selected;
  1835. item.preSortIndex = i;
  1836. }
  1837. }
  1838. });
  1839. sortService.Sort(self.config.sortInfo, tempData);
  1840. angular.forEach(tempData, function(item, i) {
  1841. self.rowCache[i].entity = item;
  1842. self.rowCache[i].selected = item.preSortSelected;
  1843. self.rowMap[item.preSortIndex] = i;
  1844. delete item.preSortSelected;
  1845. delete item.preSortIndex;
  1846. });
  1847. }
  1848. };
  1849. self.clearSortingData = function (col) {
  1850. if (!col) {
  1851. angular.forEach(self.lastSortedColumns, function (c) {
  1852. c.sortDirection = "";
  1853. c.sortPriority = null;
  1854. });
  1855. self.lastSortedColumns = [];
  1856. } else {
  1857. angular.forEach(self.lastSortedColumns, function (c) {
  1858. if (col.index !== c.index) {
  1859. c.sortDirection = "";
  1860. c.sortPriority = null;
  1861. }
  1862. });
  1863. self.lastSortedColumns[0] = col;
  1864. self.lastSortedColumns.length = 1;
  1865. }
  1866. };
  1867. self.fixColumnIndexes = function() {
  1868. //fix column indexes
  1869. for (var i = 0; i < $scope.columns.length; i++) {
  1870. $scope.columns[i].index = i;
  1871. }
  1872. };
  1873. self.fixGroupIndexes = function() {
  1874. angular.forEach($scope.configGroups, function(item, i) {
  1875. item.groupIndex = i + 1;
  1876. });
  1877. };
  1878. //$scope vars
  1879. $scope.elementsNeedMeasuring = true;
  1880. $scope.columns = [];
  1881. $scope.renderedRows = [];
  1882. $scope.renderedColumns = [];
  1883. $scope.headerRow = null;
  1884. $scope.rowHeight = self.config.rowHeight;
  1885. $scope.jqueryUITheme = self.config.jqueryUITheme;
  1886. $scope.showSelectionCheckbox = self.config.showSelectionCheckbox;
  1887. $scope.enableCellSelection = self.config.enableCellSelection;
  1888. $scope.enableCellEditOnFocus = self.config.enableCellEditOnFocus;
  1889. $scope.footer = null;
  1890. $scope.selectedItems = self.config.selectedItems;
  1891. $scope.multiSelect = self.config.multiSelect;
  1892. $scope.showFooter = self.config.showFooter;
  1893. $scope.footerRowHeight = $scope.showFooter ? self.config.footerRowHeight : 0;
  1894. $scope.showColumnMenu = self.config.showColumnMenu;
  1895. $scope.forceSyncScrolling = self.config.forceSyncScrolling;
  1896. $scope.showMenu = false;
  1897. $scope.configGroups = [];
  1898. $scope.gridId = self.gridId;
  1899. //Paging
  1900. $scope.enablePaging = self.config.enablePaging;
  1901. $scope.pagingOptions = self.config.pagingOptions;
  1902. //i18n support
  1903. $scope.i18n = {};
  1904. $utils.seti18n($scope, self.config.i18n);
  1905. $scope.adjustScrollLeft = function (scrollLeft) {
  1906. var colwidths = 0,
  1907. totalLeft = 0,
  1908. x = $scope.columns.length,
  1909. newCols = [],
  1910. dcv = !self.config.enableColumnHeavyVirt;
  1911. var r = 0;
  1912. var addCol = function (c) {
  1913. if (dcv) {
  1914. newCols.push(c);
  1915. } else {
  1916. if (!$scope.renderedColumns[r]) {
  1917. $scope.renderedColumns[r] = c.copy();
  1918. } else {
  1919. $scope.renderedColumns[r].setVars(c);
  1920. }
  1921. }
  1922. r++;
  1923. };
  1924. for (var i = 0; i < x; i++) {
  1925. var col = $scope.columns[i];
  1926. if (col.visible !== false) {
  1927. var w = col.width + colwidths;
  1928. if (col.pinned) {
  1929. addCol(col);
  1930. var newLeft = i > 0 ? (scrollLeft + totalLeft) : scrollLeft;
  1931. domUtilityService.setColLeft(col, newLeft, self);
  1932. totalLeft += col.width;
  1933. } else {
  1934. if (w >= scrollLeft) {
  1935. if (colwidths <= scrollLeft + self.rootDim.outerWidth) {
  1936. addCol(col);
  1937. }
  1938. }
  1939. }
  1940. colwidths += col.width;
  1941. }
  1942. }
  1943. if (dcv) {
  1944. $scope.renderedColumns = newCols;
  1945. }
  1946. };
  1947. self.prevScrollTop = 0;
  1948. self.prevScrollIndex = 0;
  1949. $scope.adjustScrollTop = function(scrollTop, force) {
  1950. if (self.prevScrollTop === scrollTop && !force) {
  1951. return;
  1952. }
  1953. if (scrollTop > 0 && self.$viewport[0].scrollHeight - scrollTop <= self.$viewport.outerHeight()) {
  1954. $scope.$emit('ngGridEventScroll');
  1955. }
  1956. var rowIndex = Math.floor(scrollTop / self.config.rowHeight);
  1957. var newRange;
  1958. if (self.filteredRows.length > self.config.virtualizationThreshold) {
  1959. // Have we hit the threshold going down?
  1960. if (self.prevScrollTop < scrollTop && rowIndex < self.prevScrollIndex + SCROLL_THRESHOLD) {
  1961. return;
  1962. }
  1963. //Have we hit the threshold going up?
  1964. if (self.prevScrollTop > scrollTop && rowIndex > self.prevScrollIndex - SCROLL_THRESHOLD) {
  1965. return;
  1966. }
  1967. newRange = new ngRange(Math.max(0, rowIndex - EXCESS_ROWS), rowIndex + self.minRowsToRender() + EXCESS_ROWS);
  1968. } else {
  1969. var maxLen = $scope.configGroups.length > 0 ? self.rowFactory.parsedData.length : self.filteredRows.length;
  1970. newRange = new ngRange(0, Math.max(maxLen, self.minRowsToRender() + EXCESS_ROWS));
  1971. }
  1972. self.prevScrollTop = scrollTop;
  1973. self.rowFactory.UpdateViewableRange(newRange);
  1974. self.prevScrollIndex = rowIndex;
  1975. };
  1976. //scope funcs
  1977. $scope.toggleShowMenu = function() {
  1978. $scope.showMenu = !$scope.showMenu;
  1979. };
  1980. $scope.toggleSelectAll = function(state, selectOnlyVisible) {
  1981. $scope.selectionProvider.toggleSelectAll(state, false, selectOnlyVisible);
  1982. };
  1983. $scope.totalFilteredItemsLength = function() {
  1984. return self.filteredRows.length;
  1985. };
  1986. $scope.showGroupPanel = function() {
  1987. return self.config.showGroupPanel;
  1988. };
  1989. $scope.topPanelHeight = function() {
  1990. return self.config.showGroupPanel === true ? self.config.headerRowHeight + 32 : self.config.headerRowHeight;
  1991. };
  1992. $scope.viewportDimHeight = function() {
  1993. return Math.max(0, self.rootDim.outerHeight - $scope.topPanelHeight() - $scope.footerRowHeight - 2);
  1994. };
  1995. $scope.groupBy = function (col) {
  1996. if (self.data.length < 1 || !col.groupable || !col.field) {
  1997. return;
  1998. }
  1999. //first sort the column
  2000. if (!col.sortDirection) {
  2001. col.sort({ shiftKey: $scope.configGroups.length > 0 ? true : false });
  2002. }
  2003. var indx = $scope.configGroups.indexOf(col);
  2004. if (indx === -1) {
  2005. col.isGroupedBy = true;
  2006. $scope.configGroups.push(col);
  2007. col.groupIndex = $scope.configGroups.length;
  2008. } else {
  2009. $scope.removeGroup(indx);
  2010. }
  2011. self.$viewport.scrollTop(0);
  2012. domUtilityService.digest($scope);
  2013. };
  2014. $scope.removeGroup = function(index) {
  2015. var col = $scope.columns.filter(function(item) {
  2016. return item.groupIndex === (index + 1);
  2017. })[0];
  2018. col.isGroupedBy = false;
  2019. col.groupIndex = 0;
  2020. if ($scope.columns[index].isAggCol) {
  2021. $scope.columns.splice(index, 1);
  2022. $scope.configGroups.splice(index, 1);
  2023. self.fixGroupIndexes();
  2024. }
  2025. if ($scope.configGroups.length === 0) {
  2026. self.fixColumnIndexes();
  2027. domUtilityService.digest($scope);
  2028. }
  2029. $scope.adjustScrollLeft(0);
  2030. };
  2031. $scope.togglePin = function (col) {
  2032. var indexFrom = col.index;
  2033. var indexTo = 0;
  2034. for (var i = 0; i < $scope.columns.length; i++) {
  2035. if (!$scope.columns[i].pinned) {
  2036. break;
  2037. }
  2038. indexTo++;
  2039. }
  2040. if (col.pinned) {
  2041. indexTo = Math.max(col.originalIndex, indexTo - 1);
  2042. }
  2043. col.pinned = !col.pinned;
  2044. // Splice the columns
  2045. $scope.columns.splice(indexFrom, 1);
  2046. $scope.columns.splice(indexTo, 0, col);
  2047. self.fixColumnIndexes();
  2048. // Finally, rebuild the CSS styles.
  2049. domUtilityService.BuildStyles($scope, self, true);
  2050. self.$viewport.scrollLeft(self.$viewport.scrollLeft() - col.width);
  2051. };
  2052. $scope.totalRowWidth = function() {
  2053. var totalWidth = 0,
  2054. cols = $scope.columns;
  2055. for (var i = 0; i < cols.length; i++) {
  2056. if (cols[i].visible !== false) {
  2057. totalWidth += cols[i].width;
  2058. }
  2059. }
  2060. return totalWidth;
  2061. };
  2062. $scope.headerScrollerDim = function() {
  2063. var viewportH = $scope.viewportDimHeight(),
  2064. maxHeight = self.maxCanvasHt,
  2065. vScrollBarIsOpen = (maxHeight > viewportH),
  2066. newDim = new ngDimension();
  2067. newDim.autoFitHeight = true;
  2068. newDim.outerWidth = $scope.totalRowWidth();
  2069. if (vScrollBarIsOpen) {
  2070. newDim.outerWidth += self.elementDims.scrollW;
  2071. } else if ((maxHeight - viewportH) <= self.elementDims.scrollH) { //if the horizontal scroll is open it forces the viewport to be smaller
  2072. newDim.outerWidth += self.elementDims.scrollW;
  2073. }
  2074. return newDim;
  2075. };
  2076. };
  2077. var ngRange = function (top, bottom) {
  2078. this.topRow = top;
  2079. this.bottomRow = bottom;
  2080. };
  2081. var ngRow = function (entity, config, selectionProvider, rowIndex, $utils) {
  2082. this.entity = entity;
  2083. this.config = config;
  2084. this.selectionProvider = selectionProvider;
  2085. this.rowIndex = rowIndex;
  2086. this.utils = $utils;
  2087. this.selected = selectionProvider.getSelection(entity);
  2088. this.cursor = this.config.enableRowSelection && !this.config.selectWithCheckboxOnly ? 'pointer' : 'default';
  2089. this.beforeSelectionChange = config.beforeSelectionChangeCallback;
  2090. this.afterSelectionChange = config.afterSelectionChangeCallback;
  2091. this.offsetTop = this.rowIndex * config.rowHeight;
  2092. this.rowDisplayIndex = 0;
  2093. };
  2094. ngRow.prototype.setSelection = function (isSelected) {
  2095. this.selectionProvider.setSelection(this, isSelected);
  2096. this.selectionProvider.lastClickedRow = this;
  2097. };
  2098. ngRow.prototype.continueSelection = function (event) {
  2099. this.selectionProvider.ChangeSelection(this, event);
  2100. };
  2101. ngRow.prototype.ensureEntity = function (expected) {
  2102. if (this.entity !== expected) {
  2103. // Update the entity and determine our selected property
  2104. this.entity = expected;
  2105. this.selected = this.selectionProvider.getSelection(this.entity);
  2106. }
  2107. };
  2108. ngRow.prototype.toggleSelected = function (event) {
  2109. if (!this.config.enableRowSelection && !this.config.enableCellSelection) {
  2110. return true;
  2111. }
  2112. var element = event.target || event;
  2113. //check and make sure its not the bubbling up of our checked 'click' event
  2114. if (element.type === "checkbox" && element.parentElement.className !== "ngSelectionCell ng-scope") {
  2115. return true;
  2116. }
  2117. if (this.config.selectWithCheckboxOnly && element.type !== "checkbox") {
  2118. this.selectionProvider.lastClickedRow = this;
  2119. return true;
  2120. }
  2121. if (this.beforeSelectionChange(this, event)) {
  2122. this.continueSelection(event);
  2123. }
  2124. return false;
  2125. };
  2126. ngRow.prototype.alternatingRowClass = function () {
  2127. var isEven = (this.rowIndex % 2) === 0;
  2128. var classes = {
  2129. 'ngRow' : true,
  2130. 'selected': this.selected,
  2131. 'even': isEven,
  2132. 'odd': !isEven,
  2133. 'ui-state-default': this.config.jqueryUITheme && isEven,
  2134. 'ui-state-active': this.config.jqueryUITheme && !isEven
  2135. };
  2136. return classes;
  2137. };
  2138. ngRow.prototype.getProperty = function (path) {
  2139. return this.utils.evalProperty(this.entity, path);
  2140. };
  2141. ngRow.prototype.copy = function () {
  2142. this.clone = new ngRow(this.entity, this.config, this.selectionProvider, this.rowIndex, this.utils);
  2143. this.clone.isClone = true;
  2144. this.clone.elm = this.elm;
  2145. this.clone.orig = this;
  2146. return this.clone;
  2147. };
  2148. ngRow.prototype.setVars = function (fromRow) {
  2149. fromRow.clone = this;
  2150. this.entity = fromRow.entity;
  2151. this.selected = fromRow.selected;
  2152. this.orig = fromRow;
  2153. };
  2154. var ngRowFactory = function (grid, $scope, domUtilityService, $templateCache, $utils) {
  2155. var self = this;
  2156. // we cache rows when they are built, and then blow the cache away when sorting
  2157. self.aggCache = {};
  2158. self.parentCache = []; // Used for grouping and is cleared each time groups are calulated.
  2159. self.dataChanged = true;
  2160. self.parsedData = [];
  2161. self.rowConfig = {};
  2162. self.selectionProvider = $scope.selectionProvider;
  2163. self.rowHeight = 30;
  2164. self.numberOfAggregates = 0;
  2165. self.groupedData = undefined;
  2166. self.rowHeight = grid.config.rowHeight;
  2167. self.rowConfig = {
  2168. enableRowSelection: grid.config.enableRowSelection,
  2169. rowClasses: grid.config.rowClasses,
  2170. selectedItems: $scope.selectedItems,
  2171. selectWithCheckboxOnly: grid.config.selectWithCheckboxOnly,
  2172. beforeSelectionChangeCallback: grid.config.beforeSelectionChange,
  2173. afterSelectionChangeCallback: grid.config.afterSelectionChange,
  2174. jqueryUITheme: grid.config.jqueryUITheme,
  2175. enableCellSelection: grid.config.enableCellSelection,
  2176. rowHeight: grid.config.rowHeight
  2177. };
  2178. self.renderedRange = new ngRange(0, grid.minRowsToRender() + EXCESS_ROWS);
  2179. // @entity - the data item
  2180. // @rowIndex - the index of the row
  2181. self.buildEntityRow = function(entity, rowIndex) {
  2182. // build the row
  2183. return new ngRow(entity, self.rowConfig, self.selectionProvider, rowIndex, $utils);
  2184. };
  2185. self.buildAggregateRow = function(aggEntity, rowIndex) {
  2186. var agg = self.aggCache[aggEntity.aggIndex]; // first check to see if we've already built it
  2187. if (!agg) {
  2188. // build the row
  2189. agg = new ngAggregate(aggEntity, self, self.rowConfig.rowHeight, grid.config.groupsCollapsedByDefault);
  2190. self.aggCache[aggEntity.aggIndex] = agg;
  2191. }
  2192. agg.rowIndex = rowIndex;
  2193. agg.offsetTop = rowIndex * self.rowConfig.rowHeight;
  2194. return agg;
  2195. };
  2196. self.UpdateViewableRange = function(newRange) {
  2197. self.renderedRange = newRange;
  2198. self.renderedChange();
  2199. };
  2200. self.filteredRowsChanged = function() {
  2201. // check for latebound autogenerated columns
  2202. if (grid.lateBoundColumns && grid.filteredRows.length > 0) {
  2203. grid.config.columnDefs = undefined;
  2204. grid.buildColumns();
  2205. grid.lateBoundColumns = false;
  2206. $scope.$evalAsync(function() {
  2207. $scope.adjustScrollLeft(0);
  2208. });
  2209. }
  2210. self.dataChanged = true;
  2211. if (grid.config.groups.length > 0) {
  2212. self.getGrouping(grid.config.groups);
  2213. }
  2214. self.UpdateViewableRange(self.renderedRange);
  2215. };
  2216. self.renderedChange = function() {
  2217. if (!self.groupedData || grid.config.groups.length < 1) {
  2218. self.renderedChangeNoGroups();
  2219. grid.refreshDomSizes();
  2220. return;
  2221. }
  2222. self.wasGrouped = true;
  2223. self.parentCache = [];
  2224. var x = 0;
  2225. var temp = self.parsedData.filter(function (e) {
  2226. if (e.isAggRow) {
  2227. if (e.parent && e.parent.collapsed) {
  2228. return false;
  2229. }
  2230. return true;
  2231. }
  2232. if (!e[NG_HIDDEN]) {
  2233. e.rowIndex = x++;
  2234. }
  2235. return !e[NG_HIDDEN];
  2236. });
  2237. self.totalRows = temp.length;
  2238. var rowArr = [];
  2239. for (var i = self.renderedRange.topRow; i < self.renderedRange.bottomRow; i++) {
  2240. if (temp[i]) {
  2241. temp[i].offsetTop = i * grid.config.rowHeight;
  2242. rowArr.push(temp[i]);
  2243. }
  2244. }
  2245. grid.setRenderedRows(rowArr);
  2246. };
  2247. self.renderedChangeNoGroups = function () {
  2248. var rowArr = [];
  2249. for (var i = self.renderedRange.topRow; i < self.renderedRange.bottomRow; i++) {
  2250. if (grid.filteredRows[i]) {
  2251. grid.filteredRows[i].rowIndex = i;
  2252. grid.filteredRows[i].offsetTop = i * grid.config.rowHeight;
  2253. rowArr.push(grid.filteredRows[i]);
  2254. }
  2255. }
  2256. grid.setRenderedRows(rowArr);
  2257. };
  2258. self.fixRowCache = function () {
  2259. var newLen = grid.data.length;
  2260. var diff = newLen - grid.rowCache.length;
  2261. if (diff < 0) {
  2262. grid.rowCache.length = grid.rowMap.length = newLen;
  2263. } else {
  2264. for (var i = grid.rowCache.length; i < newLen; i++) {
  2265. grid.rowCache[i] = grid.rowFactory.buildEntityRow(grid.data[i], i);
  2266. }
  2267. }
  2268. };
  2269. //magical recursion. it works. I swear it. I figured it out in the shower one day.
  2270. self.parseGroupData = function(g) {
  2271. if (g.values) {
  2272. for (var x = 0; x < g.values.length; x++){
  2273. // get the last parent in the array because that's where our children want to be
  2274. self.parentCache[self.parentCache.length - 1].children.push(g.values[x]);
  2275. //add the row to our return array
  2276. self.parsedData.push(g.values[x]);
  2277. }
  2278. } else {
  2279. for (var prop in g) {
  2280. // exclude the meta properties.
  2281. if (prop === NG_FIELD || prop === NG_DEPTH || prop === NG_COLUMN) {
  2282. continue;
  2283. } else if (g.hasOwnProperty(prop)) {
  2284. //build the aggregate row
  2285. var agg = self.buildAggregateRow({
  2286. gField: g[NG_FIELD],
  2287. gLabel: prop,
  2288. gDepth: g[NG_DEPTH],
  2289. isAggRow: true,
  2290. '_ng_hidden_': false,
  2291. children: [],
  2292. aggChildren: [],
  2293. aggIndex: self.numberOfAggregates,
  2294. aggLabelFilter: g[NG_COLUMN].aggLabelFilter
  2295. }, 0);
  2296. self.numberOfAggregates++;
  2297. //set the aggregate parent to the parent in the array that is one less deep.
  2298. agg.parent = self.parentCache[agg.depth - 1];
  2299. // if we have a parent, set the parent to not be collapsed and append the current agg to its children
  2300. if (agg.parent) {
  2301. agg.parent.collapsed = false;
  2302. agg.parent.aggChildren.push(agg);
  2303. }
  2304. // add the aggregate row to the parsed data.
  2305. self.parsedData.push(agg);
  2306. // the current aggregate now the parent of the current depth
  2307. self.parentCache[agg.depth] = agg;
  2308. // dig deeper for more aggregates or children.
  2309. self.parseGroupData(g[prop]);
  2310. }
  2311. }
  2312. }
  2313. };
  2314. //Shuffle the data into their respective groupings.
  2315. self.getGrouping = function(groups) {
  2316. self.aggCache = [];
  2317. self.numberOfAggregates = 0;
  2318. self.groupedData = {};
  2319. // Here we set the onmousedown event handler to the header container.
  2320. var rows = grid.filteredRows,
  2321. maxDepth = groups.length,
  2322. cols = $scope.columns;
  2323. function filterCols(cols, group) {
  2324. return cols.filter(function(c) {
  2325. return c.field === group;
  2326. });
  2327. }
  2328. for (var x = 0; x < rows.length; x++) {
  2329. var model = rows[x].entity;
  2330. if (!model) {
  2331. return;
  2332. }
  2333. rows[x][NG_HIDDEN] = grid.config.groupsCollapsedByDefault;
  2334. var ptr = self.groupedData;
  2335. for (var y = 0; y < groups.length; y++) {
  2336. var group = groups[y];
  2337. var col = filterCols(cols, group)[0];
  2338. var val = $utils.evalProperty(model, group);
  2339. val = val ? val.toString() : 'null';
  2340. if (!ptr[val]) {
  2341. ptr[val] = {};
  2342. }
  2343. if (!ptr[NG_FIELD]) {
  2344. ptr[NG_FIELD] = group;
  2345. }
  2346. if (!ptr[NG_DEPTH]) {
  2347. ptr[NG_DEPTH] = y;
  2348. }
  2349. if (!ptr[NG_COLUMN]) {
  2350. ptr[NG_COLUMN] = col;
  2351. }
  2352. ptr = ptr[val];
  2353. }
  2354. if (!ptr.values) {
  2355. ptr.values = [];
  2356. }
  2357. ptr.values.push(rows[x]);
  2358. }
  2359. //moved out of above loops due to if no data initially, but has initial grouping, columns won't be added
  2360. if(cols.length > 0) {
  2361. for (var z = 0; z < groups.length; z++) {
  2362. if (!cols[z].isAggCol && z <= maxDepth) {
  2363. cols.splice(0, 0, new ngColumn({
  2364. colDef: {
  2365. field: '',
  2366. width: 25,
  2367. sortable: false,
  2368. resizable: false,
  2369. headerCellTemplate: '<div class="ngAggHeader"></div>',
  2370. pinned: grid.config.pinSelectionCheckbox
  2371. },
  2372. enablePinning: grid.config.enablePinning,
  2373. isAggCol: true,
  2374. headerRowHeight: grid.config.headerRowHeight
  2375. }, $scope, grid, domUtilityService, $templateCache, $utils));
  2376. }
  2377. }
  2378. }
  2379. grid.fixColumnIndexes();
  2380. $scope.adjustScrollLeft(0);
  2381. self.parsedData.length = 0;
  2382. self.parseGroupData(self.groupedData);
  2383. self.fixRowCache();
  2384. };
  2385. if (grid.config.groups.length > 0 && grid.filteredRows.length > 0) {
  2386. self.getGrouping(grid.config.groups);
  2387. }
  2388. };
  2389. var ngSearchProvider = function ($scope, grid, $filter) {
  2390. var self = this,
  2391. searchConditions = [];
  2392. self.extFilter = grid.config.filterOptions.useExternalFilter;
  2393. $scope.showFilter = grid.config.showFilter;
  2394. $scope.filterText = '';
  2395. self.fieldMap = {};
  2396. var convertToFieldMap = function(obj) {
  2397. var fieldMap = {};
  2398. for (var prop in obj) {
  2399. if (obj.hasOwnProperty(prop)) {
  2400. fieldMap[prop.toLowerCase()] = obj[prop];
  2401. }
  2402. }
  2403. return fieldMap;
  2404. };
  2405. var searchEntireRow = function(condition, item, fieldMap){
  2406. var result;
  2407. for (var prop in item) {
  2408. if (item.hasOwnProperty(prop)) {
  2409. var c = fieldMap[prop.toLowerCase()];
  2410. if (!c) {
  2411. continue;
  2412. }
  2413. var pVal = item[prop];
  2414. if(typeof pVal === 'object' && !(pVal instanceof Date)) {
  2415. var objectFieldMap = convertToFieldMap(c);
  2416. result = searchEntireRow(condition, pVal, objectFieldMap);
  2417. if (result) {
  2418. return true;
  2419. }
  2420. } else {
  2421. var f = null,
  2422. s = null;
  2423. if (c && c.cellFilter) {
  2424. s = c.cellFilter.split(':');
  2425. f = $filter(s[0]);
  2426. }
  2427. if (pVal !== null && pVal !== undefined) {
  2428. if (typeof f === "function") {
  2429. // Have to slice off the quotes the parser would have removed
  2430. var filterRes = f(pVal, s[1].slice(1,-1)).toString();
  2431. result = condition.regex.test(filterRes);
  2432. } else {
  2433. result = condition.regex.test(pVal.toString());
  2434. }
  2435. if (result) {
  2436. return true;
  2437. }
  2438. }
  2439. }
  2440. }
  2441. }
  2442. return false;
  2443. };
  2444. var searchColumn = function(condition, item){
  2445. var result;
  2446. var col = self.fieldMap[condition.columnDisplay];
  2447. if (!col) {
  2448. return false;
  2449. }
  2450. var sp = col.cellFilter.split(':');
  2451. var filter = col.cellFilter ? $filter(sp[0]) : null;
  2452. var value = item[condition.column] || item[col.field.split('.')[0]];
  2453. if (value === null || value === undefined) {
  2454. return false;
  2455. }
  2456. if (typeof filter === "function") {
  2457. var filterResults = filter(typeof value === "object" ? evalObject(value, col.field) : value, sp[1]).toString();
  2458. result = condition.regex.test(filterResults);
  2459. }
  2460. else {
  2461. result = condition.regex.test(typeof value === "object" ? evalObject(value, col.field).toString() : value.toString());
  2462. }
  2463. if (result) {
  2464. return true;
  2465. }
  2466. return false;
  2467. };
  2468. var filterFunc = function(item) {
  2469. for (var x = 0, len = searchConditions.length; x < len; x++) {
  2470. var condition = searchConditions[x];
  2471. var result;
  2472. if (!condition.column) {
  2473. result = searchEntireRow(condition, item, self.fieldMap);
  2474. } else {
  2475. result = searchColumn(condition, item);
  2476. }
  2477. if(!result) {
  2478. return false;
  2479. }
  2480. }
  2481. return true;
  2482. };
  2483. self.evalFilter = function () {
  2484. if (searchConditions.length === 0) {
  2485. grid.filteredRows = grid.rowCache;
  2486. } else {
  2487. grid.filteredRows = grid.rowCache.filter(function(row) {
  2488. return filterFunc(row.entity);
  2489. });
  2490. }
  2491. for (var i = 0; i < grid.filteredRows.length; i++)
  2492. {
  2493. grid.filteredRows[i].rowIndex = i;
  2494. }
  2495. grid.rowFactory.filteredRowsChanged();
  2496. };
  2497. //Traversing through the object to find the value that we want. If fail, then return the original object.
  2498. var evalObject = function (obj, columnName) {
  2499. if (typeof obj !== "object" || typeof columnName !== "string") {
  2500. return obj;
  2501. }
  2502. var args = columnName.split('.');
  2503. var cObj = obj;
  2504. if (args.length > 1) {
  2505. for (var i = 1, len = args.length; i < len; i++) {
  2506. cObj = cObj[args[i]];
  2507. if (!cObj) {
  2508. return obj;
  2509. }
  2510. }
  2511. return cObj;
  2512. }
  2513. return obj;
  2514. };
  2515. var getRegExp = function (str, modifiers) {
  2516. try {
  2517. return new RegExp(str, modifiers);
  2518. } catch (err) {
  2519. //Escape all RegExp metacharacters.
  2520. return new RegExp(str.replace(/(\^|\$|\(|\)|<|>|\[|\]|\{|\}|\\|\||\.|\*|\+|\?)/g, '\\$1'));
  2521. }
  2522. };
  2523. var buildSearchConditions = function (a) {
  2524. //reset.
  2525. searchConditions = [];
  2526. var qStr;
  2527. if (!(qStr = $.trim(a))) {
  2528. return;
  2529. }
  2530. var columnFilters = qStr.split(";");
  2531. for (var i = 0; i < columnFilters.length; i++) {
  2532. var args = columnFilters[i].split(':');
  2533. if (args.length > 1) {
  2534. var columnName = $.trim(args[0]);
  2535. var columnValue = $.trim(args[1]);
  2536. if (columnName && columnValue) {
  2537. searchConditions.push({
  2538. column: columnName,
  2539. columnDisplay: columnName.replace(/\s+/g, '').toLowerCase(),
  2540. regex: getRegExp(columnValue, 'i')
  2541. });
  2542. }
  2543. } else {
  2544. var val = $.trim(args[0]);
  2545. if (val) {
  2546. searchConditions.push({
  2547. column: '',
  2548. regex: getRegExp(val, 'i')
  2549. });
  2550. }
  2551. }
  2552. }
  2553. };
  2554. if (!self.extFilter) {
  2555. $scope.$on('$destroy', $scope.$watch('columns', function (cs) {
  2556. for (var i = 0; i < cs.length; i++) {
  2557. var col = cs[i];
  2558. if (col.field) {
  2559. if(col.field.match(/\./g)){
  2560. var properties = col.field.split('.');
  2561. var currentProperty = self.fieldMap;
  2562. for(var j = 0; j < properties.length - 1; j++) {
  2563. currentProperty[ properties[j] ] = currentProperty[ properties[j] ] || {};
  2564. currentProperty = currentProperty[properties[j]];
  2565. }
  2566. currentProperty[ properties[properties.length - 1] ] = col;
  2567. } else {
  2568. self.fieldMap[col.field.toLowerCase()] = col;
  2569. }
  2570. }
  2571. if (col.displayName) {
  2572. self.fieldMap[col.displayName.toLowerCase().replace(/\s+/g, '')] = col;
  2573. }
  2574. }
  2575. }));
  2576. }
  2577. $scope.$on('$destroy', $scope.$watch(
  2578. function () {
  2579. return grid.config.filterOptions.filterText;
  2580. },
  2581. function (a) {
  2582. $scope.filterText = a;
  2583. }
  2584. ));
  2585. $scope.$on('$destroy', $scope.$watch('filterText', function(a){
  2586. if (!self.extFilter) {
  2587. $scope.$emit('ngGridEventFilter', a);
  2588. buildSearchConditions(a);
  2589. self.evalFilter();
  2590. }
  2591. }));
  2592. };
  2593. var ngSelectionProvider = function (grid, $scope, $parse) {
  2594. var self = this;
  2595. self.multi = grid.config.multiSelect;
  2596. self.selectedItems = grid.config.selectedItems;
  2597. self.selectedIndex = grid.config.selectedIndex;
  2598. self.lastClickedRow = undefined;
  2599. self.ignoreSelectedItemChanges = false; // flag to prevent circular event loops keeping single-select var in sync
  2600. self.pKeyParser = $parse(grid.config.primaryKey);
  2601. // function to manage the selection action of a data item (entity)
  2602. self.ChangeSelection = function (rowItem, evt) {
  2603. // ctrl-click + shift-click multi-selections
  2604. // up/down key navigation in multi-selections
  2605. var charCode = evt.which || evt.keyCode;
  2606. var isUpDownKeyPress = (charCode === 40 || charCode === 38);
  2607. if (evt && evt.shiftKey && !evt.keyCode && self.multi && grid.config.enableRowSelection) {
  2608. if (self.lastClickedRow) {
  2609. var rowsArr;
  2610. if ($scope.configGroups.length > 0) {
  2611. rowsArr = grid.rowFactory.parsedData.filter(function(row) {
  2612. return !row.isAggRow;
  2613. });
  2614. }
  2615. else {
  2616. rowsArr = grid.filteredRows;
  2617. }
  2618. var thisIndx = rowItem.rowIndex;
  2619. var prevIndx = self.lastClickedRowIndex;
  2620. if (thisIndx === prevIndx) {
  2621. return false;
  2622. }
  2623. if (thisIndx < prevIndx) {
  2624. thisIndx = thisIndx ^ prevIndx;
  2625. prevIndx = thisIndx ^ prevIndx;
  2626. thisIndx = thisIndx ^ prevIndx;
  2627. thisIndx--;
  2628. }
  2629. else {
  2630. prevIndx++;
  2631. }
  2632. var rows = [];
  2633. for (; prevIndx <= thisIndx; prevIndx++) {
  2634. rows.push(rowsArr[prevIndx]);
  2635. }
  2636. if (rows[rows.length - 1].beforeSelectionChange(rows, evt)) {
  2637. for (var i = 0; i < rows.length; i++) {
  2638. var ri = rows[i];
  2639. var selectionState = ri.selected;
  2640. ri.selected = !selectionState;
  2641. if (ri.clone) {
  2642. ri.clone.selected = ri.selected;
  2643. }
  2644. var index = self.selectedItems.indexOf(ri.entity);
  2645. if (index === -1) {
  2646. self.selectedItems.push(ri.entity);
  2647. }
  2648. else {
  2649. self.selectedItems.splice(index, 1);
  2650. }
  2651. }
  2652. rows[rows.length - 1].afterSelectionChange(rows, evt);
  2653. }
  2654. self.lastClickedRow = rowItem;
  2655. self.lastClickedRowIndex = rowItem.rowIndex;
  2656. return true;
  2657. }
  2658. }
  2659. else if (!self.multi) {
  2660. if (self.lastClickedRow === rowItem) {
  2661. self.setSelection(self.lastClickedRow, grid.config.keepLastSelected ? true : !rowItem.selected);
  2662. } else {
  2663. if (self.lastClickedRow) {
  2664. self.setSelection(self.lastClickedRow, false);
  2665. }
  2666. self.setSelection(rowItem, !rowItem.selected);
  2667. }
  2668. }
  2669. else if (!evt.keyCode || isUpDownKeyPress && !grid.config.selectWithCheckboxOnly) {
  2670. self.setSelection(rowItem, !rowItem.selected);
  2671. }
  2672. self.lastClickedRow = rowItem;
  2673. self.lastClickedRowIndex = rowItem.rowIndex;
  2674. return true;
  2675. };
  2676. self.getSelection = function (entity) {
  2677. return self.getSelectionIndex(entity) !== -1;
  2678. };
  2679. self.getSelectionIndex = function (entity) {
  2680. var index = -1;
  2681. if (grid.config.primaryKey) {
  2682. var val = self.pKeyParser(entity);
  2683. angular.forEach(self.selectedItems, function (c, k) {
  2684. if (val === self.pKeyParser(c)) {
  2685. index = k;
  2686. }
  2687. });
  2688. }
  2689. else {
  2690. index = self.selectedItems.indexOf(entity);
  2691. }
  2692. return index;
  2693. };
  2694. // just call this func and hand it the rowItem you want to select (or de-select)
  2695. self.setSelection = function (rowItem, isSelected) {
  2696. if(grid.config.enableRowSelection){
  2697. if (!isSelected) {
  2698. var indx = self.getSelectionIndex(rowItem.entity);
  2699. if (indx !== -1) {
  2700. self.selectedItems.splice(indx, 1);
  2701. }
  2702. }
  2703. else {
  2704. if (self.getSelectionIndex(rowItem.entity) === -1) {
  2705. if (!self.multi && self.selectedItems.length > 0) {
  2706. self.toggleSelectAll(false, true);
  2707. }
  2708. self.selectedItems.push(rowItem.entity);
  2709. }
  2710. }
  2711. rowItem.selected = isSelected;
  2712. if (rowItem.orig) {
  2713. rowItem.orig.selected = isSelected;
  2714. }
  2715. if (rowItem.clone) {
  2716. rowItem.clone.selected = isSelected;
  2717. }
  2718. rowItem.afterSelectionChange(rowItem);
  2719. }
  2720. };
  2721. // @return - boolean indicating if all items are selected or not
  2722. // @val - boolean indicating whether to select all/de-select all
  2723. self.toggleSelectAll = function (checkAll, bypass, selectFiltered) {
  2724. var rows = selectFiltered ? grid.filteredRows : grid.rowCache;
  2725. if (bypass || grid.config.beforeSelectionChange(rows, checkAll)) {
  2726. var selectedlength = self.selectedItems.length;
  2727. if (selectedlength > 0) {
  2728. self.selectedItems.length = 0;
  2729. }
  2730. for (var i = 0; i < rows.length; i++) {
  2731. rows[i].selected = checkAll;
  2732. if (rows[i].clone) {
  2733. rows[i].clone.selected = checkAll;
  2734. }
  2735. if (checkAll) {
  2736. self.selectedItems.push(rows[i].entity);
  2737. }
  2738. }
  2739. if (!bypass) {
  2740. grid.config.afterSelectionChange(rows, checkAll);
  2741. }
  2742. }
  2743. };
  2744. };
  2745. var ngStyleProvider = function($scope, grid) {
  2746. $scope.headerCellStyle = function(col) {
  2747. return { "height": col.headerRowHeight + "px" };
  2748. };
  2749. $scope.rowStyle = function (row) {
  2750. var ret = { "top": row.offsetTop + "px", "height": $scope.rowHeight + "px" };
  2751. if (row.isAggRow) {
  2752. ret.left = row.offsetLeft;
  2753. }
  2754. return ret;
  2755. };
  2756. $scope.canvasStyle = function() {
  2757. return { "height": grid.maxCanvasHt + "px" };
  2758. };
  2759. $scope.headerScrollerStyle = function() {
  2760. return { "height": grid.config.headerRowHeight + "px" };
  2761. };
  2762. $scope.topPanelStyle = function() {
  2763. return { "width": grid.rootDim.outerWidth + "px", "height": $scope.topPanelHeight() + "px" };
  2764. };
  2765. $scope.headerStyle = function() {
  2766. return { "width": grid.rootDim.outerWidth + "px", "height": grid.config.headerRowHeight + "px" };
  2767. };
  2768. $scope.groupPanelStyle = function () {
  2769. return { "width": grid.rootDim.outerWidth + "px", "height": "32px" };
  2770. };
  2771. $scope.viewportStyle = function() {
  2772. return { "width": grid.rootDim.outerWidth + "px", "height": $scope.viewportDimHeight() + "px" };
  2773. };
  2774. $scope.footerStyle = function() {
  2775. return { "width": grid.rootDim.outerWidth + "px", "height": $scope.footerRowHeight + "px" };
  2776. };
  2777. };
  2778. ngGridDirectives.directive('ngCellHasFocus', ['$domUtilityService',
  2779. function (domUtilityService) {
  2780. var focusOnInputElement = function($scope, elm) {
  2781. $scope.isFocused = true;
  2782. domUtilityService.digest($scope);
  2783. $scope.$broadcast('ngGridEventStartCellEdit');
  2784. $scope.$emit('ngGridEventStartCellEdit');
  2785. $scope.$on('$destroy', $scope.$on('ngGridEventEndCellEdit', function() {
  2786. $scope.isFocused = false;
  2787. domUtilityService.digest($scope);
  2788. }));
  2789. };
  2790. return function($scope, elm) {
  2791. var isFocused = false;
  2792. var isCellEditableOnMouseDown = false;
  2793. $scope.editCell = function() {
  2794. if(!$scope.enableCellEditOnFocus) {
  2795. setTimeout(function() {
  2796. focusOnInputElement($scope,elm);
  2797. }, 0);
  2798. }
  2799. };
  2800. function mousedown (evt) {
  2801. if($scope.enableCellEditOnFocus) {
  2802. isCellEditableOnMouseDown = true;
  2803. } else {
  2804. elm.focus();
  2805. }
  2806. return true;
  2807. }
  2808. function click (evt) {
  2809. if($scope.enableCellEditOnFocus) {
  2810. evt.preventDefault();
  2811. isCellEditableOnMouseDown = false;
  2812. focusOnInputElement($scope,elm);
  2813. }
  2814. }
  2815. elm.bind('mousedown', mousedown);
  2816. elm.bind('click', click);
  2817. function focus (evt) {
  2818. isFocused = true;
  2819. if($scope.enableCellEditOnFocus && !isCellEditableOnMouseDown) {
  2820. focusOnInputElement($scope,elm);
  2821. }
  2822. return true;
  2823. }
  2824. elm.bind('focus', focus);
  2825. function blur() {
  2826. isFocused = false;
  2827. return true;
  2828. }
  2829. elm.bind('blur', blur);
  2830. function keydown (evt) {
  2831. if(!$scope.enableCellEditOnFocus) {
  2832. if (isFocused && evt.keyCode !== 37 && evt.keyCode !== 38 && evt.keyCode !== 39 && evt.keyCode !== 40 && evt.keyCode !== 9 && !evt.shiftKey && evt.keyCode !== 13) {
  2833. focusOnInputElement($scope,elm);
  2834. }
  2835. if (isFocused && evt.shiftKey && (evt.keyCode >= 65 && evt.keyCode <= 90)) {
  2836. focusOnInputElement($scope, elm);
  2837. }
  2838. if (evt.keyCode === 27) {
  2839. elm.focus();
  2840. }
  2841. }
  2842. return true;
  2843. }
  2844. elm.bind('keydown', keydown);
  2845. elm.on('$destroy', function() {
  2846. elm.off('mousedown', mousedown);
  2847. elm.off('click', click);
  2848. elm.off('focus', focus);
  2849. elm.off('blur', blur);
  2850. elm.off('keydown', keydown);
  2851. });
  2852. };
  2853. }]);
  2854. ngGridDirectives.directive('ngCellText',
  2855. function () {
  2856. return function(scope, elm) {
  2857. function mouseover (evt) {
  2858. evt.preventDefault();
  2859. }
  2860. elm.bind('mouseover', mouseover);
  2861. function mouseleave(evt) {
  2862. evt.preventDefault();
  2863. }
  2864. elm.bind('mouseleave', mouseleave);
  2865. elm.on('$destroy', function() {
  2866. elm.off('mouseover', mouseover);
  2867. elm.off('mouseleave', mouseleave);
  2868. });
  2869. };
  2870. });
  2871. ngGridDirectives.directive('ngCell', ['$compile', '$domUtilityService', function ($compile, domUtilityService) {
  2872. var ngCell = {
  2873. scope: false,
  2874. compile: function() {
  2875. return {
  2876. pre: function($scope, iElement) {
  2877. var html;
  2878. var cellTemplate = $scope.col.cellTemplate.replace(COL_FIELD, 'row.entity.' + $scope.col.field);
  2879. if ($scope.col.enableCellEdit) {
  2880. html = $scope.col.cellEditTemplate;
  2881. html = html.replace(CELL_EDITABLE_CONDITION, $scope.col.cellEditableCondition);
  2882. html = html.replace(DISPLAY_CELL_TEMPLATE, cellTemplate);
  2883. html = html.replace(EDITABLE_CELL_TEMPLATE, $scope.col.editableCellTemplate.replace(COL_FIELD, 'row.entity.' + $scope.col.field));
  2884. } else {
  2885. html = cellTemplate;
  2886. }
  2887. var cellElement = $(html);
  2888. iElement.append(cellElement);
  2889. $compile(cellElement)($scope);
  2890. if ($scope.enableCellSelection && cellElement[0].className.indexOf('ngSelectionCell') === -1) {
  2891. cellElement[0].setAttribute('tabindex', 0);
  2892. cellElement.addClass('ngCellElement');
  2893. }
  2894. },
  2895. post: function($scope, iElement) {
  2896. if ($scope.enableCellSelection) {
  2897. $scope.domAccessProvider.selectionHandlers($scope, iElement);
  2898. }
  2899. $scope.$on('$destroy', $scope.$on('ngGridEventDigestCell', function() {
  2900. domUtilityService.digest($scope);
  2901. }));
  2902. }
  2903. };
  2904. }
  2905. };
  2906. return ngCell;
  2907. }]);
  2908. /*
  2909. * Defines the ui-if tag. This removes/adds an element from the dom depending on a condition
  2910. * Originally created by @tigbro, for the @jquery-mobile-angular-adapter
  2911. * https://github.com/tigbro/jquery-mobile-angular-adapter
  2912. */
  2913. ngGridDirectives.directive('ngEditCellIf', [function () {
  2914. return {
  2915. transclude: 'element',
  2916. priority: 1000,
  2917. terminal: true,
  2918. restrict: 'A',
  2919. compile: function (e, a, transclude) {
  2920. return function (scope, element, attr) {
  2921. var childElement;
  2922. var childScope;
  2923. scope.$on('$destroy', scope.$watch(attr['ngEditCellIf'], function (newValue) {
  2924. if (childElement) {
  2925. childElement.remove();
  2926. childElement = undefined;
  2927. }
  2928. if (childScope) {
  2929. childScope.$destroy();
  2930. childScope = undefined;
  2931. }
  2932. if (newValue) {
  2933. childScope = scope.$new();
  2934. transclude(childScope, function (clone) {
  2935. childElement = clone;
  2936. element.after(clone);
  2937. });
  2938. }
  2939. }));
  2940. };
  2941. }
  2942. };
  2943. }]);
  2944. ngGridDirectives.directive('ngGridFooter', ['$compile', '$templateCache', function ($compile, $templateCache) {
  2945. var ngGridFooter = {
  2946. scope: false,
  2947. compile: function () {
  2948. return {
  2949. pre: function ($scope, iElement) {
  2950. if (iElement.children().length === 0) {
  2951. iElement.append($compile($templateCache.get($scope.gridId + 'footerTemplate.html'))($scope));
  2952. }
  2953. }
  2954. };
  2955. }
  2956. };
  2957. return ngGridFooter;
  2958. }]);
  2959. ngGridDirectives.directive('ngGridMenu', ['$compile', '$templateCache', function ($compile, $templateCache) {
  2960. var ngGridMenu = {
  2961. scope: false,
  2962. compile: function () {
  2963. return {
  2964. pre: function ($scope, iElement) {
  2965. if (iElement.children().length === 0) {
  2966. iElement.append($compile($templateCache.get($scope.gridId + 'menuTemplate.html'))($scope));
  2967. }
  2968. }
  2969. };
  2970. }
  2971. };
  2972. return ngGridMenu;
  2973. }]);
  2974. ngGridDirectives.directive('ngGrid', ['$compile', '$filter', '$templateCache', '$sortService', '$domUtilityService', '$utilityService', '$timeout', '$parse', '$http', '$q', function ($compile, $filter, $templateCache, sortService, domUtilityService, $utils, $timeout, $parse, $http, $q) {
  2975. var ngGridDirective = {
  2976. scope: true,
  2977. compile: function() {
  2978. return {
  2979. pre: function($scope, iElement, iAttrs) {
  2980. var $element = $(iElement);
  2981. var options = $scope.$eval(iAttrs.ngGrid);
  2982. options.gridDim = new ngDimension({ outerHeight: $($element).height(), outerWidth: $($element).width() });
  2983. var grid = new ngGrid($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse, $http, $q);
  2984. // Set up cleanup now in case something fails
  2985. $scope.$on('$destroy', function cleanOptions() {
  2986. options.gridDim = null;
  2987. options.selectRow = null;
  2988. options.selectItem = null;
  2989. options.selectAll = null;
  2990. options.selectVisible = null;
  2991. options.groupBy = null;
  2992. options.sortBy = null;
  2993. options.gridId = null;
  2994. options.ngGrid = null;
  2995. options.$gridScope = null;
  2996. options.$gridServices = null;
  2997. $scope.domAccessProvider.grid = null;
  2998. // Plugins should already have been killed as they are children of $scope
  2999. // Remove the grid's stylesheet from dom
  3000. angular.element(grid.styleSheet).remove();
  3001. grid.styleSheet = null;
  3002. });
  3003. return grid.init().then(function() {
  3004. // if columndefs are a string of a property ont he scope watch for changes and rebuild columns.
  3005. if (typeof options.columnDefs === "string") {
  3006. $scope.$on('$destroy', $scope.$parent.$watch(options.columnDefs, function (a) {
  3007. if (!a) {
  3008. grid.refreshDomSizes();
  3009. grid.buildColumns();
  3010. return;
  3011. }
  3012. // we have to set this to false in case we want to autogenerate columns with no initial data.
  3013. grid.lateBoundColumns = false;
  3014. $scope.columns = [];
  3015. grid.config.columnDefs = a;
  3016. grid.buildColumns();
  3017. grid.eventProvider.assignEvents();
  3018. domUtilityService.RebuildGrid($scope, grid);
  3019. }, true));
  3020. }
  3021. else {
  3022. grid.buildColumns();
  3023. }
  3024. // Watch totalServerItems if it's a string
  3025. if (typeof options.totalServerItems === "string") {
  3026. $scope.$on('$destroy', $scope.$parent.$watch(options.totalServerItems, function (newTotal, oldTotal) {
  3027. // If the newTotal is not defined (like during init, set the value to 0)
  3028. if (!angular.isDefined(newTotal)) {
  3029. $scope.totalServerItems = 0;
  3030. }
  3031. // Otherwise set the value to the new total
  3032. else {
  3033. $scope.totalServerItems = newTotal;
  3034. }
  3035. }));
  3036. }
  3037. // If it's NOT a string, then just set totalServerItems to 0 since they should only be setting this if using a string
  3038. else {
  3039. $scope.totalServerItems = 0;
  3040. }
  3041. // if it is a string we can watch for data changes. otherwise you won't be able to update the grid data
  3042. if (typeof options.data === "string") {
  3043. var dataWatcher = function (a) {
  3044. // make a temporary copy of the data
  3045. grid.data = $.extend([], a);
  3046. grid.rowFactory.fixRowCache();
  3047. angular.forEach(grid.data, function (item, j) {
  3048. var indx = grid.rowMap[j] || j;
  3049. if (grid.rowCache[indx]) {
  3050. grid.rowCache[indx].ensureEntity(item);
  3051. }
  3052. grid.rowMap[indx] = j;
  3053. });
  3054. grid.searchProvider.evalFilter();
  3055. grid.configureColumnWidths();
  3056. grid.refreshDomSizes();
  3057. if (grid.config.sortInfo.fields.length > 0) {
  3058. grid.sortColumnsInit();
  3059. $scope.$emit('ngGridEventSorted', grid.config.sortInfo);
  3060. }
  3061. $scope.$emit("ngGridEventData", grid.gridId);
  3062. };
  3063. $scope.$on('$destroy', $scope.$parent.$watch(options.data, dataWatcher));
  3064. $scope.$on('$destroy', $scope.$parent.$watch(options.data + '.length', function() {
  3065. dataWatcher($scope.$eval(options.data));
  3066. $scope.adjustScrollTop(grid.$viewport.scrollTop(), true);
  3067. }));
  3068. }
  3069. grid.footerController = new ngFooter($scope, grid);
  3070. //set the right styling on the container
  3071. iElement.addClass("ngGrid").addClass(grid.gridId.toString());
  3072. if (!options.enableHighlighting) {
  3073. iElement.addClass("unselectable");
  3074. }
  3075. if (options.jqueryUITheme) {
  3076. iElement.addClass('ui-widget');
  3077. }
  3078. iElement.append($compile($templateCache.get('gridTemplate.html'))($scope)); // make sure that if any of these change, we re-fire the calc logic
  3079. //walk the element's graph and the correct properties on the grid
  3080. domUtilityService.AssignGridContainers($scope, iElement, grid);
  3081. //now use the manager to assign the event handlers
  3082. grid.eventProvider = new ngEventProvider(grid, $scope, domUtilityService, $timeout);
  3083. // method for user to select a specific row programatically
  3084. options.selectRow = function (rowIndex, state) {
  3085. if (grid.rowCache[rowIndex]) {
  3086. if (grid.rowCache[rowIndex].clone) {
  3087. grid.rowCache[rowIndex].clone.setSelection(state ? true : false);
  3088. }
  3089. grid.rowCache[rowIndex].setSelection(state ? true : false);
  3090. }
  3091. };
  3092. // method for user to select the row by data item programatically
  3093. options.selectItem = function (itemIndex, state) {
  3094. options.selectRow(grid.rowMap[itemIndex], state);
  3095. };
  3096. // method for user to set the select all state.
  3097. options.selectAll = function (state) {
  3098. $scope.toggleSelectAll(state);
  3099. };
  3100. // method for user to set the select all state on visible items.
  3101. options.selectVisible = function (state) {
  3102. $scope.toggleSelectAll(state, true);
  3103. };
  3104. // method for user to set the groups programatically
  3105. options.groupBy = function (field) {
  3106. if (field) {
  3107. $scope.groupBy($scope.columns.filter(function(c) {
  3108. return c.field === field;
  3109. })[0]);
  3110. } else {
  3111. var arr = $.extend(true, [], $scope.configGroups);
  3112. angular.forEach(arr, $scope.groupBy);
  3113. }
  3114. };
  3115. // method for user to set the sort field programatically
  3116. options.sortBy = function (field) {
  3117. var col = $scope.columns.filter(function (c) {
  3118. return c.field === field;
  3119. })[0];
  3120. if (col) {
  3121. col.sort();
  3122. }
  3123. };
  3124. // the grid Id, entity, scope for convenience
  3125. options.gridId = grid.gridId;
  3126. options.ngGrid = grid;
  3127. options.$gridScope = $scope;
  3128. options.$gridServices = { SortService: sortService, DomUtilityService: domUtilityService, UtilityService: $utils };
  3129. $scope.$on('$destroy', $scope.$on('ngGridEventDigestGrid', function(){
  3130. domUtilityService.digest($scope.$parent);
  3131. }));
  3132. $scope.$on('$destroy', $scope.$on('ngGridEventDigestGridParent', function(){
  3133. domUtilityService.digest($scope.$parent);
  3134. }));
  3135. // set up the columns
  3136. $scope.$evalAsync(function() {
  3137. $scope.adjustScrollLeft(0);
  3138. });
  3139. //initialize plugins.
  3140. angular.forEach(options.plugins, function (p) {
  3141. if (typeof p === "function") {
  3142. p = new p(); //If p is a function, then we assume it is a class.
  3143. }
  3144. var newScope = $scope.$new();
  3145. p.init(newScope, grid, options.$gridServices);
  3146. options.plugins[$utils.getInstanceType(p)] = p;
  3147. $scope.$on('$destroy', function() {
  3148. newScope.$destroy();
  3149. });
  3150. });
  3151. //send initi finalize notification.
  3152. if (typeof options.init === "function") {
  3153. options.init(grid, $scope);
  3154. }
  3155. return null;
  3156. });
  3157. }
  3158. };
  3159. }
  3160. };
  3161. return ngGridDirective;
  3162. }]);
  3163. ngGridDirectives.directive('ngHeaderCell', ['$compile', function($compile) {
  3164. var ngHeaderCell = {
  3165. scope: false,
  3166. compile: function() {
  3167. return {
  3168. pre: function($scope, iElement) {
  3169. iElement.append($compile($scope.col.headerCellTemplate)($scope));
  3170. }
  3171. };
  3172. }
  3173. };
  3174. return ngHeaderCell;
  3175. }]);
  3176. ngGridDirectives.directive('ngHeaderRow', ['$compile', '$templateCache', function ($compile, $templateCache) {
  3177. var ngHeaderRow = {
  3178. scope: false,
  3179. compile: function () {
  3180. return {
  3181. pre: function ($scope, iElement) {
  3182. if (iElement.children().length === 0) {
  3183. iElement.append($compile($templateCache.get($scope.gridId + 'headerRowTemplate.html'))($scope));
  3184. }
  3185. }
  3186. };
  3187. }
  3188. };
  3189. return ngHeaderRow;
  3190. }]);
  3191. ngGridDirectives.directive('ngInput', [function() {
  3192. return {
  3193. require: 'ngModel',
  3194. link: function (scope, elm, attrs, ngModel) {
  3195. // Store the initial cell value so we can reset to it if need be
  3196. var oldCellValue;
  3197. var dereg = scope.$watch('ngModel', function() {
  3198. oldCellValue = ngModel.$modelValue;
  3199. dereg(); // only run this watch once, we don't want to overwrite our stored value when the input changes
  3200. });
  3201. function keydown (evt) {
  3202. switch (evt.keyCode) {
  3203. case 37: // Left arrow
  3204. case 38: // Up arrow
  3205. case 39: // Right arrow
  3206. case 40: // Down arrow
  3207. evt.stopPropagation();
  3208. break;
  3209. case 27: // Esc (reset to old value)
  3210. if (!scope.$$phase) {
  3211. scope.$apply(function() {
  3212. ngModel.$setViewValue(oldCellValue);
  3213. elm.blur();
  3214. });
  3215. }
  3216. break;
  3217. case 13: // Enter (Leave Field)
  3218. if(scope.enableCellEditOnFocus && scope.totalFilteredItemsLength() - 1 > scope.row.rowIndex && scope.row.rowIndex > 0 || scope.col.enableCellEdit) {
  3219. elm.blur();
  3220. }
  3221. break;
  3222. }
  3223. return true;
  3224. }
  3225. elm.bind('keydown', keydown);
  3226. function click (evt) {
  3227. evt.stopPropagation();
  3228. }
  3229. elm.bind('click', click);
  3230. function mousedown (evt) {
  3231. evt.stopPropagation();
  3232. }
  3233. elm.bind('mousedown', mousedown);
  3234. elm.on('$destroy', function() {
  3235. elm.off('keydown', keydown);
  3236. elm.off('click', click);
  3237. elm.off('mousedown', mousedown);
  3238. });
  3239. scope.$on('$destroy', scope.$on('ngGridEventStartCellEdit', function () {
  3240. elm.focus();
  3241. elm.select();
  3242. }));
  3243. angular.element(elm).bind('blur', function () {
  3244. scope.$emit('ngGridEventEndCellEdit');
  3245. });
  3246. }
  3247. };
  3248. }]);
  3249. ngGridDirectives.directive('ngRow', ['$compile', '$domUtilityService', '$templateCache', function ($compile, domUtilityService, $templateCache) {
  3250. var ngRow = {
  3251. scope: false,
  3252. compile: function() {
  3253. return {
  3254. pre: function($scope, iElement) {
  3255. $scope.row.elm = iElement;
  3256. if ($scope.row.clone) {
  3257. $scope.row.clone.elm = iElement;
  3258. }
  3259. if ($scope.row.isAggRow) {
  3260. var html = $templateCache.get($scope.gridId + 'aggregateTemplate.html');
  3261. if ($scope.row.aggLabelFilter) {
  3262. html = html.replace(CUSTOM_FILTERS, '| ' + $scope.row.aggLabelFilter);
  3263. } else {
  3264. html = html.replace(CUSTOM_FILTERS, "");
  3265. }
  3266. iElement.append($compile(html)($scope));
  3267. } else {
  3268. iElement.append($compile($templateCache.get($scope.gridId + 'rowTemplate.html'))($scope));
  3269. }
  3270. $scope.$on('$destroy', $scope.$on('ngGridEventDigestRow', function(){
  3271. domUtilityService.digest($scope);
  3272. }));
  3273. }
  3274. };
  3275. }
  3276. };
  3277. return ngRow;
  3278. }]);
  3279. ngGridDirectives.directive('ngViewport', [function() {
  3280. return function($scope, elm) {
  3281. var isMouseWheelActive;
  3282. var prevScollLeft;
  3283. var prevScollTop = 0;
  3284. var ensureDigest = function() {
  3285. if (!$scope.$root.$$phase) {
  3286. $scope.$digest();
  3287. }
  3288. };
  3289. var scrollTimer;
  3290. function scroll (evt) {
  3291. var scrollLeft = evt.target.scrollLeft,
  3292. scrollTop = evt.target.scrollTop;
  3293. if ($scope.$headerContainer) {
  3294. $scope.$headerContainer.scrollLeft(scrollLeft);
  3295. }
  3296. $scope.adjustScrollLeft(scrollLeft);
  3297. $scope.adjustScrollTop(scrollTop);
  3298. if ($scope.forceSyncScrolling) {
  3299. ensureDigest();
  3300. } else {
  3301. clearTimeout(scrollTimer);
  3302. scrollTimer = setTimeout(ensureDigest, 150);
  3303. }
  3304. prevScollLeft = scrollLeft;
  3305. prevScollTop = scrollTop;
  3306. isMouseWheelActive = false;
  3307. return true;
  3308. }
  3309. elm.bind('scroll', scroll);
  3310. function mousewheel() {
  3311. isMouseWheelActive = true;
  3312. if (elm.focus) { elm.focus(); }
  3313. return true;
  3314. }
  3315. elm.bind("mousewheel DOMMouseScroll", mousewheel);
  3316. elm.on('$destroy', function() {
  3317. elm.off('scroll', scroll);
  3318. elm.off('mousewheel DOMMouseScroll', mousewheel);
  3319. });
  3320. if (!$scope.enableCellSelection) {
  3321. $scope.domAccessProvider.selectionHandlers($scope, elm);
  3322. }
  3323. };
  3324. }]);
  3325. window.ngGrid.i18n['da'] = {
  3326. ngAggregateLabel: 'artikler',
  3327. ngGroupPanelDescription: 'Grupér rækker udfra en kolonne ved at trække dens overskift hertil.',
  3328. ngSearchPlaceHolder: 'Søg...',
  3329. ngMenuText: 'Vælg kolonner:',
  3330. ngShowingItemsLabel: 'Viste rækker:',
  3331. ngTotalItemsLabel: 'Rækker totalt:',
  3332. ngSelectedItemsLabel: 'Valgte rækker:',
  3333. ngPageSizeLabel: 'Side størrelse:',
  3334. ngPagerFirstTitle: 'Første side',
  3335. ngPagerNextTitle: 'Næste side',
  3336. ngPagerPrevTitle: 'Forrige side',
  3337. ngPagerLastTitle: 'Sidste side'
  3338. };
  3339. window.ngGrid.i18n['de'] = {
  3340. ngAggregateLabel: 'eintrag',
  3341. ngGroupPanelDescription: 'Ziehen Sie eine Spaltenüberschrift hierhin um nach dieser Spalte zu gruppieren.',
  3342. ngSearchPlaceHolder: 'Suche...',
  3343. ngMenuText: 'Spalten auswählen:',
  3344. ngShowingItemsLabel: 'Zeige Einträge:',
  3345. ngTotalItemsLabel: 'Einträge gesamt:',
  3346. ngSelectedItemsLabel: 'Ausgewählte Einträge:',
  3347. ngPageSizeLabel: 'Einträge pro Seite:',
  3348. ngPagerFirstTitle: 'Erste Seite',
  3349. ngPagerNextTitle: 'Nächste Seite',
  3350. ngPagerPrevTitle: 'Vorherige Seite',
  3351. ngPagerLastTitle: 'Letzte Seite'
  3352. };
  3353. window.ngGrid.i18n['en'] = {
  3354. ngAggregateLabel: 'items',
  3355. ngGroupPanelDescription: 'Drag a column header here and drop it to group by that column.',
  3356. ngSearchPlaceHolder: 'Search...',
  3357. ngMenuText: 'Choose Columns:',
  3358. ngShowingItemsLabel: 'Showing Items:',
  3359. ngTotalItemsLabel: 'Total Items:',
  3360. ngSelectedItemsLabel: 'Selected Items:',
  3361. ngPageSizeLabel: 'Page Size:',
  3362. ngPagerFirstTitle: 'First Page',
  3363. ngPagerNextTitle: 'Next Page',
  3364. ngPagerPrevTitle: 'Previous Page',
  3365. ngPagerLastTitle: 'Last Page'
  3366. };
  3367. window.ngGrid.i18n['es'] = {
  3368. ngAggregateLabel: 'Artículos',
  3369. ngGroupPanelDescription: 'Arrastre un encabezado de columna aquí y soltarlo para agrupar por esa columna.',
  3370. ngSearchPlaceHolder: 'Buscar...',
  3371. ngMenuText: 'Elegir columnas:',
  3372. ngShowingItemsLabel: 'Artículos Mostrando:',
  3373. ngTotalItemsLabel: 'Artículos Totales:',
  3374. ngSelectedItemsLabel: 'Artículos Seleccionados:',
  3375. ngPageSizeLabel: 'Tamaño de Página:',
  3376. ngPagerFirstTitle: 'Primera Página',
  3377. ngPagerNextTitle: 'Página Siguiente',
  3378. ngPagerPrevTitle: 'Página Anterior',
  3379. ngPagerLastTitle: 'Última Página'
  3380. };
  3381. window.ngGrid.i18n['fa'] = {
  3382. ngAggregateLabel: 'موردها',
  3383. ngGroupPanelDescription: 'یک عنوان ستون اینجا را بردار و به گروهی از آن ستون بیانداز.',
  3384. ngSearchPlaceHolder: 'جستجو...',
  3385. ngMenuText: 'انتخاب ستون\u200cها:',
  3386. ngShowingItemsLabel: 'نمایش موردها:',
  3387. ngTotalItemsLabel: 'همهٔ موردها:',
  3388. ngSelectedItemsLabel: 'موردهای انتخاب\u200cشده:',
  3389. ngPageSizeLabel: 'اندازهٔ صفحه:',
  3390. ngPagerFirstTitle: 'صفحهٔ اول',
  3391. ngPagerNextTitle: 'صفحهٔ بعد',
  3392. ngPagerPrevTitle: 'صفحهٔ قبل',
  3393. ngPagerLastTitle: 'آخرین صفحه'
  3394. };
  3395. window.ngGrid.i18n['fr'] = {
  3396. ngAggregateLabel: 'articles',
  3397. ngGroupPanelDescription: 'Faites glisser un en-tête de colonne ici et déposez-le vers un groupe par cette colonne.',
  3398. ngSearchPlaceHolder: 'Recherche...',
  3399. ngMenuText: 'Choisir des colonnes:',
  3400. ngShowingItemsLabel: 'Articles Affichage des:',
  3401. ngTotalItemsLabel: 'Nombre total d\'articles:',
  3402. ngSelectedItemsLabel: 'Éléments Articles:',
  3403. ngPageSizeLabel: 'Taille de page:',
  3404. ngPagerFirstTitle: 'Première page',
  3405. ngPagerNextTitle: 'Page Suivante',
  3406. ngPagerPrevTitle: 'Page précédente',
  3407. ngPagerLastTitle: 'Dernière page'
  3408. };
  3409. window.ngGrid.i18n['nl'] = {
  3410. ngAggregateLabel: 'items',
  3411. ngGroupPanelDescription: 'Sleep hier een kolomkop om op te groeperen.',
  3412. ngSearchPlaceHolder: 'Zoeken...',
  3413. ngMenuText: 'Kies kolommen:',
  3414. ngShowingItemsLabel: 'Toon items:',
  3415. ngTotalItemsLabel: 'Totaal items:',
  3416. ngSelectedItemsLabel: 'Geselecteerde items:',
  3417. ngPageSizeLabel: 'Pagina grootte:, ',
  3418. ngPagerFirstTitle: 'Eerste pagina',
  3419. ngPagerNextTitle: 'Volgende pagina',
  3420. ngPagerPrevTitle: 'Vorige pagina',
  3421. ngPagerLastTitle: 'Laatste pagina'
  3422. };
  3423. window.ngGrid.i18n['pt-br'] = {
  3424. ngAggregateLabel: 'itens',
  3425. ngGroupPanelDescription: 'Arraste e solte uma coluna aqui para agrupar por essa coluna',
  3426. ngSearchPlaceHolder: 'Procurar...',
  3427. ngMenuText: 'Selecione as colunas:',
  3428. ngShowingItemsLabel: 'Mostrando os Itens:',
  3429. ngTotalItemsLabel: 'Total de Itens:',
  3430. ngSelectedItemsLabel: 'Items Selecionados:',
  3431. ngPageSizeLabel: 'Tamanho da Página:',
  3432. ngPagerFirstTitle: 'Primeira Página',
  3433. ngPagerNextTitle: 'Próxima Página',
  3434. ngPagerPrevTitle: 'Página Anterior',
  3435. ngPagerLastTitle: 'Última Página'
  3436. };
  3437. window.ngGrid.i18n['zh-cn'] = {
  3438. ngAggregateLabel: '条目',
  3439. ngGroupPanelDescription: '拖曳表头到此处以进行分组',
  3440. ngSearchPlaceHolder: '搜索...',
  3441. ngMenuText: '数据分组与选择列:',
  3442. ngShowingItemsLabel: '当前显示条目:',
  3443. ngTotalItemsLabel: '条目总数:',
  3444. ngSelectedItemsLabel: '选中条目:',
  3445. ngPageSizeLabel: '每页显示数:',
  3446. ngPagerFirstTitle: '回到首页',
  3447. ngPagerNextTitle: '下一页',
  3448. ngPagerPrevTitle: '上一页',
  3449. ngPagerLastTitle: '前往尾页'
  3450. };
  3451. window.ngGrid.i18n['zh-tw'] = {
  3452. ngAggregateLabel: '筆',
  3453. ngGroupPanelDescription: '拖拉表頭到此處以進行分組',
  3454. ngSearchPlaceHolder: '搜尋...',
  3455. ngMenuText: '選擇欄位:',
  3456. ngShowingItemsLabel: '目前顯示筆數:',
  3457. ngTotalItemsLabel: '總筆數:',
  3458. ngSelectedItemsLabel: '選取筆數:',
  3459. ngPageSizeLabel: '每頁顯示:',
  3460. ngPagerFirstTitle: '第一頁',
  3461. ngPagerNextTitle: '下一頁',
  3462. ngPagerPrevTitle: '上一頁',
  3463. ngPagerLastTitle: '最後頁'
  3464. };
  3465. angular.module('ngGrid').run(['$templateCache', function($templateCache) {
  3466. 'use strict';
  3467. $templateCache.put('aggregateTemplate.html',
  3468. "<div ng-click=\"row.toggleExpand()\" ng-style=\"rowStyle(row)\" class=\"ngAggregate\">\r" +
  3469. "\n" +
  3470. " <span class=\"ngAggregateText\">{{row.label CUSTOM_FILTERS}} ({{row.totalChildren()}} {{AggItemsLabel}})</span>\r" +
  3471. "\n" +
  3472. " <div class=\"{{row.aggClass()}}\"></div>\r" +
  3473. "\n" +
  3474. "</div>\r" +
  3475. "\n"
  3476. );
  3477. $templateCache.put('cellEditTemplate.html',
  3478. "<div ng-cell-has-focus ng-dblclick=\"CELL_EDITABLE_CONDITION && editCell()\">\r" +
  3479. "\n" +
  3480. "\t<div ng-edit-cell-if=\"!(isFocused && CELL_EDITABLE_CONDITION)\">\t\r" +
  3481. "\n" +
  3482. "\t\tDISPLAY_CELL_TEMPLATE\r" +
  3483. "\n" +
  3484. "\t</div>\r" +
  3485. "\n" +
  3486. "\t<div ng-edit-cell-if=\"isFocused && CELL_EDITABLE_CONDITION\">\r" +
  3487. "\n" +
  3488. "\t\tEDITABLE_CELL_TEMPLATE\r" +
  3489. "\n" +
  3490. "\t</div>\r" +
  3491. "\n" +
  3492. "</div>\r" +
  3493. "\n"
  3494. );
  3495. $templateCache.put('cellTemplate.html',
  3496. "<div class=\"ngCellText\" ng-class=\"col.colIndex()\"><span ng-cell-text>{{COL_FIELD CUSTOM_FILTERS}}</span></div>"
  3497. );
  3498. $templateCache.put('checkboxCellTemplate.html',
  3499. "<div class=\"ngSelectionCell\"><input tabindex=\"-1\" class=\"ngSelectionCheckbox\" type=\"checkbox\" ng-checked=\"row.selected\" /></div>"
  3500. );
  3501. $templateCache.put('checkboxHeaderTemplate.html',
  3502. "<input class=\"ngSelectionHeader\" type=\"checkbox\" ng-show=\"multiSelect\" ng-model=\"allSelected\" ng-change=\"toggleSelectAll(allSelected, true)\"/>"
  3503. );
  3504. $templateCache.put('editableCellTemplate.html',
  3505. "<input ng-class=\"'colt' + col.index\" ng-input=\"COL_FIELD\" ng-model=\"COL_FIELD\" />"
  3506. );
  3507. $templateCache.put('footerTemplate.html',
  3508. "<div ng-show=\"showFooter\" class=\"ngFooterPanel\" ng-class=\"{'ui-widget-content': jqueryUITheme, 'ui-corner-bottom': jqueryUITheme}\" ng-style=\"footerStyle()\">\r" +
  3509. "\n" +
  3510. " <div class=\"ngTotalSelectContainer\" >\r" +
  3511. "\n" +
  3512. " <div class=\"ngFooterTotalItems\" ng-class=\"{'ngNoMultiSelect': !multiSelect}\" >\r" +
  3513. "\n" +
  3514. " <span class=\"ngLabel\">{{i18n.ngTotalItemsLabel}} {{maxRows()}}</span><span ng-show=\"filterText.length > 0\" class=\"ngLabel\">({{i18n.ngShowingItemsLabel}} {{totalFilteredItemsLength()}})</span>\r" +
  3515. "\n" +
  3516. " </div>\r" +
  3517. "\n" +
  3518. " <div class=\"ngFooterSelectedItems\" ng-show=\"multiSelect\">\r" +
  3519. "\n" +
  3520. " <span class=\"ngLabel\">{{i18n.ngSelectedItemsLabel}} {{selectedItems.length}}</span>\r" +
  3521. "\n" +
  3522. " </div>\r" +
  3523. "\n" +
  3524. " </div>\r" +
  3525. "\n" +
  3526. " <div class=\"ngPagerContainer\" style=\"float: right; margin-top: 10px;\" ng-show=\"enablePaging\" ng-class=\"{'ngNoMultiSelect': !multiSelect}\">\r" +
  3527. "\n" +
  3528. " <div style=\"float:left; margin-right: 10px;\" class=\"ngRowCountPicker\">\r" +
  3529. "\n" +
  3530. " <span style=\"float: left; margin-top: 3px;\" class=\"ngLabel\">{{i18n.ngPageSizeLabel}}</span>\r" +
  3531. "\n" +
  3532. " <select style=\"float: left;height: 27px; width: 100px\" ng-model=\"pagingOptions.pageSize\" >\r" +
  3533. "\n" +
  3534. " <option ng-repeat=\"size in pagingOptions.pageSizes\">{{size}}</option>\r" +
  3535. "\n" +
  3536. " </select>\r" +
  3537. "\n" +
  3538. " </div>\r" +
  3539. "\n" +
  3540. " <div style=\"float:left; margin-right: 10px; line-height:25px;\" class=\"ngPagerControl\" style=\"float: left; min-width: 135px;\">\r" +
  3541. "\n" +
  3542. " <button type=\"button\" class=\"ngPagerButton\" ng-click=\"pageToFirst()\" ng-disabled=\"cantPageBackward()\" title=\"{{i18n.ngPagerFirstTitle}}\"><div class=\"ngPagerFirstTriangle\"><div class=\"ngPagerFirstBar\"></div></div></button>\r" +
  3543. "\n" +
  3544. " <button type=\"button\" class=\"ngPagerButton\" ng-click=\"pageBackward()\" ng-disabled=\"cantPageBackward()\" title=\"{{i18n.ngPagerPrevTitle}}\"><div class=\"ngPagerFirstTriangle ngPagerPrevTriangle\"></div></button>\r" +
  3545. "\n" +
  3546. " <input class=\"ngPagerCurrent\" min=\"1\" max=\"{{currentMaxPages}}\" type=\"number\" style=\"width:50px; height: 24px; margin-top: 1px; padding: 0 4px;\" ng-model=\"pagingOptions.currentPage\"/>\r" +
  3547. "\n" +
  3548. " <span class=\"ngGridMaxPagesNumber\" ng-show=\"maxPages() > 0\">/ {{maxPages()}}</span>\r" +
  3549. "\n" +
  3550. " <button type=\"button\" class=\"ngPagerButton\" ng-click=\"pageForward()\" ng-disabled=\"cantPageForward()\" title=\"{{i18n.ngPagerNextTitle}}\"><div class=\"ngPagerLastTriangle ngPagerNextTriangle\"></div></button>\r" +
  3551. "\n" +
  3552. " <button type=\"button\" class=\"ngPagerButton\" ng-click=\"pageToLast()\" ng-disabled=\"cantPageToLast()\" title=\"{{i18n.ngPagerLastTitle}}\"><div class=\"ngPagerLastTriangle\"><div class=\"ngPagerLastBar\"></div></div></button>\r" +
  3553. "\n" +
  3554. " </div>\r" +
  3555. "\n" +
  3556. " </div>\r" +
  3557. "\n" +
  3558. "</div>\r" +
  3559. "\n"
  3560. );
  3561. $templateCache.put('gridTemplate.html',
  3562. "<div class=\"ngTopPanel\" ng-class=\"{'ui-widget-header':jqueryUITheme, 'ui-corner-top': jqueryUITheme}\" ng-style=\"topPanelStyle()\">\r" +
  3563. "\n" +
  3564. " <div class=\"ngGroupPanel\" ng-show=\"showGroupPanel()\" ng-style=\"groupPanelStyle()\">\r" +
  3565. "\n" +
  3566. " <div class=\"ngGroupPanelDescription\" ng-show=\"configGroups.length == 0\">{{i18n.ngGroupPanelDescription}}</div>\r" +
  3567. "\n" +
  3568. " <ul ng-show=\"configGroups.length > 0\" class=\"ngGroupList\">\r" +
  3569. "\n" +
  3570. " <li class=\"ngGroupItem\" ng-repeat=\"group in configGroups\">\r" +
  3571. "\n" +
  3572. " <span class=\"ngGroupElement\">\r" +
  3573. "\n" +
  3574. " <span class=\"ngGroupName\">{{group.displayName}}\r" +
  3575. "\n" +
  3576. " <span ng-click=\"removeGroup($index)\" class=\"ngRemoveGroup\">x</span>\r" +
  3577. "\n" +
  3578. " </span>\r" +
  3579. "\n" +
  3580. " <span ng-hide=\"$last\" class=\"ngGroupArrow\"></span>\r" +
  3581. "\n" +
  3582. " </span>\r" +
  3583. "\n" +
  3584. " </li>\r" +
  3585. "\n" +
  3586. " </ul>\r" +
  3587. "\n" +
  3588. " </div>\r" +
  3589. "\n" +
  3590. " <div class=\"ngHeaderContainer\" ng-style=\"headerStyle()\">\r" +
  3591. "\n" +
  3592. " <div ng-header-row class=\"ngHeaderScroller\" ng-style=\"headerScrollerStyle()\"></div>\r" +
  3593. "\n" +
  3594. " </div>\r" +
  3595. "\n" +
  3596. " <div ng-grid-menu></div>\r" +
  3597. "\n" +
  3598. "</div>\r" +
  3599. "\n" +
  3600. "<div class=\"ngViewport\" unselectable=\"on\" ng-viewport ng-class=\"{'ui-widget-content': jqueryUITheme}\" ng-style=\"viewportStyle()\">\r" +
  3601. "\n" +
  3602. " <div class=\"ngCanvas\" ng-style=\"canvasStyle()\">\r" +
  3603. "\n" +
  3604. " <div ng-style=\"rowStyle(row)\" ng-repeat=\"row in renderedRows\" ng-click=\"row.toggleSelected($event)\" ng-class=\"row.alternatingRowClass()\" ng-row></div>\r" +
  3605. "\n" +
  3606. " </div>\r" +
  3607. "\n" +
  3608. "</div>\r" +
  3609. "\n" +
  3610. "<div ng-grid-footer></div>\r" +
  3611. "\n"
  3612. );
  3613. $templateCache.put('headerCellTemplate.html',
  3614. "<div class=\"ngHeaderSortColumn {{col.headerClass}}\" ng-style=\"{'cursor': col.cursor}\" ng-class=\"{ 'ngSorted': !col.noSortVisible() }\">\r" +
  3615. "\n" +
  3616. " <div ng-click=\"col.sort($event)\" ng-class=\"'colt' + col.index\" class=\"ngHeaderText\">{{col.displayName}}</div>\r" +
  3617. "\n" +
  3618. " <div class=\"ngSortButtonDown\" ng-click=\"col.sort($event)\" ng-show=\"col.showSortButtonDown()\"></div>\r" +
  3619. "\n" +
  3620. " <div class=\"ngSortButtonUp\" ng-click=\"col.sort($event)\" ng-show=\"col.showSortButtonUp()\"></div>\r" +
  3621. "\n" +
  3622. " <div class=\"ngSortPriority\">{{col.sortPriority}}</div>\r" +
  3623. "\n" +
  3624. " <div ng-class=\"{ ngPinnedIcon: col.pinned, ngUnPinnedIcon: !col.pinned }\" ng-click=\"togglePin(col)\" ng-show=\"col.pinnable\"></div>\r" +
  3625. "\n" +
  3626. "</div>\r" +
  3627. "\n" +
  3628. "<div ng-show=\"col.resizable\" class=\"ngHeaderGrip\" ng-click=\"col.gripClick($event)\" ng-mousedown=\"col.gripOnMouseDown($event)\"></div>\r" +
  3629. "\n"
  3630. );
  3631. $templateCache.put('headerRowTemplate.html',
  3632. "<div ng-style=\"{ height: col.headerRowHeight }\" ng-repeat=\"col in renderedColumns\" ng-class=\"col.colIndex()\" class=\"ngHeaderCell\">\r" +
  3633. "\n" +
  3634. "\t<div class=\"ngVerticalBar\" ng-style=\"{height: col.headerRowHeight}\" ng-class=\"{ ngVerticalBarVisible: !$last }\">&nbsp;</div>\r" +
  3635. "\n" +
  3636. "\t<div ng-header-cell></div>\r" +
  3637. "\n" +
  3638. "</div>"
  3639. );
  3640. $templateCache.put('menuTemplate.html',
  3641. "<div ng-show=\"showColumnMenu || showFilter\" class=\"ngHeaderButton\" ng-click=\"toggleShowMenu()\">\r" +
  3642. "\n" +
  3643. " <div class=\"ngHeaderButtonArrow\"></div>\r" +
  3644. "\n" +
  3645. "</div>\r" +
  3646. "\n" +
  3647. "<div ng-show=\"showMenu\" class=\"ngColMenu\">\r" +
  3648. "\n" +
  3649. " <div ng-show=\"showFilter\">\r" +
  3650. "\n" +
  3651. " <input placeholder=\"{{i18n.ngSearchPlaceHolder}}\" type=\"text\" ng-model=\"filterText\"/>\r" +
  3652. "\n" +
  3653. " </div>\r" +
  3654. "\n" +
  3655. " <div ng-show=\"showColumnMenu\">\r" +
  3656. "\n" +
  3657. " <span class=\"ngMenuText\">{{i18n.ngMenuText}}</span>\r" +
  3658. "\n" +
  3659. " <ul class=\"ngColList\">\r" +
  3660. "\n" +
  3661. " <li class=\"ngColListItem\" ng-repeat=\"col in columns | ngColumns\">\r" +
  3662. "\n" +
  3663. " <label><input ng-disabled=\"col.pinned\" type=\"checkbox\" class=\"ngColListCheckbox\" ng-model=\"col.visible\"/>{{col.displayName}}</label>\r" +
  3664. "\n" +
  3665. "\t\t\t\t<a title=\"Group By\" ng-class=\"col.groupedByClass()\" ng-show=\"col.groupable && col.visible\" ng-click=\"groupBy(col)\"></a>\r" +
  3666. "\n" +
  3667. "\t\t\t\t<span class=\"ngGroupingNumber\" ng-show=\"col.groupIndex > 0\">{{col.groupIndex}}</span> \r" +
  3668. "\n" +
  3669. " </li>\r" +
  3670. "\n" +
  3671. " </ul>\r" +
  3672. "\n" +
  3673. " </div>\r" +
  3674. "\n" +
  3675. "</div>"
  3676. );
  3677. $templateCache.put('rowTemplate.html',
  3678. "<div ng-style=\"{ 'cursor': row.cursor }\" ng-repeat=\"col in renderedColumns\" ng-class=\"col.colIndex()\" class=\"ngCell {{col.cellClass}}\">\r" +
  3679. "\n" +
  3680. "\t<div class=\"ngVerticalBar\" ng-style=\"{height: rowHeight}\" ng-class=\"{ ngVerticalBarVisible: !$last }\">&nbsp;</div>\r" +
  3681. "\n" +
  3682. "\t<div ng-cell></div>\r" +
  3683. "\n" +
  3684. "</div>"
  3685. );
  3686. }]);
  3687. }(window, jQuery));