repeater.js 89 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037
  1. 
  2. // ******* Repeater MANAGER ******** //
  3. $axure.internal(function($ax) {
  4. var _repeaterManager = {};
  5. $ax.repeater = _repeaterManager;
  6. //This is a mapping of current editItems
  7. var repeaterToEditItems = {};
  8. //This is a mapping of current filters
  9. var repeaterToFilters = {};
  10. // This is a mapping of current sorts
  11. var repeaterToSorts = {};
  12. // This is a mapping of repeater page info
  13. var repeaterToPageInfo = {};
  14. //Hopefully this can be simplified, but for now I think 3 are needed.
  15. //This is the data set that is owned by this repeater. The repeater may or may not reference this data set, and others can reference it.
  16. var repeaterToLocalDataSet = {};
  17. //This is the data set referenced by the repeater. It is not a copy of the local data set, but a reference to a local data set (or eventually a global data set could be referenced).
  18. var repeaterToCurrentDataSet = {};
  19. //This is a copy of the current data set, that is replaced whenever a set or refresh is done.
  20. var repeaterToActiveDataSet = {};
  21. var _loadRepeaters = function() {
  22. $ax(function(obj) {
  23. return $ax.public.fn.IsRepeater(obj.type);
  24. }).each(function(obj, repeaterId) {
  25. repeaterToLocalDataSet[repeaterId] = $ax.deepCopy(obj.data);
  26. repeaterToLocalDataSet[repeaterId].props = obj.dataProps;
  27. repeaterToEditItems[repeaterId] = [];
  28. _initPageInfo(obj, repeaterId);
  29. _setRepeaterDataSet(repeaterId, repeaterId);
  30. var initialItemIds = obj.repeaterPropMap.itemIds;
  31. for (var i = 0; i < initialItemIds.length; i++) $ax.addItemIdToRepeater(initialItemIds[i], repeaterId);
  32. $ax.visibility.initRepeater(repeaterId);
  33. });
  34. };
  35. _repeaterManager.load = _loadRepeaters;
  36. var _loaded = {};
  37. var _initRepeaters = function() {
  38. $ax(function(obj, repeaterId) {
  39. return $ax.public.fn.IsRepeater(obj.type) && !_loaded[repeaterId];
  40. }).each(function(obj, repeaterId) {
  41. _refreshRepeater(repeaterId, undefined, true);
  42. // Fix selected and default if necessary
  43. var states = obj.evaluatedStates;
  44. for(var i = 0; i < states.length; i++) {
  45. var state = states[i];
  46. $ax.style.SetWidgetEnabled(state.id, true); // So selected will take place. If disabled, selected wouldn't happen.
  47. $ax.style.SetWidgetSelected(state.id, state.selected);
  48. $ax.style.SetWidgetEnabled(state.id, !state.disabled);
  49. }
  50. });
  51. };
  52. _repeaterManager.initRefresh = _initRepeaters;
  53. var repeatersHaveNewDataSet = [];
  54. var _setRepeaterDataSet = function(repeaterId, dataSetId) {
  55. //TODO: No idea about how global data sets will be handled...
  56. repeaterToCurrentDataSet[repeaterId] = repeaterToLocalDataSet[dataSetId];
  57. repeaterToActiveDataSet[repeaterId] = getActiveDataSet(repeaterId);
  58. repeaterToFilters[repeaterId] = [];
  59. repeaterToSorts[repeaterId] = [];
  60. // Not using this currently
  61. // if(repeatersHaveNewDataSet.indexOf(repeaterId) == -1) repeatersHaveNewDataSet[repeatersHaveNewDataSet.length] = repeaterId;
  62. };
  63. _repeaterManager.setDataSet = _setRepeaterDataSet;
  64. var _refreshRepeater = function(repeaterId, eventInfo, itemsPregen) {
  65. // Don't show if you have a parent rdos thats limboed.
  66. var rdoPath = $ax.getPathFromScriptId(repeaterId);
  67. // Check each parent rdo through appropriate views to see if you are limboed
  68. while (rdoPath.length > 0) {
  69. if(!$ax.getScriptIdFromPath(rdoPath)) {
  70. removeItems(repeaterId);
  71. return;
  72. }
  73. $ax.splice(rdoPath, rdoPath.length - 1, 1);
  74. }
  75. _loaded[repeaterId] = true;
  76. $ax.action.refreshStart(repeaterId);
  77. $ax.style.ClearCacheForRepeater(repeaterId);
  78. if($ax.visibility.limboIds[repeaterId]) {
  79. removeItems(repeaterId);
  80. $ax.dynamicPanelManager.fitParentPanel(repeaterId);
  81. return;
  82. }
  83. // Remove delete map if there is one at this point
  84. if(eventInfo && eventInfo.repeaterDeleteMap) delete eventInfo.repeaterDeleteMap[repeaterId];
  85. var path = $ax.getPathFromScriptId(repeaterId);
  86. path.pop();
  87. if(eventInfo) {
  88. eventInfo = $ax.eventCopy(eventInfo);
  89. }
  90. var obj = $ax.getObjectFromScriptId(repeaterId);
  91. var propMap = obj.repeaterPropMap;
  92. //If there is no wrap, then set it to be above the number of rows
  93. var viewId = $ax.adaptive.currentViewId || '';
  94. var wrap = _getAdaptiveProp(propMap, 'wrap', viewId);
  95. var vertical = _getAdaptiveProp(propMap, 'vertical', viewId);
  96. var offset = propMap[viewId];
  97. // Right now pregen only works for default adaptive view
  98. if(viewId) itemsPregen = false;
  99. var orderedIds = [];
  100. if(itemsPregen) {
  101. var repeaterChildren = $jobj(repeaterId).children();
  102. // Start at 1 to skip script div child
  103. for(var i = 1; i < repeaterChildren.length; i++) {
  104. orderedIds.push(_getItemIdFromElementId($(repeaterChildren[i]).attr('id')));
  105. }
  106. } else orderedIds = getOrderedIds(repeaterId, eventInfo);
  107. var ids = [];
  108. var background = _getAdaptiveProp(propMap, 'backColor', viewId);
  109. var hasAltColor = _getAdaptiveProp(propMap, 'hasAltColor', viewId);
  110. var altColor = hasAltColor ? _getAdaptiveProp(propMap, 'altColor', viewId) : undefined;
  111. var useAlt = false;
  112. if(itemsPregen) {
  113. var start = 0;
  114. var end = orderedIds.length;
  115. } else {
  116. var bounds = _getVisibleDataBounds(repeaterToPageInfo[repeaterId], itemsPregen ? obj.data.length : orderedIds.length);
  117. start = bounds[0];
  118. end = bounds[1];
  119. }
  120. var preevalMap = {};
  121. if(itemsPregen) {
  122. var templateIds = [repeaterId];
  123. var processScriptIds = function (full, prop, id) {
  124. if(id.indexOf('_') <= 0 && id.indexOf('p') == -1) templateIds.push('u' + id);
  125. };
  126. $('#' + repeaterId + '_script').html().replace(/(id|for)="?u([0-9]+(p([0-9]){3})?(_[_a-z0-9]*)?)"?/g, processScriptIds);
  127. for(var i = 0; i < templateIds.length; i++) {
  128. for(var j = 0; j < orderedIds.length; j++) {
  129. ids.push(_createElementId(templateIds[i], orderedIds[j]));
  130. }
  131. }
  132. for(var pos = start; pos < end; pos++) {
  133. var itemId = orderedIds[pos];
  134. itemElementId = _createElementId(repeaterId, itemId);
  135. var jobj = $jobj(itemElementId);
  136. var preeval = jobj.hasClass('preeval');
  137. for(var i = 0; i < templateIds.length; i++) $ax.initializeObjectEvents($ax('#' + _createElementId(templateIds[i], itemId)), !preeval);
  138. if(preeval) {
  139. preevalMap[itemId] = true;
  140. jobj.removeClass('preeval');
  141. }
  142. }
  143. } else {
  144. var html = $('#' + repeaterId + '_script').html();
  145. // var container = $('<div></div>');
  146. // container.html(html);
  147. // container.attr('id', '' + repeaterId + '_container');
  148. // container.css({ position: 'absolute' });
  149. // container.offset({ left: -obj.x, top: -obj.y });
  150. var div = $('<div></div>');
  151. div.html(html);
  152. div.find('.' + $ax.visibility.HIDDEN_CLASS).removeClass($ax.visibility.HIDDEN_CLASS);
  153. div.find('.' + $ax.visibility.UNPLACED_CLASS).removeClass($ax.visibility.UNPLACED_CLASS);
  154. var paddingTop = _getAdaptiveProp(propMap, 'paddingTop', viewId);
  155. var paddingLeft = _getAdaptiveProp(propMap, 'paddingLeft', viewId);
  156. var paddingY = paddingTop + _getAdaptiveProp(propMap, 'paddingBottom', viewId);
  157. var paddingX = paddingLeft + _getAdaptiveProp(propMap, 'paddingRight', viewId);
  158. var spacingX = _getAdaptiveProp(propMap, 'horizontalSpacing', viewId);
  159. var xOffset = offset.width + spacingX;
  160. var spacingY = _getAdaptiveProp(propMap, 'verticalSpacing', viewId);
  161. var yOffset = offset.height + spacingY;
  162. div.css({
  163. width: offset.width,
  164. height: offset.height
  165. });
  166. _applyColorCss(background, div);
  167. var altDiv = div;
  168. if(hasAltColor) altDiv = _applyColorCss(altColor, div.clone());
  169. // Hide repeater, if shown, while updating.
  170. var shown = $ax.visibility.IsIdVisible(repeaterId);
  171. if(shown) document.getElementById(repeaterId).style.visibility = 'hidden';
  172. var resized = $ax.getItemIdsForRepeater(repeaterId).length != (end - start) ||
  173. (repeaterSizes[repeaterId] && repeaterSizes[repeaterId].resized);
  174. //clean up old items as late as possible
  175. removeItems(repeaterId);
  176. resetItemSizes(repeaterId, offset, bounds, orderedIds, vertical, wrap);
  177. var i = 0;
  178. var top = paddingTop;
  179. var left = paddingLeft;
  180. for(pos = start; pos < end; pos++) {
  181. itemId = orderedIds[pos];
  182. var itemElementId = _createElementId(repeaterId, itemId);
  183. $ax.addItemIdToRepeater(itemId, repeaterId);
  184. ids.push(itemElementId);
  185. var processId = function(full, prop, id) {
  186. var elementId = _createElementId('u' + id, itemId);
  187. //If there is a suffix (ex. _img), then don't push the id.
  188. if (id.indexOf('_') <= 0 && id.indexOf('p') == -1) ids.push(elementId);
  189. return prop + '="' + elementId + '"';
  190. };
  191. var copy = (useAlt ? altDiv : div).clone();
  192. useAlt = !useAlt;
  193. copy.attr('id', itemElementId);
  194. copy.html(div.html().replace(/(id|for)="?u([0-9]+(p([0-9]){3})?(_[_a-z0-9]*)?)"?/g, processId));
  195. copy.css({
  196. 'position': 'absolute',
  197. 'top': top + 'px',
  198. 'left': left + 'px',
  199. 'width': obj.width + 'px',
  200. 'height': obj.height + 'px'
  201. });
  202. $('#' + repeaterId).append(copy);
  203. i++;
  204. if(wrap != -1 && i % wrap == 0) {
  205. if(vertical) {
  206. top = paddingTop;
  207. left += xOffset;
  208. } else {
  209. left = paddingLeft;
  210. top += yOffset;
  211. }
  212. } else if (vertical) top += yOffset;
  213. else left += xOffset;
  214. }
  215. var shownCount = end - start;
  216. var repeaterSize = { width: paddingX, height: paddingY};
  217. if(shownCount > 0) {
  218. var primaryCount = wrap == -1 ? shownCount : Math.min(shownCount, wrap);
  219. var secondaryCount = wrap == -1 ? 1 : Math.ceil(shownCount / wrap);
  220. var widthCount = vertical ? secondaryCount : primaryCount;
  221. var heightCount = vertical ? primaryCount : secondaryCount;
  222. repeaterSize.width += offset.width + (widthCount - 1) * xOffset;
  223. repeaterSize.height += offset.height + (heightCount - 1) * yOffset;
  224. }
  225. var borderWidth = $ax.getNumFromPx($jobj(repeaterId).css('border-width')) || 0;
  226. repeaterSize.width += borderWidth * 2;
  227. repeaterSize.height += borderWidth * 2;
  228. $jobj(repeaterId).css(repeaterSize);
  229. // TODO: Should be able to combine this with initialization done in pregen items. Just need to have ids and template ids be the same.
  230. for(var i = 0; i < ids.length; i++) $ax.initializeObjectEvents($ax('#' + ids[i]), true);
  231. }
  232. var query = _getItemQuery(repeaterId);
  233. if(viewId) $ax.adaptive.applyView(viewId, query);
  234. else $ax.visibility.resetLimboAndHiddenToDefaults(_getItemQuery(repeaterId, preevalMap));
  235. // else {
  236. // var limbo = {};
  237. // var hidden = {};
  238. // query.each(function(diagramObject, elementId) {
  239. // // sigh, javascript. we need the === here because undefined means not overriden
  240. //// var visible = diagramObject.style.visible;
  241. // var visible = $ax.visibility.IsIdVisible(elementId);
  242. // if (visible === false) hidden[elementId] = true;
  243. // //todo: **mas** check if the limboed widgets are hidden by default by the generator
  244. // if(diagramObject.style.limbo) limbo[elementId] = true;
  245. // });
  246. // $ax.visibility.addLimboAndHiddenIds(limbo, hidden, query);
  247. // $ax.dynamicPanelManager.updatePercentPanelCache(query);
  248. // }
  249. $ax.annotation.InitializeAnnotations(query);
  250. for(var index = 0; index < ids.length; index++) {
  251. var id = ids[index];
  252. var childObj = $obj(id);
  253. var childJobj = $jobj(id);
  254. var childItemId = _getItemIdFromElementId(id);
  255. if (obj.repeaterPropMap.isolateRadio && $ax.public.fn.IsRadioButton(childObj.type)) {
  256. var input = $jobj(_applySuffixToElementId(id, '_input'));
  257. input.attr('name', _createElementId(input.attr('name'), childItemId));
  258. if($ax.ieColorManager) $ax.ieColorManager.applyBackground($ax('#' + id));
  259. }
  260. if(obj.repeaterPropMap.isolateSelection && childJobj.attr('selectiongroup')) {
  261. childJobj.attr('selectiongroup', _createElementId(childJobj.attr('selectiongroup'), childItemId));
  262. }
  263. // Had to move this earlier, because it sets up cursor: pointer on inline links,
  264. // but must be done before style cached when adaptive view is set.
  265. //$ax.initializeObjectEvents($ax('#' + id));
  266. $ax.dynamicPanelManager.initFitPanels($ax('#' + id));
  267. $ax.style.initializeObjectTextAlignment($ax('#' + id));
  268. $ax.applyHighlight($ax('#' + id), true);
  269. }
  270. //$ax.event.raiseSyntheticEvent(itemElementId, 'onLoad', true);
  271. //$ax.loadDynamicPanelsAndMasters(obj.objects, path, itemId);
  272. // Now load
  273. for(pos = start; pos < end; pos++) {
  274. itemId = orderedIds[pos];
  275. itemElementId = _createElementId(repeaterId, itemId);
  276. if(!preevalMap[orderedIds[pos]]) $ax.event.raiseSyntheticEvent(itemElementId, 'onItemLoad', true);
  277. $ax.loadDynamicPanelsAndMasters(obj.objects, path, itemId);
  278. }
  279. // Reshow repeater if it was originally shown (load is complete by now)
  280. if(shown && !itemsPregen) document.getElementById(repeaterId).style.visibility = 'visible';
  281. $ax.dynamicPanelManager.fitParentPanel(repeaterId);
  282. // Right now we assume only one refresh at a time. If we can manually trigger refreshes, that may possibly change.
  283. $ax.action.refreshEnd();
  284. };
  285. _repeaterManager.refreshRepeater = _refreshRepeater;
  286. var _getItemQuery = function(repeaterId, preevalMap) {
  287. var query = $ax(function (diagramObject, elementId) {
  288. // Also need to check that this in not preeval
  289. if(preevalMap) {
  290. var itemId = _getItemIdFromElementId(elementId);
  291. if(preevalMap[itemId]) return false;
  292. }
  293. // All objects with the repeater as their parent, except the repeater itself.
  294. var scriptId = _getScriptIdFromElementId(elementId);
  295. return $ax.getParentRepeaterFromScriptId(scriptId) == repeaterId && scriptId != repeaterId;
  296. });
  297. return query;
  298. }
  299. _repeaterManager.refreshAllRepeaters = function() {
  300. $ax('*').each(function(diagramObject, elementId) {
  301. if (!$ax.public.fn.IsRepeater(diagramObject.type)) return;
  302. _initPageInfo(diagramObject, elementId);
  303. _refreshRepeater(elementId, $ax.getEventInfoFromEvent($ax.getjBrowserEvent()));
  304. });
  305. };
  306. _repeaterManager.refreshRepeaters = function(ids, eventInfo) {
  307. for(var i = 0; i < ids.length; i++) _refreshRepeater(ids[i], eventInfo);
  308. };
  309. var _initPageInfo = function(obj, elementId) {
  310. var pageInfo = {};
  311. var map = obj.repeaterPropMap;
  312. var currentViewId = $ax.adaptive.currentViewId || '';
  313. var itemsPerPage = _getAdaptiveProp(map, 'itemsPerPage', currentViewId);
  314. if(itemsPerPage == -1) pageInfo.noLimit = true;
  315. else {
  316. pageInfo.itemsPerPage = itemsPerPage;
  317. pageInfo.currPage = _getAdaptiveProp(map, 'currPage', currentViewId);
  318. }
  319. repeaterToPageInfo[elementId] = pageInfo;
  320. };
  321. _repeaterManager.initialize = function() {
  322. $ax(function (obj) {
  323. return $ax.public.fn.IsRepeater(obj.type);
  324. }).each(function (obj, repeaterId) {
  325. _initPregen(repeaterId);
  326. });
  327. }
  328. var _initPregen = function(repeaterId) {
  329. var obj = $ax.getObjectFromScriptId(repeaterId);
  330. var propMap = obj.repeaterPropMap;
  331. //If there is no wrap, then set it to be above the number of rows
  332. var viewId = $ax.adaptive.currentViewId || '';
  333. var wrap = _getAdaptiveProp(propMap, 'wrap', viewId);
  334. var vertical = _getAdaptiveProp(propMap, 'vertical', viewId);
  335. var orderedIds = [];
  336. var ids = [];
  337. var background = _getAdaptiveProp(propMap, 'backColor', viewId);
  338. var hasAltColor = _getAdaptiveProp(propMap, 'hasAltColor', viewId);
  339. var altColor = hasAltColor ? _getAdaptiveProp(propMap, 'altColor', viewId) : undefined;
  340. var useAlt = false;
  341. var bounds = _getVisibleDataBounds(repeaterToPageInfo[repeaterId], obj.data.length);
  342. var start = bounds[0];
  343. var end = bounds[1];
  344. // Starts empty
  345. if(start == end) {
  346. $ax.action.refreshEnd(repeaterId);
  347. return;
  348. }
  349. var unprocessedBaseIds = $jobj($ax.repeater.createElementId(repeaterId, start + 1)).html().match(/(id|for)="?u([0-9]+)/g);
  350. var baseIds = [];
  351. if(unprocessedBaseIds) {
  352. for(var i = 0; i < unprocessedBaseIds.length; i++) {
  353. var val = unprocessedBaseIds[i].split('=')[1].substr(1);
  354. if(baseIds.indexOf(val) == -1) baseIds.push(val);
  355. }
  356. }
  357. for(var itemNum = start; itemNum < end; itemNum++) {
  358. ids.push($ax.repeater.createElementId(repeaterId, itemNum + 1));
  359. for(i = 0; i < baseIds.length; i++) ids.push($ax.repeater.createElementId(baseIds[i], itemNum + 1));
  360. var itemId = itemNum + 1;
  361. orderedIds[itemNum] = itemId;
  362. var itemDiv = $jobj($ax.repeater.createElementId(repeaterId, itemNum + 1));
  363. _applyColorCss(useAlt ? altColor : background, itemDiv);
  364. if(hasAltColor) useAlt = !useAlt;
  365. }
  366. resetItemSizes(repeaterId, undefined, bounds, orderedIds, vertical, wrap);
  367. };
  368. var _applyColorCss = function(json, div) {
  369. var args = json.r + ', ' + json.g + ', ' + json.b;
  370. var background = json.a == 0 ? '' : json.a == 1 ? 'rgb(' + args + ')' : 'rgba(' + args + ', ' + json.a + ')';
  371. if($ax.ieColorManager && json.a != 0 && json.a != 1) {
  372. var ieColor = $ax.ieColorManager.getColorFromArgb(json.a * 255, json.r, json.g, json.b, true);
  373. if(ieColor) background = ieColor;
  374. }
  375. div.css('background-color', background);
  376. return div;
  377. };
  378. var _getAdaptiveProp = _repeaterManager.getAdaptiveProp = function(map, prop, viewId) {
  379. var viewChain = $ax.adaptive.getAdaptiveIdChain(viewId);
  380. for(var i = viewChain.length - 1; i >= 0; i--) {
  381. viewId = viewChain[i];
  382. var viewProps = map[viewId];
  383. if(viewProps.hasOwnProperty(prop)) return viewProps[prop];
  384. }
  385. var base = map[''];
  386. if(base.hasOwnProperty(prop)) return base[prop];
  387. return map['default'][prop];
  388. };
  389. _repeaterManager.getItemCount = function(repeaterId) {
  390. var data = repeaterToActiveDataSet[repeaterId].length;
  391. var info = repeaterToPageInfo[repeaterId];
  392. if(!info.noLimit) {
  393. var start = Math.min(data, info.itemsPerPage * info.currPage);
  394. var end = Math.min(data, start + info.itemsPerPage);
  395. data = end - start;
  396. }
  397. return data;
  398. };
  399. _repeaterManager.setDisplayProps = function(obj, repeaterId, itemIndex) {
  400. var data = repeaterToActiveDataSet[repeaterId];
  401. var info = repeaterToPageInfo[repeaterId];
  402. var start = 0;
  403. var end = data.length;
  404. if(!info.noLimit) {
  405. start = Math.min(end, info.itemsPerPage * (info.currPage - 1));
  406. end = Math.min(end, start + info.itemsPerPage);
  407. }
  408. var count = end - start;
  409. var index = -1;
  410. for(var i = 0; i < count; i++) {
  411. if(data[start + i].index == itemIndex) index = i + 1;
  412. }
  413. if(index == -1) return;
  414. obj.index = index;
  415. obj.isfirst = index == 1;
  416. obj.islast = index == end - start;
  417. obj.iseven = index % 2 == 0;
  418. obj.isodd = index % 2 == 1;
  419. };
  420. var _getVisibleDataBounds = function(pageInfo, count) {
  421. var retval = [0, count];
  422. if(!pageInfo.noLimit) {
  423. var end = pageInfo.itemsPerPage * pageInfo.currPage;
  424. var start = end - pageInfo.itemsPerPage;
  425. // If past the end, move to last page
  426. if(start >= count) {
  427. pageInfo.currPage = Math.floor((count - 1) / pageInfo.itemsPerPage) + 1;
  428. if(pageInfo.currPage <= 0) pageInfo.currPage = 1;
  429. end = pageInfo.itemsPerPage * pageInfo.currPage;
  430. start = end - pageInfo.itemsPerPage;
  431. }
  432. end = Math.min(end, count);
  433. retval[0] = start;
  434. retval[1] = end;
  435. }
  436. return retval;
  437. };
  438. _repeaterManager.getVisibleDataCount = function(repeaterId) {
  439. var bounds = _getVisibleDataBounds(repeaterToPageInfo[repeaterId], repeaterToActiveDataSet[repeaterId].length);
  440. return bounds[1] - bounds[0];
  441. };
  442. _repeaterManager.getDataCount = function(repeaterId) {
  443. return repeaterToCurrentDataSet[repeaterId].length;
  444. };
  445. var _getFilteredDataCount = _repeaterManager.getFilteredDataCount = function(repeaterId) {
  446. return repeaterToActiveDataSet[repeaterId].length;
  447. };
  448. _repeaterManager.getPageCount = function(repeaterId) {
  449. var info = repeaterToPageInfo[repeaterId];
  450. return info.noLimit ? 1 : Math.ceil(_getFilteredDataCount(repeaterId) / info.itemsPerPage);
  451. };
  452. _repeaterManager.getPageIndex = function(repeaterId) {
  453. var info = repeaterToPageInfo[repeaterId];
  454. return info.noLimit ? 1 : info.currPage;
  455. };
  456. var getActiveDataSet = function(repeaterId) {
  457. var active = $ax.deepCopy(repeaterToCurrentDataSet[repeaterId]);
  458. // Set up 1 indexing each item.
  459. for(var i = 0; i < active.length; i++) active[i].index = i + 1;
  460. return active;
  461. };
  462. var getOrderedIds = function(repeaterId, eventInfo) {
  463. var data = repeaterToActiveDataSet[repeaterId] = getActiveDataSet(repeaterId);
  464. // Filter first so less to sort
  465. applyFilter(repeaterId, data, eventInfo);
  466. // Sort next
  467. var sorts = repeaterToSorts[repeaterId] || [];
  468. if(sorts.length != 0 && data.length > 1) {
  469. // TODO: Make this generic and factor out if we want to use it elsewhere...
  470. // Compare is a function that takes 2 arguments, and returns a number. A high number means the second should go first
  471. // Otherwise the first stays first.
  472. var mergesort = function(list, start, end, compare) {
  473. var middle = Math.floor((start + end) / 2);
  474. if(middle - start > 1) mergesort(list, start, middle, compare);
  475. if(end - middle > 1) mergesort(list, middle, end, compare);
  476. var index1 = start;
  477. var index2 = middle;
  478. var tempList = [];
  479. while(index1 < middle && index2 < end) {
  480. tempList[tempList.length] = list[compare(list[index1], list[index2]) > 0 ? index2++ : index1++];
  481. }
  482. while(index1 < middle) tempList[tempList.length] = list[index1++];
  483. while(index2 < end) tempList[tempList.length] = list[index2++];
  484. // transfer from temp list to the real list.
  485. for(var i = 0; i < tempList.length; i++) list[start + i] = tempList[i];
  486. };
  487. // Compare is the tie breaking function to us if necessary.
  488. var getComparator = function(columnName, ascending, type, compare) {
  489. // If this needs to be sped up, break up into several smaller functions conditioned off of type
  490. return function(row1, row2) {
  491. // If column undefined, no way to measure this, so call it a tie.
  492. if(row1[columnName] === undefined || row2[columnName] === undefined) return 0;
  493. var text1 = row1[columnName].text;
  494. var text2 = row2[columnName].text;
  495. // This means we are case insensitive, so lowercase everything to kill casing
  496. if(type == 'Text') {
  497. text1 = text1.toLowerCase();
  498. text2 = text2.toLowerCase();
  499. }
  500. //If tied, go to tie breaker
  501. if(text1 == text2) {
  502. if(compare) return compare(row1, row2);
  503. // Actually a tie.
  504. return 0;
  505. }
  506. if(type == 'Text' || type == 'Text (Case Sensitive)') {
  507. if(text1 < text2 ^ ascending) return 1;
  508. else return -1;
  509. } else if(type == 'Number') {
  510. var num1 = Number(text1);
  511. var num2 = Number(text2);
  512. if(isNaN(num1) && isNaN(num2)) return 0;
  513. if(isNaN(num1) || isNaN(num2)) return isNaN(num1) ? 1 : -1;
  514. if(num1 < num2 ^ ascending) return 1;
  515. else return -1;
  516. } else if(type == 'Date - YYYY-MM-DD' || type == 'Date - MM/DD/YYYY') {
  517. var func = type == 'Date - YYYY-MM-DD' ? getDate1 : getDate2;
  518. var date1 = func(text1);
  519. var date2 = func(text2);
  520. if(!date1.valid && !date2.valid) return 0;
  521. if(!date1.valid || !date2.valid) return date1.valid ? -1 : 1;
  522. var diff = date2.year - date1.year;
  523. if(diff == 0) diff = date2.month - date1.month;
  524. if(diff == 0) diff = date2.day - date1.day;
  525. if(diff == 0) return 0;
  526. return diff > 0 ^ ascending ? 1 : -1;
  527. }
  528. console.log('unhandled sort type');
  529. return 0;
  530. };
  531. };
  532. var compareFunc = null;
  533. for(var i = 0; i < sorts.length; i++) compareFunc = getComparator(sorts[i].columnName, sorts[i].ascending, sorts[i].sortType, compareFunc);
  534. mergesort(data, 0, data.length, compareFunc);
  535. }
  536. var ids = [];
  537. for(i = 0; i < data.length; i++) ids[i] = data[i].index;
  538. return ids;
  539. };
  540. var getDate1 = function(text) {
  541. var date = { valid: false };
  542. var sections = text.split('-');
  543. if(sections.length == 1) sections = text.split('/');
  544. if(sections.length != 3) return date;
  545. date.year = Number(sections[0]);
  546. date.month = Number(sections[1]);
  547. date.day = Number(sections[2]);
  548. date.valid = !isNaN(date.year);
  549. date.valid &= !isNaN(date.month) && date.month > 0 && date.month <= 12;
  550. date.valid &= !isNaN(date.day) && date.day > 0 && date.day <= daysPerMonth(date.month, date.year);
  551. return date;
  552. };
  553. var getDate2 = function(text) {
  554. var date = { valid: false };
  555. var sections = text.split('-');
  556. if(sections.length == 1) sections = text.split('/');
  557. if(sections.length != 3) return date;
  558. date.month = Number(sections[0]);
  559. date.day = Number(sections[1]);
  560. date.year = Number(sections[2]);
  561. date.valid = !isNaN(date.year);
  562. date.valid &= !isNaN(date.month) && date.month > 0 && date.month <= 12;
  563. date.valid &= !isNaN(date.day) && date.day > 0 && date.day <= daysPerMonth(date.month, date.year);
  564. return date;
  565. };
  566. var daysPerMonth = function(month, year) {
  567. if(month == 9 || month == 4 || month == 6 || month == 11) return 30;
  568. if(month != 2) return 31;
  569. if(year % 4 != 0) return 28;
  570. if(year % 100 != 0) return 29;
  571. return year % 400 == 0 ? 29 : 28;
  572. };
  573. var applyFilter = function(repeaterId, data, eventInfo) {
  574. var dataFiltered = [];
  575. var filters = repeaterToFilters[repeaterId] || [];
  576. if (filters.length != 0) {
  577. var oldTarget = eventInfo.targetElement;
  578. var oldSrc = eventInfo.srcElement;
  579. var oldThis = eventInfo.thiswidget;
  580. var oldItem = eventInfo.item;
  581. var idToWidgetInfo = {};
  582. outer:
  583. for(var i = 1; i <= data.length; i++) {
  584. for(var j = 0; j < filters.length; j++) {
  585. eventInfo.targetElement = _createElementId(repeaterId, i);
  586. eventInfo.srcElement = filters[j].thisId;
  587. if(!idToWidgetInfo[eventInfo.srcElement]) idToWidgetInfo[eventInfo.srcElement] = $ax.getWidgetInfo(eventInfo.srcElement);
  588. eventInfo.thiswidget = idToWidgetInfo[eventInfo.srcElement];
  589. eventInfo.item = $ax.getItemInfo(eventInfo.srcElement);
  590. if($ax.expr.evaluateExpr(filters[j].filter, eventInfo) != 'true') continue outer;
  591. }
  592. dataFiltered[dataFiltered.length] = data[i - 1];
  593. }
  594. for(i = 0; i < dataFiltered.length; i++) data[i] = dataFiltered[i];
  595. while(data.length > dataFiltered.length) data.pop();
  596. eventInfo.targetElement = oldTarget;
  597. eventInfo.srcElement = oldSrc;
  598. eventInfo.thiswidget = oldThis;
  599. eventInfo.item = oldItem;
  600. }
  601. };
  602. var _addFilter = function(repeaterId, removeOtherFilters, label, filter, thisId) {
  603. if(removeOtherFilters) _removeFilter(repeaterId);
  604. var filterList = repeaterToFilters[repeaterId];
  605. if(!filterList) repeaterToFilters[repeaterId] = filterList = [];
  606. var filterObj = { filter: filter, thisId: thisId };
  607. if(label) filterObj.label = label;
  608. filterList[filterList.length] = filterObj;
  609. };
  610. _repeaterManager.addFilter = _addFilter;
  611. var _removeFilter = function(repeaterId, label) {
  612. var filterList = repeaterToFilters[repeaterId];
  613. // If no list, nothing to remove
  614. if(!filterList) return;
  615. // If no label, remove everything
  616. if(!label) {
  617. repeaterToFilters[repeaterId] = [];
  618. return;
  619. }
  620. for(var i = filterList.length - 1; i >= 0; i--) {
  621. var filterObj = filterList[i];
  622. if(filterObj.label && filterObj.label == label) $ax.splice(filterList, i, 1);
  623. }
  624. };
  625. _repeaterManager.removeFilter = _removeFilter;
  626. var _addSort = function(repeaterId, label, columnName, ascending, toggle, sortType) {
  627. var sortList = repeaterToSorts[repeaterId];
  628. if(!sortList) repeaterToSorts[repeaterId] = sortList = [];
  629. for(var i = 0; i < sortList.length; i++) {
  630. if(columnName == sortList[i].columnName) {
  631. var lastSortObj = $ax.splice(sortList, i, 1)[0];
  632. if(toggle) ascending = !lastSortObj.ascending;
  633. break;
  634. }
  635. }
  636. var sortObj = { columnName: columnName, ascending: ascending, sortType: sortType };
  637. if(label) sortObj.label = label;
  638. sortList[sortList.length] = sortObj;
  639. };
  640. _repeaterManager.addSort = _addSort;
  641. var _removeSort = function(repeaterId, label) {
  642. var sortList = repeaterToSorts[repeaterId];
  643. // If no list, nothing to remove
  644. if(!sortList) return;
  645. // If no label, remove everything
  646. if(!label) {
  647. repeaterToSorts[repeaterId] = [];
  648. return;
  649. }
  650. for(var i = sortList.length - 1; i >= 0; i--) {
  651. var sortObj = sortList[i];
  652. if(sortObj.label && sortObj.label == label) $ax.splice(sortList, i, 1);
  653. }
  654. };
  655. _repeaterManager.removeSort = _removeSort;
  656. var _setRepeaterToPage = function(repeaterId, type, value, eventInfo) {
  657. var pageInfo = repeaterToPageInfo[repeaterId];
  658. // page doesn't matter if there is no limit.
  659. if(pageInfo.noLimit) return;
  660. var dataSet = repeaterToActiveDataSet[repeaterId];
  661. if(!dataSet) dataSet = repeaterToCurrentDataSet[repeaterId];
  662. var lastPage = Math.max(1, Math.ceil(dataSet.length / pageInfo.itemsPerPage));
  663. if(type == 'Value') {
  664. var val = Number($ax.expr.evaluateExpr(value, eventInfo));
  665. // if invalid, default to 1, otherwise, clamp the value
  666. if(isNaN(val)) val = 1;
  667. else if(val < 1) val = 1;
  668. else if(val > lastPage) val = lastPage;
  669. pageInfo.currPage = val;
  670. } else if(type == 'Previous') {
  671. if(pageInfo.currPage > 1) pageInfo.currPage--;
  672. } else if(type == 'Next') {
  673. if(pageInfo.currPage < lastPage) pageInfo.currPage++;
  674. } else if(type == 'Last') {
  675. pageInfo.currPage = lastPage;
  676. } else {
  677. console.log('Unknown type');
  678. }
  679. };
  680. _repeaterManager.setRepeaterToPage = _setRepeaterToPage;
  681. var _setNoItemLimit = function(repeaterId) {
  682. var pageInfo = repeaterToPageInfo[repeaterId];
  683. delete pageInfo.currPage;
  684. delete pageInfo.itemsPerPage;
  685. pageInfo.noLimit = true;
  686. };
  687. _repeaterManager.setNoItemLimit = _setNoItemLimit;
  688. var _setItemLimit = function(repeaterId, value, eventInfo) {
  689. var pageInfo = repeaterToPageInfo[repeaterId];
  690. if(pageInfo.noLimit) {
  691. pageInfo.noLimit = false;
  692. pageInfo.currPage = 1;
  693. }
  694. var oldTarget = eventInfo.targetElement;
  695. eventInfo.targetElement = repeaterId;
  696. var itemLimit = Number($ax.expr.evaluateExpr(value, eventInfo));
  697. eventInfo.targetElement = oldTarget;
  698. if(isNaN(itemLimit)) itemLimit = 20;
  699. else if(itemLimit < 1) itemLimit = 1;
  700. pageInfo.itemsPerPage = itemLimit;
  701. };
  702. _repeaterManager.setItemLimit = _setItemLimit;
  703. var removeItems = function(repeaterId) {
  704. var elementIds = $ax.getChildElementIdsForRepeater(repeaterId);
  705. var itemId = $ax.getItemIdsForRepeater(repeaterId);
  706. for(var i = 0; i < itemId.length; i++) $jobj(_createElementId(repeaterId, itemId[i])).remove();
  707. $ax.visibility.clearLimboAndHiddenIds(elementIds);
  708. $ax.clearItemsForRepeater(repeaterId);
  709. };
  710. var repeaterSizes = {};
  711. var resetItemSizes = function (repeaterId, itemSize, bounds, ids, vertical, wrap) {
  712. var calcItem = !itemSize;
  713. if(calcItem) itemSize = {};
  714. var repeaterMap = {};
  715. repeaterMap.vert = vertical;
  716. var sizesMap = {};
  717. var sizes = [];
  718. var currSizes = wrap == -1 ? sizes : [];
  719. for(var i = 0; i + bounds[0] < bounds[1]; i++) {
  720. var itemId = ids[i + bounds[0]];
  721. if(calcItem) {
  722. var itemJobj = $jobj(_createElementId(repeaterId, itemId));
  723. itemSize.width = $ax.getNumFromPx(itemJobj.css('width'));
  724. itemSize.height = $ax.getNumFromPx(itemJobj.css('height'));
  725. }
  726. var size = { itemId: itemId, width: itemSize.width, height: itemSize.height };
  727. currSizes.push(size);
  728. sizesMap[size.itemId] = size;
  729. if(currSizes.length == wrap) {
  730. sizes.push(currSizes);
  731. currSizes = [];
  732. }
  733. }
  734. if (wrap != -1 && currSizes.length > 0) sizes.push(currSizes);
  735. repeaterMap.sizes = sizes;
  736. repeaterMap.sizesMap = sizesMap;
  737. repeaterSizes[repeaterId] = repeaterMap;
  738. };
  739. _repeaterManager.getItemSize = function(repeaterId, itemId) {
  740. var repeaterSize = repeaterSizes[repeaterId];
  741. if (!repeaterSize) return false;
  742. return repeaterSize.sizesMap[itemId];
  743. }
  744. _repeaterManager.setItemSize = function (repeaterId, itemId, width, height) {
  745. var repeaterSize = repeaterSizes[repeaterId];
  746. if(!repeaterSize) return false;
  747. var size = repeaterSize.sizesMap[itemId];
  748. var deltaX = width - size.width;
  749. var deltaY = height - size.height;
  750. if(!deltaX && !deltaY) return false;
  751. repeaterSize.resized = true;
  752. if(deltaX) _pushItems(repeaterId, itemId, deltaX, false, true);
  753. if(deltaY) _pushItems(repeaterId, itemId, deltaY, true, true);
  754. if(deltaX || deltaY) $ax.event.raiseSyntheticEvent(_createElementId(repeaterId, itemId), 'onItemResize');
  755. return true;
  756. }
  757. var _pushItems = _repeaterManager.pushItems = function (repeaterId, itemId, delta, vertical, suppressFire) {
  758. if(delta == 0) return;
  759. // Update repeater item size
  760. var prop = vertical ? 'height' : 'width';
  761. var itemObj = $jobj(_createElementId(repeaterId, itemId));
  762. itemObj.css(prop, $ax.getNumFromPx(itemObj.css(prop)) + delta);
  763. var repeaterObj = $jobj(repeaterId);
  764. var repeaterMap = repeaterSizes[repeaterId];
  765. var sizes = repeaterMap.sizes;
  766. var wrap = sizes[0].length != undefined;
  767. var vert = repeaterMap.vert;
  768. // Not wrapping, has to push in primary direction
  769. if (!wrap && vert != vertical) {
  770. var before = 0;
  771. var after = 0;
  772. var limit = 0;
  773. for(var i = 0; i < sizes.length; i++) {
  774. var size = sizes[i];
  775. if(size.itemId == itemId) {
  776. before = size[prop];
  777. size[prop] += delta;
  778. after = size[prop];
  779. } else {
  780. limit = limit ? Math.max(limit, size[prop]) : size[prop];
  781. }
  782. }
  783. // Repeater delta is because an item can increase secondary direction, but if another item is already larger, then repeater size isn't effected.
  784. var repeaterDelta = delta;
  785. if(sizes.length != 1) {
  786. if(after >= limit) repeaterDelta = after - Math.max(limit, before);
  787. else if(before > limit) repeaterDelta = limit - before;
  788. else repeaterDelta = 0;
  789. }
  790. _updateRepeaterSize(prop, repeaterObj, repeaterDelta);
  791. if(!suppressFire) $ax.event.raiseSyntheticEvent(_createElementId(repeaterId, itemId), 'onItemResize');
  792. return;
  793. }
  794. var index = 0;
  795. var index2 = 0;
  796. // Get the indices first
  797. if(wrap) {
  798. outer:
  799. for(; index < sizes.length; index++) {
  800. var innerSizes = sizes[index];
  801. for(index2 = 0; index2 < innerSizes.length; index2++) if(innerSizes[index2].itemId == itemId) break outer;
  802. }
  803. } else {
  804. for(; index < sizes.length; index++) if(sizes[index].itemId == itemId) break;
  805. }
  806. // Find out who is being pushed
  807. var itemIdsEffected = [];
  808. if (vert == vertical) {
  809. // To check for repeater resize, non-wrap is easy, for wrap you have to see if your new size is enough to effect the size given other col/row sizes.
  810. repeaterDelta = delta;
  811. if(wrap && sizes.length > 1) {
  812. var viewId = $ax.adaptive.currentViewId || '';
  813. var spacing = _getAdaptiveProp($obj(repeaterId).repeaterPropMap, (vert ? 'vertical' : 'horizontal') + 'Spacing', viewId);
  814. for(i = 0; i < sizes.length; i++) {
  815. var rowColSize = 0;
  816. var rowCol = sizes[i];
  817. for(var j = 0; j < rowCol.length; j++) {
  818. if(j != 0) rowColSize += spacing;
  819. rowColSize += rowCol[j][prop];
  820. }
  821. if(i == index) {
  822. before = rowColSize;
  823. after = before + delta;
  824. } else {
  825. limit = limit ? Math.max(limit, rowColSize) : rowColSize;
  826. }
  827. }
  828. if(after >= limit) repeaterDelta = after - Math.max(limit, before);
  829. else if (before > limit) repeaterDelta = limit - before;
  830. else repeaterDelta = 0;
  831. }
  832. if (repeaterDelta) {
  833. _updateRepeaterSize(prop, repeaterObj, repeaterDelta);
  834. }
  835. // Done the hard part, calculating/updating new repeater size. Now just resize items and find what to push.
  836. var array = wrap ? sizes[index] : sizes;
  837. i = wrap ? index2 : index;
  838. array[i][prop] += delta;
  839. for(i++; i < array.length; i++) itemIdsEffected.push(array[i].itemId);
  840. } else {
  841. // Secondary push is more interesting. See how much your primary row/column is already pushing, if that changes
  842. // then effect all rows/columns after it
  843. // Get the biggest one in the current row/column, ignoring the one we're changing
  844. var biggest = 0;
  845. var currSizes = sizes[index];
  846. for(i = 0; i < currSizes.length; i++) {
  847. if (i == index2) continue;
  848. biggest = Math.max(biggest, currSizes[i][prop]);
  849. }
  850. var beforeSize = Math.max(biggest, currSizes[index2][prop]);
  851. currSizes[index2][prop] += delta;
  852. var afterSize = Math.max(biggest, currSizes[index2][prop]);
  853. // Nothing pushed/pulled
  854. if (afterSize == beforeSize) return;
  855. for(i = index + 1; i < sizes.length; i++) {
  856. currSizes = sizes[i];
  857. for(j = 0; j < currSizes.length; j++) itemIdsEffected.push(currSizes[j].itemId);
  858. }
  859. // Delta is only how much the whole row/column changed
  860. delta = afterSize - beforeSize;
  861. // Repeater resize secondary is determined by the effective delta.
  862. _updateRepeaterSize(prop, repeaterObj, delta);
  863. }
  864. for(i = 0; i < itemIdsEffected.length; i++) {
  865. var currItemId = itemIdsEffected[i];
  866. var elementId = _createElementId(repeaterId, currItemId);
  867. var loc = vertical ? 'top' : 'left';
  868. var jobj = $jobj(elementId);
  869. var currVal = Number(jobj.css(loc).replace('px', ''));
  870. jobj.css(loc, currVal + delta);
  871. }
  872. if(!suppressFire) $ax.event.raiseSyntheticEvent(_createElementId(repeaterId, itemId), 'onItemResize');
  873. }
  874. var _updateRepeaterSize = function(prop, jobj, delta) {
  875. if (delta == 0) return;
  876. var val = $ax.getNumFromPx(jobj.css(prop)) + delta;
  877. var border = $ax.getNumFromPx(jobj.css('border-width')) || 0;
  878. val += border * 2;
  879. jobj.css(prop, val);
  880. $ax.dynamicPanelManager.fitParentPanel(jobj.attr('id'));
  881. }
  882. var _getDataFromDataSet = function (eventInfo, repeaterId, itemId, propName, type) {
  883. var row = undefined;
  884. var deleteMap = eventInfo && eventInfo.repeaterDeleteMap && eventInfo.repeaterDeleteMap[repeaterId];
  885. if(deleteMap) row = deleteMap.idToRow[itemId];
  886. if(!row) {
  887. var itemNum = _getRealItemId(eventInfo, repeaterId, Number(itemId));
  888. row = repeaterToCurrentDataSet[repeaterId][itemNum];
  889. }
  890. // Default to obj with text as empty string, as we don't generate the data for empty props
  891. var data = row[propName] || { text: '' };
  892. //For now text is always the default. May change this to depend on context.
  893. return type == 'data' && data.type != 'text' ? data : (type && data[type]) || data['text'];
  894. };
  895. _repeaterManager.getData = _getDataFromDataSet;
  896. _repeaterManager.hasData = function(id, propName) {
  897. if(!_getItemIdFromElementId(id)) return false;
  898. var repeaterId = $ax.getParentRepeaterFromScriptId(_getScriptIdFromElementId(id));
  899. return Boolean(repeaterToCurrentDataSet[repeaterId] && repeaterToCurrentDataSet[repeaterId].props.indexOf(propName) != -1);
  900. };
  901. var _getEventDeleteData = function(eventInfo, repeaterId) {
  902. var repeaterDeleteMap = eventInfo.repeaterDeleteMap;
  903. if(!repeaterDeleteMap) repeaterDeleteMap = eventInfo.repeaterDeleteMap = {};
  904. var myDeleteMap = repeaterDeleteMap[repeaterId];
  905. if(!myDeleteMap) {
  906. myDeleteMap = repeaterDeleteMap[repeaterId] = {};
  907. myDeleteMap.deletedIds = [];
  908. myDeleteMap.idToRow = {};
  909. }
  910. return myDeleteMap;
  911. };
  912. var _getRealItemId = function(eventInfo, repeaterId, itemId) {
  913. var deletedBefore = 0;
  914. var map = eventInfo.repeaterDeleteMap && eventInfo.repeaterDeleteMap[repeaterId];
  915. var deletedIds = map && map.deletedIds;
  916. if(!deletedIds) return itemId - 1;
  917. for(var i = 0; i < deletedIds.length; i++) if (deletedIds[i] < itemId) deletedBefore++;
  918. return itemId - deletedBefore - 1;
  919. }
  920. var _addItemToDataSet = function(repeaterId, row, itemEventInfo) {
  921. itemEventInfo.data = true;
  922. var oldTarget = itemEventInfo.targetElement;
  923. itemEventInfo.targetElement = repeaterId;
  924. var dataSet = repeaterToLocalDataSet[repeaterId];
  925. for(var propName in row) {
  926. if(!row.hasOwnProperty(propName)) continue;
  927. var prop = row[propName];
  928. if(prop.type == 'literal') {
  929. var retval = $ax.expr.evaluateExpr(prop.literal, itemEventInfo);
  930. if(typeof (retval) == 'string' || retval instanceof Date) retval = { type: 'text', text: retval };
  931. row[propName] = retval;
  932. }
  933. }
  934. itemEventInfo.targetElement = oldTarget;
  935. dataSet[dataSet.length] = row;
  936. itemEventInfo.data = false;
  937. };
  938. _repeaterManager.addItem = _addItemToDataSet;
  939. var _deleteItemsFromDataSet = function(repeaterId, eventInfo, type, rule) {
  940. var dataSet = repeaterToCurrentDataSet[repeaterId];
  941. var deleteDataMap = _getEventDeleteData(eventInfo, repeaterId);
  942. var items;
  943. // Should always be this, marked, or rule.
  944. if(type == 'this') items = [_getItemIdFromElementId(eventInfo.srcElement)];
  945. else if(type == 'marked') items = $ax.deepCopy(repeaterToEditItems[repeaterId]);
  946. else {
  947. // This should be rule
  948. var visibleData = repeaterToCurrentDataSet[repeaterId];
  949. items = [];
  950. var oldTarget = eventInfo.targetElement;
  951. for(var i = 0; i < visibleData.length; i++) {
  952. var index = i + 1;
  953. if(deleteDataMap.deletedIds.indexOf(index) != -1) continue;
  954. eventInfo.targetElement = _createElementId(repeaterId, index);
  955. if($ax.expr.evaluateExpr(rule, eventInfo).toLowerCase() != 'true') continue;
  956. items.push(index);
  957. }
  958. eventInfo.targetElement = oldTarget;
  959. }
  960. // Want them decending
  961. items.sort(function(a, b) { return b - a; });
  962. var editItems = repeaterToEditItems[repeaterId];
  963. for(i = 0; i < items.length; i++) {
  964. var itemId = items[i];
  965. // Don't delete already deletedItem
  966. if(deleteDataMap.deletedIds.indexOf(itemId) != -1) continue;
  967. var deletedRow = $ax.splice(dataSet, _getRealItemId(eventInfo, repeaterId, itemId), 1)[0];
  968. deleteDataMap.deletedIds.push(itemId);
  969. deleteDataMap.idToRow[itemId] = deletedRow;
  970. for(var j = editItems.length - 1; j >= 0; j--) {
  971. var editItem = editItems[j];
  972. if(editItem == itemId) $ax.splice(editItems, j, 1);
  973. else if(editItem > itemId) editItems[j] = editItem - 1;
  974. }
  975. }
  976. };
  977. _repeaterManager.deleteItems = _deleteItemsFromDataSet;
  978. var _updateEditItemsInDataSet = function(repeaterId, propMap, eventInfo, type, rule) {
  979. var oldTarget = eventInfo.targetElement;
  980. var dataSet = repeaterToCurrentDataSet[repeaterId];
  981. var items;
  982. // Should always be this, marked, or rule.
  983. if(type == 'this') items = [_getItemIdFromElementId(eventInfo.srcElement)];
  984. else if(type == 'marked') items = repeaterToEditItems[repeaterId];
  985. else {
  986. // This should be rule
  987. var currData = repeaterToCurrentDataSet[repeaterId];
  988. items = [];
  989. oldTarget = eventInfo.targetElement;
  990. for(var i = 0; i < currData.length; i++) {
  991. var index = i + 1;
  992. eventInfo.targetElement = _createElementId(repeaterId, index);
  993. if($ax.expr.evaluateExpr(rule, eventInfo).toLowerCase() != 'true') continue;
  994. items.push(index);
  995. }
  996. eventInfo.targetElement = oldTarget;
  997. }
  998. eventInfo.data = true;
  999. for(var prop in propMap) {
  1000. if(!propMap.hasOwnProperty(prop)) continue;
  1001. for(i = 0; i < items.length; i++) {
  1002. var data = propMap[prop];
  1003. var item = items[i];
  1004. if(data.type == 'literal') {
  1005. eventInfo.targetElement = _createElementId(repeaterId, item);
  1006. data = $ax.expr.evaluateExpr(data.literal, eventInfo);
  1007. if(typeof (data) == 'object' && data.isWidget) data = data.text;
  1008. if(typeof (data) == 'string') data = { type: 'text', text: data };
  1009. }
  1010. dataSet[_getRealItemId(eventInfo, repeaterId, item)][prop] = data;
  1011. }
  1012. }
  1013. eventInfo.targetElement = oldTarget;
  1014. eventInfo.data = false;
  1015. };
  1016. _repeaterManager.updateEditItems = _updateEditItemsInDataSet;
  1017. var _getAllItemIds = function(repeaterId) {
  1018. var retval = [];
  1019. var currDataSet = repeaterToCurrentDataSet[repeaterId];
  1020. for(var i = 0; i < currDataSet.length; i++) retval.push(i + 1);
  1021. return retval;
  1022. };
  1023. _repeaterManager.getAllItemIds = _getAllItemIds;
  1024. var _addEditItemToRepeater = function(repeaterId, itemIds) {
  1025. for(var i = 0; i < itemIds.length; i++) {
  1026. var itemId = Number(itemIds[i]);
  1027. var items = repeaterToEditItems[repeaterId];
  1028. if(items.indexOf(itemId) == -1) items[items.length] = itemId;
  1029. }
  1030. };
  1031. _repeaterManager.addEditItems = _addEditItemToRepeater;
  1032. var _removeEditItemFromRepeater = function(repeaterId, itemIds) {
  1033. for(var i = 0; i < itemIds.length; i++) {
  1034. var itemId = itemIds[i];
  1035. var items = repeaterToEditItems[repeaterId];
  1036. var index = items.indexOf(Number(itemId));
  1037. if(index != -1) $ax.splice(items, index, 1);
  1038. }
  1039. };
  1040. _repeaterManager.removeEditItems = _removeEditItemFromRepeater;
  1041. _repeaterManager.isEditItem = function(repeaterId, itemId) {
  1042. var items = repeaterToEditItems[repeaterId];
  1043. return items.indexOf(Number(itemId)) != -1;
  1044. };
  1045. var _createElementId = function(scriptId, itemId) {
  1046. if(!itemId) return scriptId;
  1047. var i = scriptId.indexOf('_');
  1048. var sections = i > -1 ? [scriptId.substring(0, i), scriptId.substring(i + 1)] : [scriptId];
  1049. var retval = sections[0] + '-' + itemId;
  1050. return sections.length > 1 ? retval + '_' + sections[1] : retval;
  1051. };
  1052. _repeaterManager.createElementId = _createElementId;
  1053. var _getElementId = function(scriptId, childId) {
  1054. var elementId = scriptId;
  1055. if($ax.getParentRepeaterFromScriptId(scriptId)) {
  1056. // Must be in the same item as the child
  1057. var itemId = $ax.repeater.getItemIdFromElementId(childId);
  1058. elementId = $ax.repeater.createElementId(scriptId, itemId);
  1059. }
  1060. return elementId;
  1061. };
  1062. _repeaterManager.getElementId = _getElementId;
  1063. var _getScriptIdFromElementId = function(elementId) {
  1064. if(!elementId) return elementId;
  1065. var sections = elementId.split('-');
  1066. var retval = sections[0];
  1067. if(sections.length <= 1) return retval;
  1068. sections = sections[1].split('_');
  1069. return sections.length > 1 ? retval + '_' + sections[1] : retval;
  1070. };
  1071. _repeaterManager.getScriptIdFromElementId = _getScriptIdFromElementId;
  1072. var _getItemIdFromElementId = function(elementId) {
  1073. var sections = elementId.split('-');
  1074. if(sections.length < 2) return '';
  1075. sections = sections[1].split('_');
  1076. return sections[0];
  1077. };
  1078. _repeaterManager.getItemIdFromElementId = _getItemIdFromElementId;
  1079. // TODO: Just inline this if we keep it this way.
  1080. var _applySuffixToElementId = function(id, suffix) {
  1081. return id + suffix;
  1082. // return _createElementId(_getScriptIdFromElementId(id) + suffix, _getItemIdFromElementId(id));
  1083. };
  1084. _repeaterManager.applySuffixToElementId = _applySuffixToElementId;
  1085. var _removeSuffixFromElementId = function(id) {
  1086. if (id.indexOf('_') != -1) return id.split('_', 1);
  1087. return [id];
  1088. }
  1089. _repeaterManager.removeSuffixFromElementId = _removeSuffixFromElementId;
  1090. // var _getRepeaterSize = function(repeaterId) {
  1091. // var itemCount = ($ax.getItemIdsForRepeater(repeaterId) || []).length;
  1092. // if(itemCount == 0) return { width: 0, height: 0 };
  1093. // var repeater = $obj(repeaterId);
  1094. // // Width and height per item;
  1095. // var width = repeater.width;
  1096. // var height = repeater.height;
  1097. // var viewId = $ax.adaptive.currentViewId || '';
  1098. // var widthIncrement = width + _getAdaptiveProp(repeater.repeaterPropMap, 'horizontalSpacing', viewId);
  1099. // var heightIncrement = height + _getAdaptiveProp(repeater.repeaterPropMap, 'verticalSpacing', viewId);
  1100. // var wrap = _getAdaptiveProp(repeater.repeaterPropMap, 'wrap', viewId);
  1101. // var vertical = _getAdaptiveProp(repeater.repeaterPropMap, 'vertical', viewId);
  1102. // if(wrap == -1 || itemCount <= wrap) {
  1103. // if(vertical) height += heightIncrement * (itemCount - 1);
  1104. // else width += widthIncrement * (itemCount - 1);
  1105. // } else {
  1106. // var primaryDim = wrap;
  1107. // var secondaryDim = Math.ceil(itemCount / primaryDim);
  1108. // if(vertical) {
  1109. // height += heightIncrement * (primaryDim - 1);
  1110. // width += widthIncrement * (secondaryDim - 1);
  1111. // } else {
  1112. // width += widthIncrement * (primaryDim - 1);
  1113. // height += heightIncrement * (secondaryDim - 1);
  1114. // }
  1115. // }
  1116. // return { width: width, height: height };
  1117. // };
  1118. // _repeaterManager.getRepeaterSize = _getRepeaterSize;
  1119. });
  1120. // ******* Dynamic Panel Manager ******** //
  1121. $axure.internal(function($ax) {
  1122. // TODO: Probably a lot of the dynamic panel functions from pagescript should be moved here at some point...
  1123. var _dynamicPanelManager = $ax.dynamicPanelManager = {};
  1124. var _isIdFitToContent = _dynamicPanelManager.isIdFitToContent = function(id) {
  1125. var obj = $obj(id);
  1126. if (!obj || !$ax.public.fn.IsDynamicPanel(obj.type) || !obj.fitToContent) return false;
  1127. var jobj = $jobj($ax.visibility.GetPanelState(id));
  1128. return jobj.css('position') == 'relative';
  1129. };
  1130. var _fitParentPanel = function(widgetId) {
  1131. // Find parent panel if there is one.
  1132. var parentPanelInfo = getParentPanel(widgetId);
  1133. if(parentPanelInfo) {
  1134. var parentId = parentPanelInfo.parent;
  1135. if(_updateFitPanel(parentId, parentPanelInfo.state)) _fitParentPanel(parentId);
  1136. return;
  1137. }
  1138. // Otherwise, try to get parent repeater
  1139. var parentRepeaterId = $ax.getParentRepeaterFromElementId(widgetId);
  1140. var repeaterObj = $obj(parentRepeaterId);
  1141. if(!repeaterObj || widgetId == parentRepeaterId || !repeaterObj.repeaterPropMap.fitToContent) return;
  1142. var itemId = $ax.repeater.getItemIdFromElementId(widgetId);
  1143. var size = getContainerSize($ax.repeater.createElementId(parentRepeaterId, itemId));
  1144. if($ax.repeater.setItemSize(parentRepeaterId, itemId, size.width, size.height)) _fitParentPanel(parentRepeaterId);
  1145. };
  1146. _dynamicPanelManager.fitParentPanel = _fitParentPanel;
  1147. _dynamicPanelManager.initialize = function() {
  1148. _dynamicPanelManager.initFitPanels($ax('*'));
  1149. $axure.resize(_handleResize);
  1150. };
  1151. _dynamicPanelManager.initFitPanels = function(query) {
  1152. var fitToContent = [];
  1153. query.each(function (obj, elementId) {
  1154. var scriptId = $ax.repeater.getScriptIdFromElementId(elementId);
  1155. if($ax.public.fn.IsDynamicPanel(obj.type) && obj.fitToContent && !$ax.visibility.isScriptIdLimbo(scriptId)) {
  1156. fitToContent[fitToContent.length] = elementId;
  1157. }
  1158. });
  1159. for(var i = fitToContent.length - 1; i >= 0; i--) {
  1160. var panelId = fitToContent[i];
  1161. var stateCount = $obj(panelId).diagrams.length;
  1162. for(var j = 0; j < stateCount; j++) {
  1163. // Traverse through children to find what size it should be.
  1164. var stateId = $ax.repeater.applySuffixToElementId(panelId, '_state' + j);
  1165. var stateContentId = stateId + '_content';
  1166. var stateQuery = $jobj(stateId);
  1167. var size = getContainerSize(stateContentId);
  1168. if(!$obj(panelId).percentWidth) stateQuery.width(size.width);
  1169. stateQuery.height(size.height);
  1170. }
  1171. }
  1172. };
  1173. var percentPanelToLeftCache = [];
  1174. var percentPanelsInitialized = false;
  1175. var _handleResize = function() {
  1176. if(percentPanelsInitialized) {
  1177. for(var key in percentPanelToLeftCache) {
  1178. //could optimize to only update non-contained panels
  1179. _updatePanelPercentWidth(key);
  1180. }
  1181. } else {
  1182. $ax('*').each(function(obj, elementId) {
  1183. if(_isPercentWidthPanel(obj)) _updatePanelPercentWidth(elementId);
  1184. });
  1185. percentPanelsInitialized = true;
  1186. }
  1187. };
  1188. var _isPercentWidthPanel = _dynamicPanelManager.isPercentWidthPanel = function(obj) {
  1189. return obj && $ax.public.fn.IsDynamicPanel(obj.type) && obj.percentWidth;
  1190. };
  1191. _dynamicPanelManager.updatePanelContentPercentWidth = function(elementId) {
  1192. // if(_isPercentWidthPanel($obj(elementId))) return;
  1193. var stateChildrenQuery = $jobj(elementId).children('.panel_state');
  1194. stateChildrenQuery.children('.panel_state_content').each(
  1195. function() {
  1196. $(this).children('.ax_dynamic_panel').each(
  1197. function() { _updatePanelPercentWidth(this.id); }
  1198. );
  1199. }
  1200. );
  1201. };
  1202. _dynamicPanelManager.updatePercentPanelCache = function(query) {
  1203. query.each(function(obj, elementId) {
  1204. if(_isPercentWidthPanel(obj)) {
  1205. if(_updatePercentPanelToLeftCache(obj, elementId, true)) {
  1206. _updatePanelPercentWidth(elementId);
  1207. }
  1208. }
  1209. });
  1210. };
  1211. _dynamicPanelManager.resetFixedPanel = function(obj, domElement) {
  1212. if(obj.fixedHorizontal == 'center') domElement.style.marginLeft = "";
  1213. if(obj.fixedVertical == 'middle') domElement.style.marginTop = "";
  1214. };
  1215. _dynamicPanelManager.resetAdaptivePercentPanel = function(obj, domElement) {
  1216. if(!_isPercentWidthPanel(obj)) return;
  1217. if(obj.fixedHorizontal == 'center') domElement.style.marginLeft = "";
  1218. else if(obj.fixedHorizontal == 'right') domElement.style.width = "";
  1219. };
  1220. var _updatePercentPanelToLeftCache = function(obj, elementId, overwrite) {
  1221. var wasUpdated = false;
  1222. var jObj = $jobj(elementId);
  1223. var axObj = $ax('#' + elementId);
  1224. if(percentPanelToLeftCache[elementId] == undefined || overwrite) {
  1225. if(obj.fixedHorizontal == 'center') percentPanelToLeftCache[elementId] = Number(jObj.css('margin-left').replace("px", ""));
  1226. else if(obj.fixedHorizontal == 'right') percentPanelToLeftCache[elementId] = axObj.width() + Number(jObj.css('right').replace("px", ""));
  1227. else percentPanelToLeftCache[elementId] = Number(jObj.css('left').replace("px", ""));
  1228. wasUpdated = true;
  1229. }
  1230. if(obj.fixedHorizontal == 'right' && _isIdFitToContent(elementId)) {
  1231. var fitWidth = getContainerSize($ax.visibility.GetPanelState(elementId) + '_content').width;
  1232. percentPanelToLeftCache[elementId] = fitWidth + Number(jObj.css('right').replace("px", ""));
  1233. wasUpdated = true;
  1234. }
  1235. return wasUpdated;
  1236. };
  1237. var _updatePanelPercentWidth = _dynamicPanelManager.updatePanelPercentWidth = function(elementId) {
  1238. var obj = $obj(elementId);
  1239. if(!_isPercentWidthPanel(obj)) return;
  1240. _updatePercentPanelToLeftCache(obj, elementId, false);
  1241. var width;
  1242. var x;
  1243. if(obj.fixedHorizontal) {
  1244. x = 0;
  1245. width = $(window).width();
  1246. } else {
  1247. var parentPanelInfo = getParentPanel(elementId);
  1248. if(parentPanelInfo) {
  1249. var parentId = parentPanelInfo.parent;
  1250. width = $ax('#' + parentId).width();
  1251. var parentObj = $obj(parentId);
  1252. if(parentObj.percentWidth) {
  1253. var stateId = $ax.repeater.applySuffixToElementId(parentId, '_state' + parentPanelInfo.state);
  1254. var stateContentId = stateId + '_content';
  1255. x = -Number($jobj(stateContentId).css('margin-left').replace("px", ""));
  1256. } else x = 0;
  1257. } else {
  1258. var parentRepeater = $ax.getParentRepeaterFromScriptId($ax.repeater.getScriptIdFromElementId(elementId));
  1259. if(parentRepeater) {
  1260. var itemId = $ax.repeater.getItemIdFromElementId(elementId);
  1261. var itemContainerId = $ax.repeater.createElementId(parentRepeater, itemId);
  1262. x = 0;
  1263. width = $ax('#' + itemContainerId).width();
  1264. } else {
  1265. var $window = $(window);
  1266. width = $window.width();
  1267. var bodyLeft = Number($('body').css('left').replace("px", ""));
  1268. var bodyWidth = Number($('body').css('width').replace("px", ""));
  1269. var isCenter = $ax.adaptive.getPageStyle().pageAlignment == 'center';
  1270. width = Math.max(width, bodyWidth);
  1271. x = isCenter ? -(width - bodyWidth) / 2 - bodyLeft : 0;
  1272. }
  1273. }
  1274. }
  1275. var jObj = $jobj(elementId);
  1276. if(obj.fixedHorizontal == 'left') jObj.css('left', x + 'px');
  1277. else if(obj.fixedHorizontal == 'center') {
  1278. jObj.css('left', x + 'px');
  1279. jObj.css('margin-left', 0 + 'px');
  1280. } else jObj.css('left', x + 'px');
  1281. jObj.css('width', width + 'px');
  1282. var panelLeft = percentPanelToLeftCache[elementId];
  1283. var stateParent = jObj;
  1284. while(stateParent.children()[0].id.indexOf($ax.visibility.CONTAINER_SUFFIX) != -1) stateParent = stateParent.children();
  1285. var stateChildrenQuery = stateParent.children('.panel_state');
  1286. stateChildrenQuery.css('width', width + 'px');
  1287. if(obj.fixedHorizontal == 'center')
  1288. stateChildrenQuery.children('.panel_state_content').css('left', '50%').css('margin-left', panelLeft + 'px');
  1289. else if(obj.fixedHorizontal == 'right')
  1290. stateChildrenQuery.children('.panel_state_content').css('left', width - panelLeft + 'px');
  1291. else stateChildrenQuery.children('.panel_state_content').css('margin-left', panelLeft - x + 'px');
  1292. };
  1293. _dynamicPanelManager.updateAllFitPanels = function() {
  1294. var fitToContent = [];
  1295. $ax('*').each(function (obj, elementId) {
  1296. var scriptId = $ax.repeater.getScriptIdFromElementId(elementId);
  1297. if($ax.public.fn.IsDynamicPanel(obj.type) && obj.fitToContent && !$ax.visibility.isScriptIdLimbo(scriptId)) {
  1298. fitToContent[fitToContent.length] = elementId;
  1299. }
  1300. });
  1301. for(var i = fitToContent.length - 1; i >= 0; i--) {
  1302. var panelId = fitToContent[i];
  1303. var stateCount = $obj(panelId).diagrams.length;
  1304. for(var j = 0; j < stateCount; j++) {
  1305. $ax.dynamicPanelManager.setFitToContentCss(panelId, true);
  1306. _updateFitPanel(panelId, j, true);
  1307. }
  1308. }
  1309. };
  1310. _dynamicPanelManager.setFitToContentCss = function(elementId, fitToContent, oldWidth, oldHeight) {
  1311. if($ax.dynamicPanelManager.isIdFitToContent(elementId) == fitToContent) return;
  1312. var panel = $jobj(elementId);
  1313. var stateCss;
  1314. var scrollbars = $obj(elementId).scrollbars;
  1315. if(fitToContent) {
  1316. panel.attr('style', '');
  1317. stateCss = {};
  1318. stateCss.position = 'relative';
  1319. if(scrollbars != 'none') {
  1320. stateCss.overflow = 'visible';
  1321. stateCss['-webkit-overflow-scrolling'] = 'visible';
  1322. }
  1323. if(scrollbars == 'verticalAsNeeded') {
  1324. stateCss['overflow-x'] = 'visible';
  1325. stateCss['-ms-overflow-x'] = 'visible';
  1326. } else if(scrollbars == 'horizontalAsNeeded') {
  1327. stateCss['overflow-y'] = 'visible';
  1328. stateCss['-ms-overflow-y'] = 'visible';
  1329. }
  1330. panel.children().css(stateCss);
  1331. } else {
  1332. var panelCss = { width: oldWidth, height: oldHeight };
  1333. stateCss = { width: oldWidth, height: oldHeight };
  1334. panelCss.overflow = 'hidden';
  1335. stateCss.position = 'absolute';
  1336. if(scrollbars != 'none') {
  1337. stateCss.overflow = 'auto';
  1338. stateCss['-webkit-overflow-scrolling'] = 'touch';
  1339. }
  1340. if(scrollbars == 'verticalAsNeeded') {
  1341. stateCss['overflow-x'] = 'hidden';
  1342. stateCss['-ms-overflow-x'] = 'hidden';
  1343. } else if(scrollbars == 'horizontalAsNeeded') {
  1344. stateCss['overflow-y'] = 'hidden';
  1345. stateCss['-ms-overflow-y'] = 'hidden';
  1346. }
  1347. panel.css(panelCss);
  1348. panel.children().css(stateCss);
  1349. }
  1350. };
  1351. var _getShownStateId = function (id) {
  1352. var obj = $obj(id);
  1353. if (!obj || !$ax.public.fn.IsDynamicPanel(obj.type)) return id;
  1354. var children = $ax.visibility.applyWidgetContainer(id, true).children();
  1355. for (var i = 0; i < children.length; i++) {
  1356. var child = children[i];
  1357. while ($ax.visibility.isContainer(child.id)) child = $(child).children()[0];
  1358. if (child && child.style && child.style.display != 'none') return child.id;
  1359. }
  1360. return id;
  1361. };
  1362. var _getShownStateObj = function(id) { return $ax('#' + _getShownStateId(id));}
  1363. _dynamicPanelManager.getShownState = function (id) { return $jobj(_getShownStateId(id)); };
  1364. var _getClamp = function(id) {
  1365. var obj = $obj(id);
  1366. if(!obj) return $ax('#' + id);
  1367. if ($ax.public.fn.IsDynamicPanel(obj.type)) return _getShownStateObj(id);
  1368. return $ax('#' + id);
  1369. };
  1370. var _updateFitPanel = function(panelId, stateIndex, initializingView) {
  1371. if(!panelId) return false;
  1372. // Only fit if fitToContent is true
  1373. if(!$ax.dynamicPanelManager.isIdFitToContent(panelId)) return false;
  1374. // Traverse through children to find what size it should be.
  1375. var stateId = $ax.repeater.applySuffixToElementId(panelId, '_state' + stateIndex);
  1376. var stateContentId = stateId + '_content';
  1377. var stateQuery = $jobj(stateId);
  1378. var size = getContainerSize(stateContentId);
  1379. // Skip if size hasn't changed
  1380. var oldWidth = stateQuery.width();
  1381. var oldHeight = stateQuery.height();
  1382. if(oldWidth == size.width && oldHeight == size.height) return false;
  1383. if(!$obj(panelId).percentWidth) stateQuery.width(size.width);
  1384. stateQuery.height(size.height);
  1385. //updatePercentWidth on all child panels
  1386. $jobj(stateContentId).children('.ax_dynamic_panel').each(
  1387. function() { _updatePanelPercentWidth(this.id); }
  1388. );
  1389. //do the following only if it is the current state
  1390. if(stateId != $ax.visibility.GetPanelState(panelId)) return false;
  1391. if(!initializingView) _adjustFixed(panelId, oldWidth, oldHeight, size.width, size.height);
  1392. else if(stateIndex != 0) {
  1393. var state0 = $jobj($ax.repeater.applySuffixToElementId(panelId, '_state0'));
  1394. _adjustFixed(panelId, state0.width(), state0.height(), size.width, size.height);
  1395. }
  1396. $ax.event.raiseSyntheticEvent(panelId, 'onResize');
  1397. $ax.flyoutManager.updateFlyout(panelId);
  1398. return true;
  1399. };
  1400. // widgetId is the one that crawls up masters until it finds a parent panel, targetId is the original widgetId (not the crawling master)
  1401. var getParentPanel = function(widgetId, path, targetId) {
  1402. path = path || $ax.getPathFromScriptId($ax.repeater.getScriptIdFromElementId(widgetId));
  1403. var obj = $obj(widgetId);
  1404. if(obj.parentDynamicPanel) {
  1405. path[path.length - 1] = obj.parentDynamicPanel;
  1406. var parentId = $ax.getScriptIdFromPath(path);
  1407. if(!parentId) return undefined;
  1408. parentId = $ax.repeater.getElementId(parentId, widgetId);
  1409. var parentObj = $obj(parentId);
  1410. var retVal = { parent: parentId };
  1411. for(var i = 0; i < parentObj.diagrams.length; i++) {
  1412. var stateId = $ax.repeater.applySuffixToElementId(parentId, '_state' + i);
  1413. var stateQuery = $jobj(stateId);
  1414. if(stateQuery.find('#' + (targetId || widgetId)).length != 0) {
  1415. retVal.state = i;
  1416. break;
  1417. }
  1418. }
  1419. return retVal;
  1420. }
  1421. if(path.length == 1) return undefined;
  1422. path.pop();
  1423. var parentMaster = $ax.getScriptIdFromPath(path);
  1424. if(!parentMaster) return undefined;
  1425. parentMaster = $ax.repeater.getElementId(parentMaster, widgetId);
  1426. return getParentPanel(parentMaster, path, targetId || widgetId);
  1427. };
  1428. // TODO: May be a better location for this. Used currently for rdo and panel state containers
  1429. var getContainerSize = function(containerId) {
  1430. var containerQuery = containerId ? $jobj(containerId) : $('#base');
  1431. var children = containerQuery.children();
  1432. // Default size
  1433. var size = { width: 0, height: 0 };
  1434. for(var i = 0; i < children.length; i++) {
  1435. var child = $(children[i]);
  1436. var childId = child.attr('id');
  1437. //var axChild = $ax('#' + childId).width();
  1438. var childObj = $obj(childId);
  1439. if(!childObj) {
  1440. // On the body there are some children that should be ignored, as they are not objects.
  1441. if(!child.hasClass('basiclink') || child.get(0).tagName.toLowerCase() != 'a') continue;
  1442. // Otherwise it should be a basic link
  1443. var linkChildren = child.children();
  1444. if(!linkChildren.length) continue;
  1445. child = $(linkChildren[0]);
  1446. childId = child.attr('id');
  1447. childObj = $obj(childId);
  1448. }
  1449. // Ignore fixed
  1450. if(!childId || $ax.visibility.limboIds[childId] || !$ax.visibility.IsIdVisible(childId)
  1451. || $ax.public.fn.IsDynamicPanel(childObj.type) && childObj.fixedHorizontal) continue;
  1452. var boundingRect = $ax.public.fn.getWidgetBoundingRect(childId);
  1453. var position = { left: boundingRect.left, top: boundingRect.top };
  1454. var width = boundingRect.width;
  1455. var height = boundingRect.height;
  1456. if($ax.public.fn.IsMaster(childObj.type)) {
  1457. var masterSize = getContainerSize(childId);
  1458. width = masterSize.width;
  1459. height = masterSize.height;
  1460. // } else if($ax.public.fn.IsRepeater(childObj.type)) {
  1461. // var repeaterSize = $ax.repeater.getRepeaterSize(childId);
  1462. // width = repeaterSize.width;
  1463. // height = repeaterSize.height;
  1464. // if(width == 0 && height == 0) continue;
  1465. // position.left += childObj.x;
  1466. // position.top += childObj.y;
  1467. } else if ($ax.public.fn.IsDynamicPanel(childObj.type)) {
  1468. if($ax.dynamicPanelManager.isIdFitToContent(childId)) {
  1469. var stateQuery = $jobj($ax.visibility.GetPanelState(childId));
  1470. width = stateQuery.width();
  1471. height = stateQuery.height();
  1472. }
  1473. }
  1474. size.width = Math.max(size.width, position.left + width);
  1475. size.height = Math.max(size.height, position.top + height);
  1476. }
  1477. return size;
  1478. };
  1479. var _adjustFixed = _dynamicPanelManager.adjustFixed = function(panelId, oldWidth, oldHeight, width, height) {
  1480. var loc = _getFixedPosition(panelId, oldWidth, oldHeight, width, height);
  1481. if(loc) {
  1482. $ax.action.addAnimation(panelId, $ax.action.queueTypes.move, function() {
  1483. $ax.move.MoveWidget(panelId, loc[0], loc[1], { easing: 'none', duration: 0 }, false, null, true);
  1484. });
  1485. }
  1486. };
  1487. var _getFixedPosition = _dynamicPanelManager.getFixedPosition = function(panelId, oldWidth, oldHeight, width, height) {
  1488. var panelObj = $obj(panelId);
  1489. var x = 0;
  1490. var y = 0;
  1491. if(panelObj.fixedHorizontal == 'center') {
  1492. x = (oldWidth - width) / 2;
  1493. }
  1494. if(panelObj.fixedVertical == 'middle') {
  1495. y = (oldHeight - height) / 2;
  1496. }
  1497. return x == 0 && y == 0 ? undefined : [x, y];
  1498. };
  1499. _dynamicPanelManager.getFixedInfo = function(panelId) {
  1500. var panelObj = $obj(panelId);
  1501. if (!panelObj || !$ax.public.fn.IsDynamicPanel(panelObj.type)) return {};
  1502. var jobj = $jobj(panelId);
  1503. if(jobj.css('position') == 'absolute') return {};
  1504. var info = {};
  1505. var horizontal = panelObj.fixedHorizontal;
  1506. if(!horizontal) return info;
  1507. info.fixed = true;
  1508. info.horizontal = horizontal;
  1509. info.vertical = panelObj.fixedVertical;
  1510. if(info.horizontal == 'left') info.x = Number(jobj.css('left').replace('px', ''));
  1511. else if(info.horizontal == 'center') info.x = Number(jobj.css('margin-left').replace('px', ''));
  1512. else if(info.horizontal == 'right') info.x = Number(jobj.css('right').replace('px', ''));
  1513. if(info.vertical == 'top') info.y = Number(jobj.css('top').replace('px', ''));
  1514. else if(info.vertical == 'middle') info.y = Number(jobj.css('margin-top').replace('px', ''));
  1515. else if(info.vertical == 'bottom') info.y = Number(jobj.css('bottom').replace('px', ''));
  1516. return info;
  1517. };
  1518. // Show isn't necessary if this is always done before toggling (which is currently true), but I don't want that
  1519. // change (if it happened) to break this.
  1520. var _compressToggle = function (id, vert, show, easing, duration) {
  1521. var layer = $ax.getTypeFromElementId(id) == $ax.constants.LAYER_TYPE;
  1522. var locProp = vert ? 'top' : 'left';
  1523. var dimProp = vert ? 'height' : 'width';
  1524. var threshold;
  1525. var delta;
  1526. threshold = $ax('#' + id)[locProp](true);
  1527. delta = layer ? $ax('#' + id)[dimProp]() : _getShownStateObj(id)[dimProp]();
  1528. if(!show) {
  1529. // Need to make threshold bottom/right
  1530. threshold += delta;
  1531. // Delta is in the opposite direction
  1532. delta *= -1;
  1533. }
  1534. _compress(id, vert, threshold, delta, easing, duration);
  1535. };
  1536. _dynamicPanelManager.compressToggle = _compressToggle;
  1537. // Used when setting state of dynamic panel
  1538. var _compressDelta = function(id, oldState, newState, vert, easing, duration) {
  1539. var oldQuery = $jobj(oldState);
  1540. var newQuery = $jobj(newState);
  1541. var thresholdProp = vert ? 'top' : 'left';
  1542. var thresholdOffset = vert ? 'height' : 'width';
  1543. var threshold = $ax('#' + id)[thresholdProp](true);
  1544. threshold += oldQuery[thresholdOffset]();
  1545. var delta = newQuery[thresholdOffset]() - oldQuery[thresholdOffset]();
  1546. var clampOffset = vert ? 'width' : 'height';
  1547. var clampWidth = Math.max(oldQuery[clampOffset](), newQuery[clampOffset]());
  1548. _compress(id, vert, threshold, delta, easing, duration, clampWidth);
  1549. };
  1550. _dynamicPanelManager.compressDelta = _compressDelta;
  1551. var _compress = function (id, vert, threshold, delta, easing, duration, clampWidth) {
  1552. // If below, a horizantal clamp, otherwise a vertical clamp
  1553. var clamp = {
  1554. prop: vert ? 'left' : 'top',
  1555. offset: vert ? 'width' : 'height'
  1556. };
  1557. // Get clamp in coords relative to parent. Account for layers farther down
  1558. if($ax.getTypeFromElementId(id) == $ax.constants.LAYER_TYPE) {
  1559. clamp.start = $ax('#' + id)[clamp.prop](true);
  1560. clamp.end = clamp.start + $ax('#' + id)[clamp.offset]();
  1561. } else {
  1562. var clampLoc = $jobj(id);
  1563. if(typeof clampWidth == 'undefined') clampWidth = _getClamp(id)[clamp.offset]();
  1564. clamp.start = Number(clampLoc.css(clamp.prop).replace('px', ''));
  1565. clamp.end = clamp.start + clampWidth;
  1566. }
  1567. // If clamps, threshold, or delta is not a number, can't compress.
  1568. if (isNaN(clamp.start) || isNaN(clamp.end) || isNaN(threshold) || isNaN(delta)) return;
  1569. // Update clamp if fixed, to account for body position (only necessary when page centered)
  1570. if($jobj(id).css('position') == 'fixed') {
  1571. var clampDelta = $('#base').position().left;
  1572. clamp.start -= clampDelta;
  1573. clamp.end -= clampDelta;
  1574. }
  1575. if(!easing) {
  1576. easing = 'none';
  1577. duration = 0;
  1578. }
  1579. var parent = $ax('#' + id).getParents(false, ['item', 'state', 'layer'])[0];
  1580. var obj = parent && $ax.getObjectFromElementId($ax.repeater.removeSuffixFromElementId(parent)[0]);
  1581. // Go until you hit a parent item or state, or a layer that is hidden to use as parent.
  1582. // Account for layer container positions as you go.
  1583. while(obj && $ax.public.fn.IsLayer(obj.type) && $ax.visibility.IsIdVisible(parent)) {
  1584. var container = $ax.visibility.applyWidgetContainer(parent, true, true);
  1585. // If layer is using container, offset is going to be necessary
  1586. if(container.length) {
  1587. var offsetX = $ax.getNumFromPx(container.css('left'));
  1588. var offsetY = $ax.getNumFromPx(container.css('top'));
  1589. var clampProp = clamp.prop == 'left' ? offsetX : offsetY;
  1590. var threshProp = clamp.prop == 'left' ? offsetY : offsetX;
  1591. threshold += threshProp;
  1592. clamp.start += clampProp;
  1593. clamp.end += clampProp;
  1594. }
  1595. parent = $ax('#' + parent).getParents(false, ['item', 'state', 'layer'])[0];
  1596. obj = parent && $ax.getObjectFromElementId($ax.repeater.removeSuffixFromElementId(parent)[0]);
  1597. }
  1598. // Add container mid push causes strange behavior because we take container into account as we go down, but if after we accounted for it,
  1599. // a container is added, that container is not accounted for with threshold and clamp values.
  1600. var layer = obj && $ax.public.fn.IsLayer(obj.type) && parent;
  1601. if(layer) {
  1602. // If your parent layer is invisible, you want to be relative to it's container. That is true already if it has a container,
  1603. // but if you are just adding one now, then you need to offset your values
  1604. var needsOffset = !$jobj(layer + '_container').length && !$ax.visibility.IsIdVisible(layer);
  1605. $ax.visibility.pushContainer(layer, false);
  1606. if(needsOffset) {
  1607. container = $jobj(layer + '_container');
  1608. offsetX = $ax.getNumFromPx(container.css('left'));
  1609. offsetY = $ax.getNumFromPx(container.css('top'));
  1610. clampProp = clamp.prop == 'left' ? offsetX : offsetY;
  1611. threshProp = clamp.prop == 'left' ? offsetY : offsetX;
  1612. threshold -= threshProp;
  1613. clamp.start -= clampProp;
  1614. clamp.end -= clampProp;
  1615. }
  1616. }
  1617. // Note: If parent is body, some of these aren't widgets
  1618. if(parent && $jobj(parent + '_content').length > 0) parent = parent + '_content';
  1619. if(parent && $jobj(parent + '_container').length > 0) parent = parent + '_container';
  1620. _compressChildrenHelper(id, $(parent ? '#' + parent : '#base').children(), vert, threshold, delta, clamp, easing, duration);
  1621. if(layer) $ax.visibility.popContainer(layer, false);
  1622. // Do item push
  1623. var itemId = $ax.repeater.getItemIdFromElementId(id);
  1624. if(!itemId) return;
  1625. var repeaterId = $ax.getParentRepeaterFromElementId(id);
  1626. // Only need to push when parent is an item directly.
  1627. if(parent != $ax.repeater.createElementId(repeaterId, itemId)) return;
  1628. // If repeater is fit to content, then don't worry about it, it'll be handled elsewhere
  1629. if(!obj.repeaterPropMap.fitToContent) $ax.repeater.pushItems(repeaterId, itemId, delta, vert);
  1630. };
  1631. var _compressChildrenHelper = function (id, children, vert, threshold, delta, clamp, easing, duration, parentLayer) {
  1632. var toMove = [];
  1633. var allMove = true;
  1634. for (var i = 0; i < children.length; i++) {
  1635. var child = $(children[i]);
  1636. //don't move fixed
  1637. if(child.css('position') == 'fixed') continue;
  1638. // Check for basic links
  1639. if(child[0] && child[0].tagName == 'A' && child.hasClass('basiclink')) child = child.children();
  1640. var childId = child.attr('id');
  1641. // Don't move self, and check id to make sure it is a widget.
  1642. if(childId == id || !childId || childId[0] != 'u') {
  1643. allMove = false;
  1644. continue;
  1645. }
  1646. if ($ax.getTypeFromElementId(childId) == $ax.constants.LAYER_TYPE) {
  1647. $ax.visibility.pushContainer(childId, false);
  1648. var addSelf;
  1649. var container = $ax.visibility.applyWidgetContainer(childId, true, true);
  1650. var layerChildren = $ax.visibility.getRealChildren(child.children());
  1651. //if(container.length) {
  1652. var offsetX = -$ax.getNumFromPx(container.css('left'));
  1653. var offsetY = -$ax.getNumFromPx(container.css('top'));
  1654. var clampProp = clamp.prop == 'left' ? offsetX : offsetY;
  1655. var threshProp = clamp.prop == 'left' ? offsetY : offsetX;
  1656. var layerClamp = { prop: clamp.prop, offset: clamp.offset, start: clamp.start + clampProp, end: clamp.end + clampProp };
  1657. addSelf = _compressChildrenHelper(id, layerChildren, vert, threshold + threshProp, delta, layerClamp, easing, duration, childId);
  1658. //} else addSelf = _compressChildrenHelper(id, layerChildren, vert, threshold, delta, clamp, easing, duration, childId);
  1659. if(addSelf) toMove.push(childId);
  1660. else allMove = false;
  1661. $ax.visibility.popContainer(childId, false);
  1662. continue;
  1663. }
  1664. var numbers = childId.substring(1).split('-');
  1665. if(numbers.length < 1 || isNaN(Number(numbers[0])) || (numbers.length == 2 && isNaN(Number(numbers[1]))) || numbers.length > 2) continue;
  1666. var marker, childClamp;
  1667. var axChild = $ax('#' + childId);
  1668. var markerProp = vert ? 'top' : 'left';
  1669. marker = Number(axChild[markerProp](true));
  1670. childClamp = [Number(axChild[clamp.prop](true))];
  1671. // Dynamic panels are not reporting correct size sometimes, so pull it from the state. Get shown state just returns the widget if it is not a dynamic panel.
  1672. var sizeChild = _getShownStateObj(childId);
  1673. childClamp[1] = childClamp[0] + sizeChild[clamp.offset]();
  1674. if(isNaN(marker) || isNaN(childClamp[0]) || isNaN(childClamp[1]) ||
  1675. marker < threshold || childClamp[1] <= clamp.start || childClamp[0] >= clamp.end) {
  1676. allMove = false;
  1677. continue;
  1678. }
  1679. if (allMove && parentLayer) {
  1680. //should i nopmove here?
  1681. //$ax.move.nopMove(childId);
  1682. $ax.event.raiseSyntheticEvent(childId, "onMove");
  1683. }
  1684. toMove.push(childId);
  1685. }
  1686. if (allMove && parentLayer) {
  1687. return true;
  1688. } else {
  1689. for(var i = 0; i < toMove.length; i++) {
  1690. $ax('#' + toMove[i]).moveBy(vert ? 0 : delta, vert ? delta : 0, easing == 'none' ? {} : { duration: duration, easing: easing });
  1691. }
  1692. }
  1693. return false;
  1694. };
  1695. var _parentHandlesStyles = function(id) {
  1696. var parents = $ax('#' + id).getParents(true, ['dynamicPanel', 'layer'])[0];
  1697. if(!parents) return false;
  1698. var directParent = true;
  1699. for(var i = 0; i < parents.length; i++) {
  1700. var parentId = parents[i];
  1701. var parentObj = $obj(parentId);
  1702. if(!parentObj.propagate) {
  1703. directParent = false;
  1704. continue;
  1705. }
  1706. return { id: parentId, direct: directParent };
  1707. }
  1708. return false;
  1709. };
  1710. _dynamicPanelManager.parentHandlesStyles = _parentHandlesStyles;
  1711. var _propagateMouseOver = function(id, value) {
  1712. propagate(id, true, value);
  1713. };
  1714. _dynamicPanelManager.propagateMouseOver = _propagateMouseOver;
  1715. var _propagateMouseDown = function(id, value) {
  1716. propagate(id, false, value);
  1717. };
  1718. _dynamicPanelManager.propagateMouseDown = _propagateMouseDown;
  1719. var propagate = function(id, hover, value) {
  1720. var hoverChildren = function(children) {
  1721. if(!children) return;
  1722. for(var i = 0; i < children.length; i++) {
  1723. var elementId = children[i].id;
  1724. var obj = $obj(elementId);
  1725. if(obj == null) {
  1726. elementId = elementId.split('_')[0];
  1727. obj = $obj(elementId);
  1728. }
  1729. if(obj == null) continue;
  1730. if (($ax.public.fn.IsDynamicPanel(obj.type) || $ax.public.fn.IsLayer(obj.type)) && !obj.propagate) continue;
  1731. if(hover) $ax.style.SetWidgetHover(elementId, value);
  1732. else $ax.style.SetWidgetMouseDown(elementId, value);
  1733. $ax.annotation.updateLinkLocations($ax.style.GetTextIdFromShape(elementId));
  1734. hoverChildren(children[i].children);
  1735. }
  1736. };
  1737. hoverChildren($ax('#' + id).getChildren(true)[0].children);
  1738. };
  1739. });