axplayer.js 111 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573
  1. var PAGE_ID_NAME = 'id';
  2. var PAGE_URL_NAME = 'p';
  3. var SITEMAP_COLLAPSE_VAR_NAME = 'c';
  4. var PLUGIN_VAR_NAME = 'g';
  5. var FOOTNOTES_VAR_NAME = 'fn';
  6. var ADAPTIVE_VIEW_VAR_NAME = 'view';
  7. var SCALE_VAR_NAME = 'sc';
  8. var DIM_VAR_NAME = 'dm';
  9. var ROT_VAR_NAME = 'r';
  10. var CLOUD_VAR_NAME = 'cl';
  11. var TRACE_VAR_NAME = 'tr';
  12. var RP_VERSION = 9;
  13. var lastLeftPanelWidth = 220;
  14. var lastRightPanelWidth = 220;
  15. var lastLeftPanelWidthDefault = 220;
  16. var lastRightPanelWidthDefault = 220;
  17. var toolBarOnly = true;
  18. // TODO: need to find a way to get rid of iphone X hacks!!!
  19. // - could possibly have app detect iphone and send information back to player, but currently, that information would arrive too late
  20. var iphoneX = false;
  21. var iphoneXFirstPass = true;
  22. // isolate scope
  23. (function () {
  24. if (!window.$axure) window.$axure = function () { };
  25. if (typeof console == 'undefined') console = {
  26. log: function () { }
  27. };
  28. if (window._axUtils) $axure.utils = _axUtils;
  29. setUpController();
  30. var getHashStringVar = $axure.player.getHashStringVar = function (query) {
  31. var qstring = self.location.href.split("#");
  32. if (qstring.length < 2) return "";
  33. return GetParameter(qstring, query);
  34. }
  35. var isCloud = $axure.player.isCloud = getHashStringVar(CLOUD_VAR_NAME);
  36. if (isCloud) {
  37. $("#topPanel").css('display', 'none');
  38. lastRightPanelWidthDefault = 290;
  39. }else {
  40. $("#topPanel").css('display', '');
  41. }
  42. $axure.loadDocument = function (document) {
  43. $axure.document = document;
  44. var configuration = $axure.document.configuration;
  45. var _settings = {};
  46. _settings.projectId = configuration.prototypeId;
  47. _settings.projectName = configuration.projectName;
  48. _settings.isAxshare = configuration.isAxshare;
  49. _settings.isExpo = configuration.isExpo == null ? false : configuration.isExpo;
  50. _settings.loadSitemap = configuration.loadSitemap;
  51. _settings.loadFeedbackPlugin = configuration.loadFeedbackPlugin;
  52. var cHash = getHashStringVar(SITEMAP_COLLAPSE_VAR_NAME);
  53. _settings.startCollapsed = cHash == "1";
  54. if (cHash == "2") closePlayer();
  55. var gHash = getHashStringVar(PLUGIN_VAR_NAME);
  56. _settings.startPluginGid = gHash;
  57. $axure.player.settings = _settings;
  58. var additionalJs = $axure.document.additionalJs;
  59. if (additionalJs != null) {
  60. var total = additionalJs.length;
  61. if (total > 0) $.holdReady(true);
  62. $.each(additionalJs, function (index, value) {
  63. var script = window.document.createElement("script");
  64. script.type = "text/javascript";
  65. script.src = value;
  66. script.async = false;
  67. script.onload = script.onreadystatechange = function (e) {
  68. if (!script.readyState || /loaded|complete/.test(script.readyState)) {
  69. script.onload = script.onreadystatechange = null;
  70. script = undefined;
  71. }
  72. if (--total == 0) $.holdReady(false);
  73. }
  74. window.document.head.appendChild(script);
  75. });
  76. }
  77. var additionalCss = $axure.document.additionalCss;
  78. if(additionalCss != null) {
  79. $.each(additionalCss, function(index, value) {
  80. var style = window.document.createElement('link');
  81. style.type = "text/css";
  82. style.rel = "stylesheet";
  83. style.href = value;
  84. window.document.head.appendChild(style);
  85. });
  86. }
  87. if(_settings.isExpo && configuration.isMobile) {
  88. initializeDeviceFrame();
  89. }
  90. // Pseudo-indicator that the document has been loaded
  91. $axure.document.isLoaded = true;
  92. };
  93. $(window).bind('load', function () {
  94. if ((CHROME && BROWSER_VERSION < 64) || // First 2018 release
  95. (SAFARI && BROWSER_VERSION < 602) || // Minor version 10
  96. (FIREFOX && BROWSER_VERSION < 57) || // Support Quantum
  97. ($axure.browser.isEdge && BROWSER_VERSION < 15) || // 15 for mobile devices (else could go 16, possibly 17)
  98. IE_10_AND_BELOW) {
  99. if (!QQ && !UC) appendOutOfDateNotification();
  100. }
  101. if (CHROME_5_LOCAL && !$('body').attr('pluginDetected')) {
  102. window.location = 'resources/chrome/chrome.html';
  103. }
  104. });
  105. function appendOutOfDateNotification() {
  106. var toAppend = '';
  107. toAppend += '<div id="browserOutOfDateNotification">';
  108. toAppend += ' <div style="font-size: 24px; text-align: center; color: #FFFFFF;">LOOKS LIKE YOUR BROWSER IS OUT OF DATE</div>';
  109. toAppend += ' <div style="font-size: 14px; text-align: center; color: #FFFFFF; margin-bottom: 16px;">This prototype may not look or function correctly until you update your browser</div>';
  110. toAppend += ' <div id="supportedBrowsersListContainer">';
  111. toAppend += ' <div class="browserContainer">';
  112. toAppend += ' <div class="browserName">Google Chrome</div><div class="browserSupportedVersion">v64 and later</div>';
  113. toAppend += ' </div>';
  114. toAppend += ' <div class="browserContainer">';
  115. toAppend += ' <div class="browserName">Mozilla Firefox</div><div class="browserSupportedVersion">v57 and later</div>';
  116. toAppend += ' </div>';
  117. toAppend += ' <div class="browserContainer">';
  118. toAppend += ' <div class="browserName">Microsoft Edge</div><div class="browserSupportedVersion">v15 and later</div>';
  119. toAppend += ' </div>';
  120. toAppend += ' <div class="browserContainer">';
  121. toAppend += ' <div class="browserName">Apple Safari</div><div class="browserSupportedVersion">v10 and later</div>';
  122. toAppend += ' </div>';
  123. toAppend += ' <div class="browserContainer">';
  124. toAppend += ' <div class="browserName">Internet Explorer</div><div class="browserSupportedVersion">v11 and later</div>';
  125. toAppend += ' </div>';
  126. toAppend += ' </div>';
  127. toAppend += ' <div id="browserOutOfDateNotificationButtons">'
  128. if (!MOBILE_DEVICE) {
  129. toAppend += ' <div style="margin-right: 36px"><a href="http://outdatedbrowser.com/en" id="updateBrowserButton">UPDATE BROWSER</a></div>';
  130. toAppend += ' <div style="flex: 0 1 45%;"><a id="continueToPrototypeButton">Continue viewing prototype anyway</a></div>';
  131. } else {
  132. toAppend += ' <div style="width: 100%; text-align:center"><a id="continueToPrototypeButton">Continue viewing prototype anyway</a></div>';
  133. }
  134. toAppend += ' </div>';
  135. toAppend += '</div>';
  136. $('body').prepend(toAppend);
  137. $('#continueToPrototypeButton').on('click', function () {
  138. var $message = $('#browserOutOfDateNotification');
  139. $message.children().hide();
  140. $message.css('padding-top', '0px');
  141. $message.animate({ 'height': '0px' }, { duration: 400, complete: function () { $message.hide(); } });
  142. });
  143. }
  144. $axure.page.bind('load.start', mainFrame_onload);
  145. $axure.messageCenter.addMessageListener(messageCenter_message);
  146. var suppressPluginVarUpdate = false;
  147. $(document).on('pluginShown', function (event, data) {
  148. if (!suppressPluginVarUpdate) $axure.player.setVarInCurrentUrlHash(PLUGIN_VAR_NAME, data ? data : '');
  149. });
  150. $(document).on('pluginCreated', function (event, data) {
  151. if (!$axure.player.isMobileMode() && $axure.player.settings.startPluginGid.indexOf(data) > -1) {
  152. suppressPluginVarUpdate = true;
  153. $axure.player.showPlugin(data);
  154. suppressPluginVarUpdate = false;
  155. }
  156. if (data == '1') {
  157. $('#interfaceControlFrame').animate({ opacity: 1 }, 200);
  158. }
  159. if ($axure.player.settings.isExpo) {
  160. // TODO: Do this only if expo is a mobile device
  161. // TODO: Figure out better way to deal with this issue
  162. $axure.messageCenter.postMessage('setDeviceMode', { device: false });
  163. $axure.messageCenter.postMessage('setDeviceMode', { device: true });
  164. //$axure.player.refreshViewPort();
  165. }
  166. });
  167. function initializeEvents() {
  168. $('#interfaceControlFrameMinimizeContainer').on('click', collapse);
  169. $('#interfaceControlFrameCloseButton').on('click', closePlayer);
  170. $('#interfacePageNameContainer').on($axure.eventNames.mouseDownName, toggleSitemap);
  171. $('#interfaceAdaptiveViewsContainer').on($axure.eventNames.mouseDownName, toggleAdaptiveViewsPopup);
  172. $('#overflowMenuButton').on($axure.eventNames.mouseDownName, toggleOverflowMenuPopup);
  173. if (!MOBILE_DEVICE) {
  174. $('#maximizePanel').mouseenter(function () {
  175. $(this).addClass('maximizePanelOver');
  176. });
  177. $('#maximizePanel').mouseleave(function () {
  178. if ($(this).hasClass('maximizePanelOver')) {
  179. $(this).animate(isMobileMode() ? {
  180. top: '-' + $('#maximizePanel').height() + 'px'
  181. } : {
  182. left: '-' + $('#maximizePanel').width() + 'px'
  183. }, 300);
  184. }
  185. $(this).removeClass('maximizePanelOver');
  186. });
  187. $('#maximizePanelOver').mouseenter(function () {
  188. $('#maximizePanel').animate(isMobileMode() ? {
  189. top: '0px'
  190. } : {
  191. left: '0px'
  192. }, 100);
  193. });
  194. }
  195. $minimizeContainer = $('#interfaceControlFrameMinimizeContainer');
  196. $minimizeContainer.mouseenter(function () { $minimizeContainer.addClass('collapseHovered') });
  197. $minimizeContainer.mouseleave(function () { $minimizeContainer.removeClass('collapseHovered') });
  198. $maximizeContainer = $('#maximizePanelContainer');
  199. $maximizeContainer.mouseenter(function () { if(!MOBILE_DEVICE) $minimizeContainer.addClass('expandHovered') });
  200. $maximizeContainer.mouseleave(function () { if(!MOBILE_DEVICE) $minimizeContainer.removeClass('expandHovered') });
  201. $('#maximizePanel').click(function () {
  202. $(this).removeClass('maximizePanelOver');
  203. $('#maximizePanelContainer').hide();
  204. $axure.messageCenter.postMessage('expandFrame');
  205. });
  206. $('#mHideSidebar').on($axure.eventNames.mouseDownName, function (e) { startM(e); });
  207. $('#lsplitbar').on($axure.eventNames.mouseDownName, startLeftSplit);
  208. $('#rsplitbar').on($axure.eventNames.mouseDownName, startRightSplit);
  209. if ($axure.mobileSupport.mobile) {
  210. var touchCount = 0;
  211. var lastTouch = Date.now();
  212. $('#mainPanel').on('touchstart',
  213. (function (e) {
  214. var now = Date.now();
  215. if (now - lastTouch < 375) {
  216. if (++touchCount === 3) {
  217. if ($axure.player.isMobileMode() || MOBILE_DEVICE) expand();
  218. touchCount = 0;
  219. e.preventDefault();
  220. };
  221. } else {
  222. touchCount = 1;
  223. }
  224. lastTouch = now;
  225. }));
  226. }
  227. $(window).resize(function () {
  228. $axure.player.resizeContent();
  229. });
  230. $(window).on("orientationchange", function () {
  231. // IOS often does not complete updating innerHeight and innerWidth
  232. // until after calling orientation changed and resized window
  233. // Also, cannot use $(window).height() call since iOS11 needs padding amount
  234. iphoneXFirstPass = false
  235. if (IOS && isMobileMode()) setTimeout(function () { $axure.player.resizeContent(true); }, 250);
  236. });
  237. $('#mainPanel').scroll(function () {
  238. repositionClippingBoundsScroll();
  239. });
  240. }
  241. function initializeMainFrame() {
  242. var legacyQString = getQueryString("Page");
  243. if (legacyQString.length > 0) {
  244. location.href = location.href.substring(0, location.href.indexOf("?")) + "#" + PAGE_URL_NAME + "=" + legacyQString;
  245. return;
  246. }
  247. var mainFrame = document.getElementById("mainFrame");
  248. //if it's local file on safari, test if we can access mainframe after its loaded
  249. if (SAFARI && document.location.href.indexOf('file://') >= 0) {
  250. $(mainFrame).on('load', function () {
  251. var canAccess;
  252. try {
  253. var mainFrameWindow = mainFrame.contentWindow || mainFrame.contentDocument;
  254. mainFrameWindow['safari_file_CORS'] = 'Y';
  255. canAccess = mainFrameWindow['safari_file_CORS'] === 'Y';
  256. } catch (err) {
  257. canAccess = false;
  258. }
  259. if (!canAccess) window.location = 'resources/chrome/safari.html';
  260. });
  261. }
  262. if($axure.player.settings != null && !$axure.player.settings.isExpo) {
  263. mainFrame.contentWindow.location.href = getInitialUrl();
  264. }
  265. }
  266. function initializeDeviceFrame() {
  267. // TODO: Load device bezel and necessary overlays if applicable
  268. // - Need to determine if device has a frame/overlay
  269. // - Determine where to store said assets
  270. // - Determine sizing, positioning, orientation, and styling for HTML containers
  271. // - Verify that it stays consistent for every state (expo)
  272. var expo = $axure.expo;
  273. var project = expo.project;
  274. var device = project.Platform.Device;
  275. // in expo.ts, Web is 12
  276. if (device === 12) {
  277. // Hide containers
  278. $('#deviceFrameContainer').hide();
  279. $('#bezelOverlay').hide();
  280. return;
  281. }
  282. // map devices to their corresponding frame/bezel/overlays
  283. }
  284. var wasMobile = false;
  285. var isMobileMode = $axure.player.isMobileMode = function () { return isShareApp() || (MOBILE_DEVICE && $(window).width() < 420); }
  286. var isMobileTextEntry = false;
  287. var isViewOverridden = $axure.player.isViewOverridden = function() {
  288. return getHashStringVar(ADAPTIVE_VIEW_VAR_NAME).length > 0;
  289. }
  290. function toggleSitemapMobileMode() {
  291. var $container = $('#sitemapHost');
  292. if (!$container.length) return;
  293. var $header = $container.find('.pluginNameHeader');
  294. var projectName = $axure.player.getProjectName();
  295. if (isMobileMode()) {
  296. $header.text('PROJECT PAGES');
  297. $container.addClass('mobileMode');
  298. $container.find('.sitemapPageName').addClass('mobileText');
  299. // Give sitemapHost left-margin so it does not collide with projectOptionsHost
  300. if (MOBILE_DEVICE) $container.css('margin-left', '13px');
  301. } else {
  302. $container.removeClass('mobileMode');
  303. $header.text(projectName ? projectName : 'Pages');
  304. $container.find('.sitemapPageName').removeClass('mobileText');
  305. if (MOBILE_DEVICE) $container.css('margin-left', '');
  306. }
  307. }
  308. function togglePageNotesMobileMode() {
  309. var $container = $('#pageNotesHost');
  310. if (!$container.length) return;
  311. if (isMobileMode()) {
  312. $container.addClass('mobileMode');
  313. $('#pageNotesSectionHeader').text('PAGE NOTES');
  314. $('#widgetNotesSectionHeader').text('WIDGET NOTES');
  315. $container.find('.notesPageNameHeader').addClass('mobileSubHeader');
  316. $container.find('.pageNote').addClass('mobileText');
  317. $container.find('.emptyStateTitle').addClass('mobileSubHeader');
  318. $container.find('.emptyStateContent').addClass('mobileText');
  319. } else {
  320. $container.removeClass('mobileMode');
  321. $('#pageNotesSectionHeader').text('Page Notes');
  322. $('#widgetNotesSectionHeader').text('Widget Notes');
  323. $container.find('.notesPageNameHeader').removeClass('mobileSubHeader');
  324. $container.find('.pageNote').removeClass('mobileText');
  325. $container.find('.emptyStateTitle').removeClass('mobileSubHeader');
  326. $container.find('.emptyStateContent').removeClass('mobileText');
  327. }
  328. }
  329. function toggleFeedbackMobileMode() {
  330. var $container = $('#feedbackHost');
  331. if (!$container.length) return;
  332. if (isMobileMode()) {
  333. $container.addClass('mobileMode');
  334. $('.noDiscussionText span').text('Comments added in Axure Cloud will appear here');
  335. } else {
  336. $container.removeClass('mobileMode');
  337. $('.noDiscussionText span').text('Either select the button above to post to a location on the page, or use the field to post without location.');
  338. }
  339. }
  340. $axure.player.updatePlugins = function updatePlugins() {
  341. if (MOBILE_DEVICE && !isShareApp()) {
  342. var hostPanelPadding = isMobileMode() ? '8px 15px 0px 15px' : '';
  343. $('.rightPanel .leftPanel .mobileOnlyPanel').css('padding', hostPanelPadding);
  344. }
  345. if (isMobileMode()) {
  346. $('body').addClass('mobileMode');
  347. if ($('#debugHost').length) $('#debugHost').hide();
  348. if ($('#handoffHost').length) $('#handoffHost').hide();
  349. } else $('body').removeClass('mobileMode');
  350. toggleSitemapMobileMode();
  351. togglePageNotesMobileMode();
  352. toggleFeedbackMobileMode();
  353. }
  354. // TODO: this is done for IOS and Android (check what can be done for Pixel, etc)
  355. $axure.player.setIsMobileModeTextEntry = function (isTextEntry) {
  356. isMobileTextEntry = isTextEntry;
  357. if (IOS && isTextEntry) {
  358. activateMobileTextEntry()
  359. } else if (IOS) {
  360. setTimeout(deactivateMobileTextEntry, 150);
  361. }
  362. }
  363. function deactivateMobileTextEntry() {
  364. newHeight = window.innerHeight;
  365. var newControlHeight = newHeight - (!isShareApp() ? 140 : IOS ? 157 : 138);
  366. if (!$('.leftPanel').hasClass('popup')) {
  367. $('.leftPanel').height(newControlHeight);
  368. }
  369. $('.rightPanel').height(newControlHeight);
  370. $('.mobileOnlyPanel').height(newControlHeight);
  371. $('#mobileControlFrameContainer').show();
  372. }
  373. function activateMobileTextEntry() {
  374. $('#mobileControlFrameContainer').hide();
  375. newHeight = window.innerHeight;
  376. var newControlHeight = newHeight - (!isShareApp() ? 140 : IOS ? 157 : 138);
  377. newControlHeight = newControlHeight + (!isShareApp() ? 61 : IOS ? 72 : 60);
  378. if (!$('.leftPanel').hasClass('popup')) {
  379. $('.leftPanel').height(newControlHeight);
  380. }
  381. $('.rightPanel').height(newControlHeight);
  382. $('.mobileOnlyPanel').height(newControlHeight);
  383. }
  384. function setAdaptiveView() {
  385. if (typeof noViewport == 'undefined') {
  386. // Block during animation -- end of animation will call resizeContent once completed with isAnimating equal to false
  387. if (!isViewOverridden() && !isAnimating) $axure.messageCenter.postMessage('setAdaptiveViewForSize', { 'width': $('#mainPanel').width(), 'height': $('#mainPanel').height() });
  388. //if (!isViewOverridden()) $axure.messageCenter.postMessage('setAdaptiveViewForSize', { 'width': $('#mainPanel').width(), 'height': $('#mainPanel').height() });
  389. $axure.player.refreshViewPort();
  390. if ($axure.player.updateAdaptiveViewHeader != null) $axure.player.updateAdaptiveViewHeader();
  391. }
  392. }
  393. $axure.player.resizeContent = function (noViewport) {
  394. var isMobile = isMobileMode();
  395. var $left = $('.leftPanel');
  396. var $right= $('.rightPanel');
  397. if (wasMobile && !isMobile) {
  398. $('#clippingBoundsScrollContainer').show();
  399. $('#outerContainer').prepend($('.leftPanel'));
  400. $('#outerContainer').append($('.rightPanel'));
  401. $axure.player.updatePlugins();
  402. $('#mHideSidebar').hide();
  403. $('#mobileBrowserControlFrame').hide();
  404. $('#nativeAppControlFrame').hide();
  405. if ($('#topPanel').is(':visible')) {
  406. $('#maximizePanelContainer').hide();
  407. $axure.player.restorePlugins();
  408. } else {
  409. $('.leftPanel').hide();
  410. $('.rightPanel').hide();
  411. if (!MOBILE_DEVICE) $('#maximizePanelContainer').show();
  412. }
  413. $('.leftPanel').css({ 'top': '', 'left': '' });
  414. $('.rightPanel').css({ 'top': '', 'left': '' });
  415. } else if (!wasMobile && isMobile) {
  416. $('#clippingBoundsScrollContainer').hide();
  417. $axure.player.closePopup();
  418. $('#lsplitbar').hide();
  419. $('#rsplitbar').hide();
  420. $('.leftPanel').show();
  421. $('.rightPanel').show();
  422. $axure.player.updatePlugins();
  423. $('#mHideSidebar').append($('.leftPanel'));
  424. $('#mHideSidebar').append($('.rightPanel'));
  425. if (MOBILE_DEVICE) $('#maximizePanelContainer').hide();
  426. $axure.messageCenter.postMessage('collapseFrameOnLoad');
  427. }
  428. var newHeight = 0;
  429. var newWidth = 0;
  430. if (iphoneX && isShareApp()) {
  431. // Hack for Iphone X
  432. newHeight = $(window).height() - ((!isMobile && $('#topPanel').is(':visible')) ? $('#topPanel').height() : 0);
  433. newWidth = $(window).width();
  434. // This does not need to make sense, since it is Iphone X
  435. var notchAndHomeOffsetPortrait = iphoneXFirstPass ? 35 : 5;
  436. var notchOffsetLandscape = iphoneXFirstPass ? 45 : 10;
  437. var homeButtonOffsetLandscape = iphoneXFirstPass ? 21 : 10;
  438. if (newHeight > newWidth) {
  439. newHeight = newHeight + notchAndHomeOffsetPortrait;
  440. } else {
  441. newWidth = newWidth + notchOffsetLandscape * 2;
  442. newHeight = newHeight + homeButtonOffsetLandscape;
  443. }
  444. } else {
  445. // innerHeight includes padding for window -- needed in iOS 11 to have prototype stretch to bottom of screen (could put in -- if (iOS) -- block if needed)
  446. //var newHeight = $(window).height() - ((!isMobile && $('#topPanel').is(':visible'))? $('#topPanel').height() : 0);
  447. newHeight = window.innerHeight - ((!isMobile && $('#topPanel').is(':visible')) ? $('#topPanel').height() : 0);
  448. newWidth = $(window).width();
  449. }
  450. $('#outerContainer').height(newHeight).width(newWidth);
  451. $('#mainPanel').height(newHeight);
  452. $('#clippingBounds').height(newHeight);
  453. if (isMobile) {
  454. $('#mobileControlFrameContainer').height(newHeight);
  455. $('#mobileControlFrameContainer').width(newWidth);
  456. var newControlHeight = newHeight - (!MOBILE_DEVICE ? 112 : !isShareApp() ? 140 : IOS ? 157 : 138);
  457. // Screen resize is only way through browser to catch mobile device keyboard expand and collapse
  458. if ($('#mHideSidebar').is(':visible') && !$('#mobileControlFrameContainer').is(':visible')) {
  459. $('#mobileControlFrameContainer').delay(150).show();
  460. } else if (isMobileTextEntry) {
  461. newControlHeight = newControlHeight + (!isShareApp() ? 61 : IOS ? 72 : 60);
  462. $('#mobileControlFrameContainer').hide();
  463. }
  464. if(!$('.leftPanel').hasClass('popup')) {
  465. $('.leftPanel').height(newControlHeight);
  466. }
  467. $('.rightPanel').height(newControlHeight);
  468. $('.mobileOnlyPanel').height(newControlHeight);
  469. } else {
  470. if (!$('.leftPanel').hasClass('popup')) {
  471. $('.leftPanel').css('height','');
  472. }
  473. $('.rightPanel').css('height', '');
  474. if ($('.rightPanel').is(':visible')) {
  475. var newWidth = Math.min($(window).width() - lastRightPanelWidthDefault, $('.rightPanel').width(), $(window).width() - $('.leftPanel').width());
  476. lastRightPanelWidth = Math.max(lastRightPanelWidthDefault, newWidth);
  477. $('.rightPanel').width(lastRightPanelWidth != 0 ? lastRightPanelWidth : lastRightPanelWidthDefault);
  478. $('#rsplitbar').css('left', $(window).width() - $('.rightPanel').width());
  479. }
  480. if ($('.leftPanel').is(':visible')) {
  481. var newWidth = Math.min($(window).width() - lastLeftPanelWidthDefault, $('.leftPanel').width(), $(window).width() - $('.rightPanel').width());
  482. lastLeftPanelWidth = Math.max(lastLeftPanelWidthDefault, newWidth);
  483. $('.leftPanel').width(lastLeftPanelWidth != 0 ? lastLeftPanelWidth : lastLeftPanelWidthDefault);
  484. $('#lsplitbar').css('left', $('.leftPanel').width() - 4);
  485. }
  486. }
  487. if (isMobile) {
  488. var newControlWidth = newWidth - 80;
  489. $('.leftPanel').css({ 'width': newControlWidth + 'px' });
  490. $('.rightPanel').css({ 'width': newControlWidth + 'px' });
  491. $('.mobileOnlyPanel').css({ 'width': newControlWidth + 'px' });
  492. adjustM('left');
  493. }
  494. updateClippingBoundsWidth();
  495. repositionClippingBoundsScroll();
  496. setAdaptiveView();
  497. wasMobile = isMobile;
  498. }
  499. function contentDocument_onload() {
  500. (function setRepositionWhenReady() {
  501. var $iframe = $('#mainPanel').find('iframe')[0];
  502. if ($($iframe.contentWindow.document.body).length === 0 || $iframe.contentWindow.document.URL === "about:blank") {
  503. setTimeout(setRepositionWhenReady, 50);
  504. } else {
  505. var $iframe = $($('#mainPanel').find('iframe')[0].contentWindow.document);
  506. $iframe.scroll(function () {
  507. repositionClippingBoundsScroll();
  508. });
  509. }
  510. })();
  511. }
  512. // This is the full width and height of the prototype (beyond the window width and height)
  513. var determineIframeDimensions = function () {
  514. var $iframe = $($('#mainPanel').find('iframe')[0].contentWindow);
  515. return {
  516. width: $iframe.width(),
  517. height: $iframe.height()
  518. };
  519. };
  520. // Position of this (upper left hand corner) should match the existingPinPanel position
  521. var determineIframePosition = function () {
  522. var dimensions = determineIframeDimensions();
  523. var $iframe = $($('#mainPanel').find('iframe')[0].contentWindow);
  524. var $body = $($iframe[0].document.body);
  525. var bodyWidth = $body.offset().left !== 0 ? $body.width() : dimensions.width;
  526. if (FIREFOX) {
  527. var left = $body[0].getBoundingClientRect().left;
  528. bodyWidth = left !== 0 ? $body.width() : dimensions.width;
  529. }
  530. return {
  531. top: 0,// Math.max(0, (dimensions.height - $($iframe[0].document.body).height()) / 2),
  532. left: Math.max(0, (dimensions.width - bodyWidth) / 2)
  533. };
  534. };
  535. // Return iframe scroll top and scroll left
  536. var determineIframeScroll = function () {
  537. var $iframe = $($('#mainPanel').find('iframe')[0].contentWindow);
  538. return {
  539. scrollTop: $iframe.scrollTop(),
  540. scrollLeft: $iframe.scrollLeft()
  541. };
  542. };
  543. function calculateClippingBoundsWidth(panelSize, isLeftPanel) {
  544. var $leftPanel = $('.leftPanel:visible');
  545. var leftPanelOffset = (!isMobileMode() && $leftPanel.length > 0 && !$leftPanel.hasClass('popup')) ? $leftPanel.width() : 0;
  546. var $rightPanel = $('.rightPanel:visible');
  547. var rightPanelOffset = (!isMobileMode() && $rightPanel.length > 0) ? $rightPanel.width() : 0;
  548. // Replace current panel size with panel size after animation for expand or collapse completes
  549. if (typeof panelSize !== 'undefined') {
  550. if (isLeftPanel) leftPanelOffset = panelSize;
  551. else rightPanelOffset = panelSize;
  552. }
  553. return $(window).width() - rightPanelOffset - leftPanelOffset;
  554. }
  555. var updateClippingBoundsWidth = $axure.player.updateClippingBoundsWidth = function () {
  556. if ($('.leftPanel').is(':visible')) $('#clippingBounds').css('left', $('.leftPanel').width());
  557. else $('#clippingBounds').css('left', '0px');
  558. $('#clippingBounds').width(calculateClippingBoundsWidth());
  559. }
  560. var contentLeftOfOriginOffset = 0;
  561. function calculateClippingBoundsScrollPosition() {
  562. // Adjust for mainPanelContainer scaling (scale should be "none" for scaleVal == 0 or scaleVal == 1)
  563. var $iframe = $($('#mainPanel').find('iframe')[0].contentWindow);
  564. var selectedScale = $('.vpScaleOption').find('.selectedRadioButton');
  565. var scaleVal = $(selectedScale).parent().attr('val');
  566. var scale = $('#mainPanelContainer').css('transform');;
  567. scale = (scale == "none") ? 1 : Number(scale.substring(scale.indexOf('(') + 1, scale.indexOf(',')));
  568. // Iframe and Main Panel Positioning
  569. var iframeScroll = determineIframeScroll();
  570. var iframePos = determineIframePosition();
  571. var viewablePanelLeftMargin = parseInt($('#mainPanelContainer').css('margin-left'));
  572. var viewablePanelTop = parseInt($('#mainPanelContainer').css('top'));
  573. if (isNaN(viewablePanelTop)) viewablePanelTop = 0;
  574. if (scaleVal == 2) {
  575. // Scale to Fit (account for main panel container scale) -- needed for device mode in Scale to Fit
  576. viewablePanelLeftMargin = ($('#mainPanel').width() - ($('#mainPanelContainer').width() * scale)) / 2
  577. viewablePanelTop = ($('#mainPanel').height() - ($('#mainPanelContainer').height() * scale)) / 2
  578. }
  579. // left and top positioning
  580. var leftPos = viewablePanelLeftMargin + (iframePos.left - iframeScroll.scrollLeft) * scale;
  581. var topPos = viewablePanelTop - iframeScroll.scrollTop * scale;
  582. // Special cases for Centered Page
  583. var isCentered = $($iframe[0].document.body).css('position') == 'relative';
  584. if (isCentered && scaleVal == 1) leftPos = 0;
  585. else if (isCentered && scaleVal == 2) leftPos = $('#mainPanelContainer').width() * scale / 2.0 - contentLeftOfOriginOffset;
  586. return {
  587. left: leftPos,
  588. top: topPos
  589. }
  590. }
  591. function repositionClippingBoundsScroll() {
  592. if (!$axure.player.settings.isAxshare) return;
  593. (function repositionWhenReady() {
  594. if ($($('#mainPanel').find('iframe')[0].contentWindow.document.body).length === 0) {
  595. setTimeout(repositionWhenReady, 50);
  596. } else {
  597. var position = calculateClippingBoundsScrollPosition();
  598. // Adding mainPanel scroll here, since it does not work well with calculating animation left position
  599. position.left = position.left - $('#mainPanel').scrollLeft() - $('#clipFrameScroll').scrollLeft();
  600. position.top = position.top - $('#mainPanel').scrollTop() - $('#clipFrameScroll').scrollTop();
  601. $('#clippingBoundsScrollContainer').css('left', position.left + 'px');
  602. $('#clippingBoundsScrollContainer').css('top', position.top + 'px');
  603. }
  604. })();
  605. }
  606. function calculateScrollLeftWithOffset(offset, isLeftPanel) {
  607. if (!$axure.player.settings.isAxshare) return;
  608. if ($($('#mainPanel').find('iframe')[0].contentWindow.document.body).length === 0) return;
  609. var scaleVal = $('.vpScaleOption').find('.selectedRadioButton').parent().attr('val');
  610. if (scaleVal == 2) return;
  611. var $iframe = $($('#mainPanel').find('iframe')[0].contentWindow);
  612. var $body = $($iframe[0].document.body);
  613. var dimStr = $('.currentAdaptiveView').attr('data-dim');
  614. var hasFrame = (!dimStr ? false : dimStr.split('x')[1] != '0') && !$axure.player.noFrame;
  615. var isCentered = $body.css('position') == 'relative'; //body position is always static while page is still loading (thus false, if called on intial load)
  616. var isCollapsing = offset > 0; //offset is positive when collapsing since we are gaining offset more space for content viewing
  617. // Base case left positioning
  618. var leftPos = calculateClippingBoundsScrollPosition().left;
  619. // If maintaining view options requires a left adjustment not equivalent to panel size (which has already being added in leftPos above)
  620. var viewAdjustment = 0;
  621. // Mobile Frame adjustment
  622. if (hasFrame) {
  623. var viewablePanelLeftMargin = parseInt($('#mainPanelContainer').css('margin-left'));
  624. var viewablePanelRightMargin = parseInt($('#mainPanelContainer').css('margin-right'));
  625. // Cases
  626. // 0) Adaptive view frame doesn't fit in viewable bounds (viewablePanelLeftMargin is zero) -- use entire offset of panel (no adjustment needed)
  627. // 1) Adaptive view frame fits in bounds -- then half of incoming panel will be split left and half right (offset / 2)
  628. // 2) and 3) View Frame either fits in bounds before animation and no longer will after, or vice versa. Mix of previous two cases
  629. if (isCollapsing) {
  630. if (viewablePanelLeftMargin != 0) {
  631. viewAdjustment = offset / 2;
  632. } else if (-viewablePanelRightMargin < offset) {
  633. viewAdjustment = ((offset + viewablePanelRightMargin) / 2);
  634. }
  635. } else if (viewablePanelLeftMargin != 0) {
  636. viewAdjustment = Math.max(offset / 2, -viewablePanelLeftMargin)
  637. }
  638. }
  639. // Centered Page adjustment
  640. if (isCentered) {
  641. // Width of content not able to fit inside current viewable frame
  642. var clippedContentWidth = $body.width() - calculateClippingBoundsWidth(Math.abs(offset), isLeftPanel);
  643. // Cases
  644. // 0) Content never fits in bounds -- then entire offset of panel will move content left value (no adjustment needed as already handled)
  645. // 1) Content fits in bounds -- then half of incoming panel offset will be split left and half right (offset / 2)
  646. // 2) and 3) Content either fits in bounds before animation and no longer will after, or vice versa. Mix of previous two cases
  647. if (clippedContentWidth <= 0) {
  648. viewAdjustment = offset / 2;
  649. } else if (isCollapsing && clippedContentWidth < offset) {
  650. viewAdjustment = (offset - clippedContentWidth) / 2;
  651. } else if (!isCollapsing && clippedContentWidth < -offset) {
  652. viewAdjustment = (clippedContentWidth + offset) / 2;
  653. }
  654. }
  655. return leftPos + viewAdjustment;
  656. }
  657. // Set to true when left panel or right panel are being expanded/collapsed
  658. // returns to false when lsplitbar (switched to clippingBounds) finishes animation (thus panels will be fully expanded or retracted at this point)
  659. var isAnimating = $axure.player.isAnimating = false;
  660. $axure.player.collapseToBar = function (context, hostId) {
  661. lastLeftPanelWidth = $('.leftPanel').width();
  662. lastRightPanelWidth = $('.rightPanel').width();
  663. if (context === 'project' || context === 'all') {
  664. if(!isMobileMode()) {
  665. isAnimating = true;
  666. var newWidth = lastLeftPanelWidth != 0 ? lastLeftPanelWidth : lastLeftPanelWidthDefault;
  667. var clippingWidth = calculateClippingBoundsWidth(0, true);
  668. var newLeft = calculateScrollLeftWithOffset(newWidth, true);
  669. $('.leftPanel').animate({ 'margin-left': -newWidth + 'px' },
  670. { duration: 200, complete: function() { $('.leftPanel').width(0).hide().css({ 'margin-left': '' }); } });
  671. $('#lsplitbar').animate({ left: '-4px' },
  672. { duration: 200, complete: function() { $('#lsplitbar').hide(); } });
  673. $('#clippingBounds').animate({ left: '', width: clippingWidth + 'px' }, { duration: 200 });
  674. $('#clippingBoundsScrollContainer').animate({ left: newLeft + 'px' },
  675. { duration: 200, complete: function () {
  676. isAnimating = false;
  677. $axure.player.resizeContent();
  678. $axure.player.pluginVisibleChanged(hostId, false);
  679. }});
  680. } else {
  681. $('.leftPanel').width(0);
  682. $('#lsplitbar').hide();
  683. }
  684. }
  685. if (context === 'inspect' || context === 'all') {
  686. if (!isMobileMode()) {
  687. isAnimating = true;
  688. var newWidth = lastRightPanelWidth != 0 ? lastRightPanelWidth : lastRightPanelWidthDefault;
  689. var clippingWidth = calculateClippingBoundsWidth(0, false);
  690. var newLeft = calculateScrollLeftWithOffset(newWidth, false);
  691. $('.rightPanel').animate({ 'margin-right': -newWidth + 'px' },
  692. { duration: 200, complete: function () { $('.rightPanel').width(0).hide().css({ 'margin-right': '' }); } });
  693. $('#rsplitbar').animate({ left: $(window).width() + 'px' },
  694. { duration: 200, complete: function () { $('#rsplitbar').hide(); } });
  695. $('#clippingBounds').animate({ width: clippingWidth + 'px' }, { duration: 200 });
  696. $('#clippingBoundsScrollContainer').animate({ left: newLeft + 'px' },
  697. { duration: 200, complete: function () {
  698. isAnimating = false;
  699. $axure.player.resizeContent();
  700. $axure.player.pluginVisibleChanged(hostId, false);
  701. }});
  702. } else {
  703. $('.rightPanel').width(0);
  704. $('#rsplitbar').hide();
  705. }
  706. }
  707. $(window).resize();
  708. toolBarOnly = true;
  709. }
  710. $axure.player.expandFromBar = function (hostId, context, isFinalPluginToRestore) {
  711. if (context === 'project') {
  712. if ($('#lsplitbar').is(':visible')) return;
  713. $('.leftPanel').removeClass('popup');
  714. if(!isMobileMode()) {
  715. isAnimating = true;
  716. var newWidth = (lastLeftPanelWidth != 0 ? lastLeftPanelWidth : lastLeftPanelWidthDefault);
  717. var clippingWidth = calculateClippingBoundsWidth(newWidth, true);
  718. var newLeft = calculateScrollLeftWithOffset(-newWidth, true);
  719. $('.leftPanel').width(newWidth);
  720. $('.leftPanel').css('margin-left', -newWidth + 'px').show();
  721. $('.leftPanel').animate({ 'margin-left': '0px' }, { duration: 200, complete: function () { $('.leftPanel').css({ 'margin-left': '' }); } });
  722. $('#lsplitbar').css('left', '-4px');
  723. $('#lsplitbar').show();
  724. $('#lsplitbar').animate({ left: newWidth - 4 + 'px' }, { duration: 200 });
  725. $('#clippingBounds').animate({ left: newWidth + 'px', width: clippingWidth + 'px' }, { duration: 200 });
  726. $('#clippingBoundsScrollContainer').animate({ left: newLeft + 'px' },
  727. { duration: 200, complete: function () {
  728. isAnimating = false;
  729. $axure.player.resizeContent();
  730. if (isFinalPluginToRestore) $('#clippingBoundsScrollContainer').show();
  731. $axure.player.pluginVisibleChanged(hostId, true);
  732. }});
  733. }
  734. } else {
  735. if($('#rsplitbar').is(':visible')) {
  736. $('#' + hostId).show();
  737. $axure.player.pluginVisibleChanged(hostId, true);
  738. return;
  739. }
  740. if (!isMobileMode()) {
  741. isAnimating = true;
  742. var newWidth = lastRightPanelWidth != 0 ? lastRightPanelWidth : lastRightPanelWidthDefault;
  743. var clippingWidth = calculateClippingBoundsWidth(newWidth, false);
  744. var newLeft = calculateScrollLeftWithOffset(-newWidth, false);
  745. $('.rightPanel').width(newWidth);
  746. $('.rightPanel').css('margin-right', -newWidth + 'px');
  747. $('#' + hostId).show();
  748. $('.rightPanel').animate({ 'margin-right': '0px' }, { duration: 200, complete: function () { $('.rightPanel').css({ 'margin-right': '' }); } });
  749. $('#rsplitbar').css('left', $(window).width());
  750. $('#rsplitbar').show();
  751. $('#rsplitbar').animate({ left: $(window).width() - $('.rightPanel').width() + 'px' }, { duration: 200 });
  752. $('#clippingBounds').animate({ width: clippingWidth + 'px' }, { duration: 200 });
  753. $('#clippingBoundsScrollContainer').animate({ left: newLeft + 'px' },
  754. { duration: 200, complete: function () {
  755. isAnimating = false;
  756. $axure.player.resizeContent();
  757. if (isFinalPluginToRestore) $('#clippingBoundsScrollContainer').show();
  758. $axure.player.pluginVisibleChanged(hostId, true);
  759. }});
  760. }
  761. }
  762. $(window).resize();
  763. toolBarOnly = false;
  764. if (isMobileMode()) {
  765. $('#mHideSidebar').show();
  766. $('#nativeAppControlFrame').show();
  767. }
  768. }
  769. var suspendRefreshViewPort = $axure.player.suspendRefreshViewPort = false;
  770. $axure.player.refreshViewPort = function () {
  771. if (suspendRefreshViewPort) return;
  772. var dimStr = $('.currentAdaptiveView').attr('data-dim');
  773. var dim = dimStr ? dimStr.split('x') : { w: '0', h: '0' };
  774. var w = dim[0] != '0' ? dim[0] : '';
  775. var h = dim[1] != '0' ? dim[1] : '';
  776. var scaleVal = $('.vpScaleOption').find('.selectedRadioButton').parent().attr('val');
  777. $axure.player.noFrame = false;
  778. if (h && scaleVal == 1) $axure.player.noFrame = true;
  779. var clipToView = h && !$axure.player.noFrame;
  780. var isDevice = h;
  781. var mainPanelWidth = $('#mainPanel').width();
  782. var mainPanelHeight = $('#mainPanel').height();
  783. if (!w || !clipToView) w = mainPanelWidth;
  784. if (!h || !clipToView) h = mainPanelHeight;
  785. if (MOBILE_DEVICE && h > mainPanelHeight) h = mainPanelHeight;
  786. if (MOBILE_DEVICE && w > mainPanelWidth) w = mainPanelWidth;
  787. if (clipToView) {
  788. if (scaleVal == '0') scaleVal = 2;
  789. w = Number(w);
  790. h = Number(h);
  791. $('#mainFrame').width(w);
  792. $('#clipFrameScroll').width(w);
  793. $('#mainFrame').height(h);
  794. $('#clipFrameScroll').height(h);
  795. var topPadding = 10;
  796. var leftPadding = 0;
  797. var rightPadding = 0;
  798. var bottomPadding = 10;
  799. if (!MOBILE_DEVICE) {
  800. w = w + leftPadding + rightPadding;
  801. h = h + topPadding + bottomPadding;
  802. }
  803. var x = (mainPanelWidth - w) / 2;
  804. var y = (mainPanelHeight - h) / 2 - 1;
  805. x = Math.max(0, x);
  806. if (scaleVal != 2) y = Math.max(0, y);
  807. $('#mainPanelContainer').css({
  808. 'margin': 'auto',
  809. 'top': y + 'px'
  810. });
  811. $('#clipFrameScroll').css({
  812. 'left': leftPadding + 'px',
  813. 'top': topPadding + 'px'
  814. });
  815. $('#mainPanelContainer').width(w);
  816. $('#mainPanelContainer').height(h);
  817. } else {
  818. $('#mainFrame').width('100%');
  819. $('#mainFrame').height(h);
  820. $('#clipFrameScroll').width('100%');
  821. $('#clipFrameScroll').height(h);
  822. $('#clipFrameScroll').css({ 'left': '', 'top': '' });
  823. $('#mainPanelContainer').width('100%');
  824. $('#mainPanelContainer').height(h);
  825. $('#mainPanelContainer').css({
  826. 'left': '',
  827. 'margin': '',
  828. 'top': ''
  829. });
  830. }
  831. $axure.messageCenter.postMessage('setDeviceMode', { device: isDevice, width: w, scaleToWidth: (scaleVal == "1") });
  832. $(".vpScaleOption").show();
  833. var prevScaleN = $('#mainPanelContainer').css('transform');
  834. prevScaleN = (prevScaleN == "none") ? 1 : Number(prevScaleN.substring(prevScaleN.indexOf('(') + 1, prevScaleN.indexOf(',')));
  835. var newScaleN = 1;
  836. $('#mainPanelContainer').css({
  837. 'transform': '',
  838. 'transform-origin': ''
  839. });
  840. var $leftPanel = $('.leftPanel:visible');
  841. var leftPanelOffset = (!isMobileMode() && $leftPanel.length > 0) ? $leftPanel.width() : 0;
  842. var $rightPanel = $('.rightPanel:visible');
  843. var rightPanelOffset = (!isMobileMode() && $rightPanel.length > 0) ? $rightPanel.width() : 0;
  844. var vpScaleData = {
  845. scale: scaleVal,
  846. prevScaleN: prevScaleN,
  847. viewportHeight: h,
  848. viewportWidth: w,
  849. panelWidthOffset: leftPanelOffset + rightPanelOffset,
  850. clipToView: clipToView
  851. };
  852. $axure.messageCenter.postMessage('getScale', vpScaleData);
  853. if (scaleVal == '0' && clipToView) $('#mainPanel').css('overflow', 'auto');
  854. else $('#mainPanel').css('overflow', '');
  855. }
  856. $axure.player.getProjectName = function getProjectName() {
  857. if (typeof PREVIEW_INFO !== 'undefined') {
  858. return PREVIEW_INFO.fileName;
  859. } else if(typeof $axure.player.settings.projectName !== 'undefined') {
  860. return $axure.player.settings.projectName;
  861. } else return false;
  862. }
  863. function initializeLogo() {
  864. if(typeof PREVIEW_INFO !== 'undefined') {
  865. $('#previewNotice').show();
  866. }
  867. //if (typeof PREVIEW_INFO !== 'undefined') {
  868. // $('#interfaceControlFrameLogoCaptionContainer').html(PREVIEW_INFO.fileName);
  869. //} else if (typeof $axure.player.settings.projectName !== 'undefined') {
  870. // $('#interfaceControlFrameLogoCaptionContainer').html($axure.player.settings.projectName);
  871. //} else {
  872. // $('#interfaceControlFrameLogoCaptionContainer').hide();
  873. //}
  874. //if ($axure.document.configuration.logoImagePath) {
  875. // var image = new Image();
  876. // //image.onload = function () {
  877. // // //$('#logoImage').css('max-width', this.width + 'px');
  878. // // $('#interfaceControlFrameContainer').css('margin-left', '-' + $('#logoImage').width() / 2 + 'px');
  879. // // //$axure.player.resizeContent();
  880. // //};
  881. // image.src = $axure.document.configuration.logoImagePath;
  882. // $('#interfaceControlFrameLogoImageContainer').html('<img id="logoImage" src="" />');
  883. // $('#logoImage').attr('src', $axure.document.configuration.logoImagePath);//.on('load', function () { $axure.player.resizeContent(); });
  884. //} else $('#interfaceControlFrameLogoImageContainer').hide();
  885. //if ($axure.document.configuration.logoImageCaption) {
  886. // $('#interfaceControlFrameLogoCaptionContainer').html($axure.document.configuration.logoImageCaption);
  887. //} else $('#interfaceControlFrameLogoCaptionContainer').hide();
  888. //if(!$('#interfaceControlFrameLogoImageContainer').is(':visible') && !$('#interfaceControlFrameLogoCaptionContainer').is(':visible')) {
  889. // $('#interfaceControlFrameLogoContainer').hide();
  890. //}
  891. }
  892. function initializePreview() {
  893. if (typeof PREVIEW_INFO !== 'undefined') {
  894. $('#separatorContainer').addClass('hasLeft');
  895. $('#overflowMadeWith').addClass('preview');
  896. var callback = undefined;
  897. $('#publishButton').click(function () {
  898. $.ajax({
  899. type: 'GET',
  900. url: 'publish',
  901. data: {},
  902. success: function (response) {
  903. if (callback) callback(response);
  904. },
  905. error: function (response) {
  906. if (callback) callback(response);
  907. },
  908. dataType: 'jsonp'
  909. });
  910. });
  911. }
  912. }
  913. var userAcct = {
  914. userId: '',
  915. userName: '',
  916. userEmail: '',
  917. userProfileImg: '',
  918. isUsingAxureAcct: false,
  919. }
  920. var authCookieValue = null;
  921. var userCookieValue = null;
  922. var isSubInstance = false;
  923. //var readOnlyMode = false;
  924. //var readOnlyMessage = '';
  925. // Watermark hints
  926. // NOTE: The trailing characters serve to be a distinguishing element in case the user actually does use text similar to the hint.
  927. var emailHint = "Email ";
  928. var passHint = "Password ";
  929. var feedbackServiceUrl = (window.AXSHARE_HOST_SECURE_URL || 'https://share.axure.com') + '/issue';
  930. // Look at creating a new location to have GetShareStatus(FbEnabled replacement) and SafariAuth since they are more general calls that are not solely for feedback now
  931. //var prototypeControlUrl = (window.AXSHARE_HOST_SECURE_URL || 'https://share.axure.com') + '/prototype';
  932. // Checks if the browser is Safari 3.0+
  933. // https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
  934. function isSafari() {
  935. // Safari 3.0+ "[object HTMLElementConstructor]"
  936. var liveSafari = /constructor/i.test(window.HTMLElement) || (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window['safari'] || (typeof safari !== 'undefined' && safari.pushNotification));
  937. return liveSafari || SAFARI || (IOS && isShareApp());
  938. };
  939. function includeTokens(ajaxData, excludeUser) {
  940. //If the authCookieValue is set (a password-protected local prototype), then send the
  941. //token as well (because cookies don't always get sent to external domains)
  942. if (authCookieValue) {
  943. $.extend(ajaxData, { token: authCookieValue });
  944. }
  945. if (!excludeUser && userCookieValue) {
  946. $.extend(ajaxData, { utoken: userCookieValue });
  947. }
  948. }
  949. function setUserLoggedInStatus(response, safariAuthResponseProfile) {
  950. if (!response.success) {
  951. userAcct.isUsingAxureAcct = false;
  952. } else {
  953. if (safariAuthResponseProfile) response = safariAuthResponseProfile;
  954. userAcct.userId = response.userId;
  955. if (safariAuthResponseProfile)
  956. userAcct.userName = response.username == null || response.username.trim() === '' ? response.userEmail : decodeURIComponent(response.username.trim());
  957. else
  958. userAcct.userName = response.nickname == null || response.nickname.trim() === '' ? response.userEmail : decodeURIComponent(response.nickname.trim());
  959. userAcct.userEmail = response.userEmail;
  960. userAcct.userProfileImg = response.profileImageUrl;
  961. userAcct.isUsingAxureAcct = true;
  962. if (response.authToken != null) {
  963. $axshare.setAuthCookie(response.authToken);
  964. userCookieValue = response.authToken;
  965. }
  966. }
  967. // If feedback is loaded, update feedback with new account information
  968. if (typeof feedback !== 'undefined') feedback.updateUserAccountInfo(userAcct, authCookieValue, userCookieValue);
  969. }
  970. // TODO: for on prem, we need to use an ajax call directly to share instead of accounts
  971. // Verify authentication against axure accounts
  972. $axure.player.axureAuth = function axureAuth(callback) {
  973. if (window.$axshare != null) {
  974. $axshare.auth(function (response) {
  975. if (response.success) {
  976. setUserLoggedInStatus(response);
  977. } else {
  978. if (isSafari()) {
  979. var ajaxData = {
  980. userId: userAcct.isUsingAxureAcct ? userAcct.userId : ""
  981. };
  982. includeTokens(ajaxData);
  983. $.ajax({
  984. type: 'GET',
  985. url: feedbackServiceUrl + '/safariAuth',
  986. data: ajaxData,
  987. success: function (response) {
  988. if (!response.success) {
  989. setUserLoggedInStatus(response);
  990. } else {
  991. setUserLoggedInStatus(response, response.data.profile[userAcct.userId]);
  992. if (callback != null) {
  993. callback(response);
  994. }
  995. }
  996. },
  997. dataType: 'jsonp'
  998. });
  999. } else {
  1000. setUserLoggedInStatus(response);
  1001. }
  1002. }
  1003. if (callback != null) {
  1004. callback(response);
  1005. }
  1006. });
  1007. }
  1008. }
  1009. // TODO: for on prem, we need to use an ajax call directly to share instead of accounts
  1010. // Log into axure accounts
  1011. $axure.player.axureLogin = function axureLogin(email, password, success, failure, saml) {
  1012. if (window.$axshare != null) {
  1013. password = password === passHint ? "" : password;
  1014. $axshare.login(email, password, false, function (response) {
  1015. if (response.redirecturl !== "" && response.redirecturl != null) {
  1016. saml(response);
  1017. return;
  1018. }
  1019. if (response.success && (response.verified || isSubInstance)) {
  1020. if (isSafari()) setUserLoggedInStatus(response);
  1021. $axure.player.axureAuth(success);
  1022. } else {
  1023. failure(response);
  1024. }
  1025. });
  1026. // TODO: add ldap authentication
  1027. //}, window.ON_PREM_LDAP_ENABLED);
  1028. } else {
  1029. failure();
  1030. }
  1031. }
  1032. function playerLogout() {
  1033. userAcct.isUsingAxureAcct = false;
  1034. userAcct.userId = '';
  1035. userAcct.userProfileImg = '';
  1036. // If feedback is loaded, update feedback with new account information
  1037. if (typeof feedback !== 'undefined') feedback.updateUserAccountInfo(userAcct);
  1038. }
  1039. $axure.player.logout = function (feedbackLogout) {
  1040. var completeLogout = playerLogout;
  1041. if (feedbackLogout) {
  1042. completeLogout = function () {
  1043. feedbackLogout();
  1044. playerLogout();
  1045. }
  1046. }
  1047. if (window.$axshare != null) {
  1048. $axshare.logout(completeLogout);
  1049. } else {
  1050. completeLogout();
  1051. }
  1052. }
  1053. /*
  1054. * TODO: Start of Login/Account Mgmt UI, which will need to be updated (currenly uses feedback9.css often)
  1055. */
  1056. function buildAccountLoginPopup() {
  1057. return [
  1058. '<div class="axClearMsgBubble_Player axureLoginBubble_Player">',
  1059. ' <div class="axureLoginBubbleContainer_Player">',
  1060. ' <span style="font-weight: bold; font-size: 10px;">Login into your Axure Cloud account</span>',
  1061. ' <input type="text" autocapitalize="none" name="email" class="axureEmail" style="margin-top: 7px;"/>',
  1062. ' <input name="password" autocapitalize="none" class="axurePassword" />',
  1063. ' <div class="feedbackGreenBtn_Player">LOG IN</div>',
  1064. ' <div class="errorMessage"></div>',
  1065. ' <div id="playerSignUpLink" style="text-align: right; margin-top: 5px; font-size: 10px;">',
  1066. ' <span>No account? <a class="axureSignUpLink" href="', window.AXSHARE_HOST_SECURE_URL, '" target="_blank">Sign Up</a></span>',
  1067. ' </div>',
  1068. ' </div>',
  1069. '</div>'
  1070. ].join("");
  1071. }
  1072. // Bind events to axure login speech bubble (watermark, login, errors, click outside)
  1073. function bindAxureLoginContainerEvent() {
  1074. var $container = $("#accountLoginContainer");
  1075. $container.find('input[name="email"]').addClass("watermark").val(emailHint).focus(function () {
  1076. if ($(this).val() === emailHint) {
  1077. $(this).removeClass("watermark").val("");
  1078. }
  1079. }).blur(function () {
  1080. if ($(this).val() === "") {
  1081. $(this).addClass("watermark").val(emailHint);
  1082. }
  1083. $container.find('.errorMessage').text('');
  1084. $container.find('.errorMessage').hide();
  1085. }).keyup(function (event) {
  1086. if (event.keyCode == 13) {
  1087. $container.find('.feedbackGreenBtn').click();
  1088. }
  1089. });
  1090. $container.find('input[name="password"]').addClass("watermark").val(passHint).focus(function () {
  1091. if ($(this).val() === passHint) {
  1092. $(this).removeClass("watermark").val("");
  1093. //$(this).removeClass("watermark").val("").attr("type", "password");
  1094. // Note: this might be an issue since jquery doesn't like it. Test in IE
  1095. $(this)[0].setAttribute('type', 'password');
  1096. }
  1097. }).blur(function () {
  1098. if ($(this).val() === "") {
  1099. $(this).val(passHint).addClass("watermark");
  1100. //$(this).val(passHint).addClass("watermark").removeAttr("type");
  1101. // Note: this might be an issue since jquery doesn't like it. Test in IE
  1102. $(this)[0].setAttribute('type', 'text');
  1103. }
  1104. $container.find('.errorMessage').text('');
  1105. $container.find('.errorMessage').hide();
  1106. }).keyup(function (event) {
  1107. if (event.keyCode == 13) {
  1108. $container.find('.feedbackGreenBtn_Player').click();
  1109. }
  1110. });
  1111. // Login Submit Event
  1112. $container.find('.feedbackGreenBtn_Player').click(function (e) {
  1113. var email = $container.find('.axureEmail').val();
  1114. var password = $container.find('.axurePassword').val();
  1115. $axure.player.axureLogin(email, password, function (response) {
  1116. // Success
  1117. // Clear out fields
  1118. $container.find('.axureEmail').val(emailHint).addClass("watermark");
  1119. $container.find('.axurePassword').val(passHint).addClass("watermark");
  1120. $container.find('.axurePassword')[0].setAttribute('type', 'text');
  1121. closePopup();
  1122. }, function (response) {
  1123. // Failure
  1124. $container.find('.errorMessage').text(response != null && response.message ? response.message : "There was an error connecting to the server, please try again later.");
  1125. $container.find('.errorMessage').show();
  1126. }, function (response) {
  1127. // SAML User
  1128. $container.find('.errorMessage').empty();
  1129. $container.find('.errorMessage').append("Please <a class='refreshLink' style='text-decoration: underline;'>refresh</a> this page after logging in via your identity provider.");
  1130. $container.find('.errorMessage').show();
  1131. window.open(response.redirecturl, '_blank');
  1132. $container.find('.errorMessage').find('.refreshLink').click(function () {
  1133. location.reload(true);
  1134. });
  1135. });
  1136. });
  1137. };
  1138. function initializeSignIn() {
  1139. if (typeof PREVIEW_INFO === 'undefined' && $axure.player.settings.isAxshare) {
  1140. (function finishInit() {
  1141. if (window.$axshare == null || $axshare.auth == null || $axshare.login == null) {
  1142. setTimeout(finishInit, 50);
  1143. } else {
  1144. // Call to set readOnlyMode, readOnlyMessage, and isSubinstance (readOnlyMode/Message currently only used for feedback9)
  1145. $.ajax({
  1146. type: 'GET',
  1147. url: feedbackServiceUrl + '/GetShareStatus',
  1148. data: {},
  1149. success: function (response) {
  1150. //readOnlyMode = response.readOnlyMode;
  1151. //readOnlyMessage = response.readOnlyMessage;
  1152. isSubInstance = response.isSubInstance;
  1153. if (isSubInstance) $('#accountLoginContainer').find("#playerSignUpLink").hide();
  1154. // For now, calling methods to set these values in feedback on start (could later make a general method to retrieve these values from player)
  1155. if (typeof feedback !== 'undefined') {
  1156. feedback.setReadOnlyModeAndMessage(response.readOnlyMode, response.readOnlyMessage);
  1157. feedback.setIsSubInstance(isSubInstance);
  1158. }
  1159. },
  1160. dataType: 'jsonp'
  1161. });
  1162. // Login container
  1163. $("#accountLoginContainer").append(buildAccountLoginPopup());
  1164. bindAxureLoginContainerEvent();
  1165. // Attempt to auth and acquire account information, then update top panel
  1166. $axure.player.axureAuth();
  1167. }
  1168. })();
  1169. }
  1170. }
  1171. function overflowIsHidden(node) {
  1172. var style = getComputedStyle(node);
  1173. return style.overflow === 'hidden' || style.overflowX === 'hidden' || style.overflowY === 'hidden';
  1174. }
  1175. function findNearestScrollableParent(firstNode) {
  1176. var node = firstNode;
  1177. var scrollable = null;
  1178. while (!scrollable && node) {
  1179. if (node.scrollWidth > node.clientWidth || node.scrollHeight > node.clientHeight) {
  1180. if (!overflowIsHidden(node) || $(node).css('-webkit-overflow-scrolling') === 'touch') {
  1181. scrollable = node;
  1182. }
  1183. }
  1184. node = node.parentNode;
  1185. }
  1186. return scrollable;
  1187. }
  1188. function getScrollOwner(target) {
  1189. var owner = findNearestScrollableParent(target);
  1190. if (!owner || owner === document.documentElement || owner === document.body || $(owner).parents('#topPanel').length || owner == document.getElementById('forwardSlash')) {
  1191. return null;
  1192. }
  1193. return owner;
  1194. }
  1195. function removeElasticScrollFromIframe() {
  1196. var $iframe = $($('#mainPanel').find('iframe')[0].contentWindow);
  1197. $iframe[0].document.body.addEventListener('touchmove', function (event) {
  1198. if (!getScrollOwner(event.target)) {
  1199. event.preventDefault();
  1200. }
  1201. }, { passive: false });
  1202. }
  1203. $(document).ready(function () {
  1204. (function finishPlayerInit() {
  1205. if ($axure.player.settings.isAxshare) {
  1206. $axure.page.bind('load.start', contentDocument_onload);
  1207. if ($axure.player.settings.loadFeedbackPlugin) {
  1208. $axure.utils.loadJS('/Scripts/plugins/feedback/feedback9.js');
  1209. /******* DEBUG: Allows for debugging/viewing feedback9.js in browser inspect mode ******/
  1210. //var hdr = document.createElement('script');
  1211. //hdr.type = "text/javascript"
  1212. //hdr.src = '/Scripts/plugins/feedback/feedback9.js';
  1213. //document.head.appendChild(hdr);
  1214. }
  1215. }
  1216. initializeEvents();
  1217. initializeMainFrame();
  1218. $('.leftPanel').width(0);
  1219. $('#maximizePanelContainer').hide();
  1220. if ($axure.player.settings.startCollapsed) {
  1221. collapse();
  1222. $('.leftPanel').width(0);
  1223. var maxPanelWidth = $('#maximizePanel').width();
  1224. setTimeout(function() {
  1225. $('#maximizePanel').animate({
  1226. left:'-' + maxPanelWidth + 'px'
  1227. }, 300);
  1228. }, 2000);
  1229. }
  1230. if (MOBILE_DEVICE) {
  1231. $('body').removeClass('hashover');
  1232. if (SAFARI) {
  1233. // Stop pinch zoom (stopping all gestures for now)
  1234. // Gesturestart is only supported in Safari
  1235. document.addEventListener("gesturestart", function (e) {
  1236. e.preventDefault();
  1237. });
  1238. }
  1239. if (IOS) {
  1240. // Attempt at removing elastic scroll while in mobile menu
  1241. var touching = false;
  1242. var pageYStart = 0;
  1243. var pageYOffset = 0;
  1244. document.body.addEventListener('touchend', function (event) {
  1245. if (getScrollOwner(event.target)) {
  1246. touching = false;
  1247. }
  1248. }, { passive: false });
  1249. document.body.addEventListener('touchmove', function (event) {
  1250. var owner = getScrollOwner(event.target)
  1251. if (!owner) {
  1252. event.preventDefault();
  1253. } else {
  1254. if ($(owner).scrollTop() == 0) {
  1255. if (touching) {
  1256. if (event.pageY >= pageYStart) {
  1257. event.preventDefault();
  1258. }
  1259. }
  1260. }
  1261. if ($(owner).scrollTop() + $(owner).height() == owner.scrollHeight) {
  1262. if (touching) {
  1263. if (event.pageY <= pageYStart) {
  1264. event.preventDefault();
  1265. }
  1266. }
  1267. }
  1268. }
  1269. }, { passive: false });
  1270. document.body.addEventListener('touchstart', function (event) {
  1271. var owner = getScrollOwner(event.target);
  1272. if (owner) {
  1273. if ($(owner).scrollTop() == 0) {
  1274. touching = true;
  1275. pageYStart = event.pageY;
  1276. pageYOffset = event.pageY;
  1277. }
  1278. if ($(owner).scrollTop() + $(owner).height() == owner.scrollHeight) {
  1279. touching = true;
  1280. pageYStart = event.pageY;
  1281. pageYOffset = event.pageY;
  1282. }
  1283. }
  1284. }, { passive: false });
  1285. removeElasticScrollFromIframe();
  1286. $('html').css('-webkit-tap-highlight-color', 'transparent');
  1287. // Stop iOS from automatically scaling parts of the mobile player
  1288. // Could stop automatic scaling on Ipads as well that we actually want, but for now, seems fine
  1289. $('body').css('-webkit-text-size-adjust', '100%');
  1290. // Prepare for Iphone X hacks
  1291. // Link for dimensions: https://kapeli.com/cheat_sheets/iOS_Design.docset/Contents/Resources/Documents/index
  1292. var ratio = window.devicePixelRatio || 1;
  1293. // Regular iphoneX
  1294. if (IOS && window.screen.width * ratio == 1125 && window.screen.height * ratio === 2436) {
  1295. iphoneX = true;
  1296. }
  1297. // Iphone XS Max and Iphone XR
  1298. if (IOS && window.screen.width == 414 && window.screen.height === 896) {
  1299. iphoneX = true;
  1300. }
  1301. window.addEventListener("orientationchange", function () {
  1302. var viewport = document.querySelector("meta[name=viewport]");
  1303. //so iOS doesn't zoom when switching back to portrait
  1304. if (iphoneX) {
  1305. viewport.setAttribute('content', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, viewport-fit=cover');
  1306. viewport.setAttribute('content', 'width=device-width, initial-scale=1.0, viewport-fit=cover');
  1307. } else {
  1308. viewport.setAttribute('content', 'width=device-width, initial-scale=1.0, maximum-scale=1.0');
  1309. viewport.setAttribute('content', 'width=device-width, initial-scale=1.0');
  1310. }
  1311. $axure.player.resizeContent();
  1312. }, false);
  1313. $axure.page.bind('load.start', function () {
  1314. $axure.player.resizeContent();
  1315. });
  1316. }
  1317. // Always append both mobile browser menu and native menu, as app might not have returned value signifying native at this point
  1318. appendNativePrototypeControlFrame();
  1319. appendMobileBrowserControlFrame();
  1320. appendProjectOptions();
  1321. }
  1322. initializeLogo();
  1323. initializePreview();
  1324. $axure.player.resizeContent(true);
  1325. // Has timeout to keep waiting to build sign in controls while axAccount is still loading
  1326. initializeSignIn();
  1327. })();
  1328. });
  1329. function appendProjectOptions() {
  1330. var toAppend = '';
  1331. toAppend += '<div id="projectOptionsHost" class="mobileOnlyPanel mobileMode">';
  1332. toAppend += ' <div class="pluginNameHeader">PROJECT OPTIONS</div>';
  1333. toAppend += ' <div id="projectOptionsScrollContainer">';
  1334. toAppend += ' <div class="mobileSubHeader">Hotspots</div>';
  1335. toAppend += ' <div id="projectOptionsShowHotspots" class="mobileText projectOptionsHotspotsRow" style="border-bottom: solid 1px #c7c7c7">';
  1336. toAppend += ' <div id="projectOptionsHotspotsCheckbox"></div>';
  1337. toAppend += ' Show Hotspots</div> ';
  1338. toAppend += ' <div class="mobileSubHeader" style="margin-top: 16px">Scale</div>';
  1339. toAppend += ' <div id="projectOptionsScaleContainer" class="mobileText"></div>';
  1340. toAppend += ' <div id="projectOptionsAdaptiveViewsHeader" class="mobileSubHeader" style="margin-top: 16px">Adaptive Views</div>';
  1341. toAppend += ' <div id="projectOptionsAdaptiveViewsContainer" class="mobileText"></div>'
  1342. toAppend += ' </div>'
  1343. toAppend += '</div>';
  1344. $('#mHideSidebar').prepend(toAppend);
  1345. $(('#projectOptionsHost')).click(function (e) { e.stopPropagation(); });
  1346. if (isMobileMode()) $axure.player.resizeContent();
  1347. }
  1348. function appendMobileBrowserControlFrame() {
  1349. var toAppend = "";
  1350. toAppend += '<div id="mobileBrowserControlFrame" class="mobilePrototypeControlFrame">';
  1351. toAppend += ' <div id="return" style="width:100%; position:relative; top:-15px; float:left">';
  1352. toAppend += ' <div id="closeBackground" class="circleBackground">';
  1353. toAppend += ' <div id="forwardSlash" class="closeIconSlash"><div id="backwardSlash" class="closeIconSlash"></div></div>';
  1354. toAppend += ' </div>';
  1355. toAppend += ' </div>';
  1356. toAppend += '</div>';
  1357. $('#mobileControlFrameContainer').append(toAppend);
  1358. $('#closeBackground').click(collapse);
  1359. // iOS will do incorrect click position / content bounds calculation which results in scroll getting reset to (0, 0)
  1360. if (IOS) $('#mobileControlFrameContainer').on($axure.eventNames.mouseDownName, function (e) { e.stopPropagation(); });
  1361. }
  1362. function appendNativePrototypeControlFrame() {
  1363. var toAppend = "";
  1364. toAppend += '<div id="nativeAppControlFrame" class="mobilePrototypeControlFrame">';
  1365. toAppend += ' <ul id="nativeMenuBlueBackground">';
  1366. toAppend += ' <li style="width:30%; float:left;">';
  1367. toAppend += ' <div id="exit" class="nativePrototypeButton" >';
  1368. toAppend += ' <div>';
  1369. toAppend += ' <div id="exitIcon"></div>';
  1370. toAppend += ' <div id="exitText" class="nativeMenuText">Exit</div>';
  1371. toAppend += ' </div>';
  1372. toAppend += ' </div>';
  1373. toAppend += ' </li>';
  1374. toAppend += ' <li id="return" style="width:40%; position:relative; top:-15px; float:left">';
  1375. toAppend += ' <div id="returnBackground" class="circleBackground">';
  1376. toAppend += ' <div id="returnIcon"></div>';
  1377. toAppend += ' </div>';
  1378. toAppend += ' <div id="returnText" class="nativeMenuText">Return to Prototype</div>';
  1379. toAppend += ' </li>';
  1380. toAppend += ' <li style="width:30%; float:right;">';
  1381. toAppend += ' <div id="refresh" class="nativePrototypeButton" >';
  1382. toAppend += ' <div>';
  1383. toAppend += ' <div id="refreshIcon"></div>';
  1384. toAppend += ' <div id="refreshText" class="nativeMenuText">Refresh</div>';
  1385. toAppend += ' </div>';
  1386. toAppend += ' </div>';
  1387. toAppend += ' </li>';
  1388. toAppend += ' </ul>';
  1389. toAppend += '</div>';
  1390. $('#mobileControlFrameContainer').append(toAppend);
  1391. var barHeight = IOS ? (iphoneX ? '82px' : '72px') : '60px';
  1392. var returnIconDisplacement = IOS ? '-15px': '-20px';
  1393. var iconTopMargin = IOS ? '14px': '7px';
  1394. var returnTextTopMargin = IOS ? '9px': '7px';
  1395. document.getElementById('nativeAppControlFrame').style.height = barHeight;
  1396. document.getElementById('nativeMenuBlueBackground').style.height = barHeight;
  1397. document.getElementById('return').style.top = returnIconDisplacement;
  1398. document.getElementById('returnText').style.marginTop = returnTextTopMargin;
  1399. document.getElementById('refreshIcon').style.marginTop = iconTopMargin;
  1400. document.getElementById('exitIcon').style.marginTop = iconTopMargin;
  1401. addAppButtonClickListener("exit");
  1402. addAppButtonClickListener("refresh");
  1403. $('#returnBackground').click(collapse);
  1404. $('#nativeAppControlFrame').on('touchmove', function (e) {
  1405. e.stopPropagation();
  1406. }, false);
  1407. }
  1408. function addAppButtonClickListener(id) {
  1409. var func = function () { IOS ? window.webkit.messageHandlers.prototypeMenuButtonClick.postMessage(id) : ShareApp.PrototypeMenuButtonClick(id); };
  1410. document.getElementById(id).addEventListener("click", func, false);
  1411. }
  1412. function toggleSitemap() {
  1413. $axure.player.showPlugin(1);
  1414. }
  1415. function closePopup() {
  1416. var $container = $('.popup');
  1417. var isLeftPanel = $container.hasClass('leftPanel');
  1418. $container.removeClass('popup');
  1419. $('#overflowMenuButton').removeClass('selected');
  1420. $('#interfaceAdaptiveViewsContainer').removeClass('selected');
  1421. $container.hide();
  1422. $('div.splitterMask').unbind($axure.eventNames.mouseDownName, closePopup);
  1423. $('div.splitterMask').remove();
  1424. }
  1425. $axure.player.closePopup = closePopup;
  1426. function showPopup($container) {
  1427. if ($('#browserOutOfDateNotification').is(":visible")) return;
  1428. $container.addClass('popup');
  1429. $container.show();
  1430. $('<div class="splitterMask"></div>').insertAfter($container);
  1431. $('div.splitterMask').bind($axure.eventNames.mouseDownName, closePopup);
  1432. }
  1433. $axure.player.showPopup = showPopup;
  1434. function toggleAdaptiveViewsPopup() {
  1435. if (($('#interfaceAdaptiveViewsListContainer').hasClass('popup'))) {
  1436. closePopup();
  1437. } else {
  1438. $('#interfaceAdaptiveViewsContainer').addClass('selected');
  1439. showPopup($('#interfaceAdaptiveViewsListContainer'));
  1440. }
  1441. }
  1442. function toggleOverflowMenuPopup() {
  1443. if (($('#overflowMenuContainer').hasClass('popup'))) {
  1444. closePopup();
  1445. } else {
  1446. $('#overflowMenuButton').addClass('selected');
  1447. showPopup($('#overflowMenuContainer'));
  1448. }
  1449. }
  1450. var startSplitX;
  1451. var startSplitWidth;
  1452. function startLeftSplit() {
  1453. startSplitX = window.event.pageX;
  1454. startSplitWidth = lastLeftPanelWidth;
  1455. var $left = $('#lsplitbar');
  1456. $left.addClass('active');
  1457. $('<div class="splitterMask"></div>').insertAfter($left);
  1458. $(document).bind($axure.eventNames.mouseMoveName, doLeftSplitMove).bind($axure.eventNames.mouseUpName, endLeftSplitMove);
  1459. }
  1460. function startRightSplit() {
  1461. startSplitX = window.event.pageX;
  1462. startSplitWidth = lastRightPanelWidth;
  1463. var $left = $('#rsplitbar');
  1464. $left.addClass('active');
  1465. $('<div class="splitterMask"></div>').insertAfter($left);
  1466. $(document).bind($axure.eventNames.mouseMoveName, doRightSplitMove).bind($axure.eventNames.mouseUpName, endRightSplitMove);
  1467. }
  1468. function doLeftSplitMove() {
  1469. var currentX = window.event.pageX;
  1470. var newWidth = Math.min(startSplitWidth + currentX - startSplitX, $(window).width() - $('.rightPanel').width(), $(window).width() - lastRightPanelWidthDefault);
  1471. lastLeftPanelWidth = Math.max(lastLeftPanelWidthDefault, newWidth);
  1472. $('.leftPanel').width(lastLeftPanelWidth != 0 ? lastLeftPanelWidth : lastLeftPanelWidthDefault);
  1473. $('#lsplitbar').css('left', $('.leftPanel').width() - 4);
  1474. $axure.player.updateClippingBoundsWidth();
  1475. $axure.player.refreshViewPort();
  1476. }
  1477. function doRightSplitMove() {
  1478. var currentX = window.event.pageX;
  1479. var newWidth = Math.min(startSplitWidth - currentX + startSplitX, $(window).width() - $('.leftPanel').width(), $(window).width() - lastLeftPanelWidthDefault);
  1480. lastRightPanelWidth = Math.max(lastRightPanelWidthDefault, newWidth);
  1481. $('.rightPanel').width(lastRightPanelWidth != 0 ? lastRightPanelWidth : lastRightPanelWidthDefault);
  1482. $('#rsplitbar').css('left', $(window).width() - $('.rightPanel').width());
  1483. $axure.player.updateClippingBoundsWidth();
  1484. $axure.player.refreshViewPort();
  1485. }
  1486. function endLeftSplitMove() {
  1487. $('div.splitterMask').remove();
  1488. var $left = $('#lsplitbar');
  1489. $left.removeClass('active');
  1490. $(document).unbind($axure.eventNames.mouseMoveName, doLeftSplitMove).unbind($axure.eventNames.mouseUpName, endLeftSplitMove);
  1491. setAdaptiveView()
  1492. }
  1493. function endRightSplitMove() {
  1494. $('div.splitterMask').remove();
  1495. var $left = $('#rsplitbar');
  1496. $left.removeClass('active');
  1497. $(document).unbind($axure.eventNames.mouseMoveName, doRightSplitMove).unbind($axure.eventNames.mouseUpName, endRightSplitMove);
  1498. setAdaptiveView()
  1499. }
  1500. var startMX;
  1501. var startMLeft;
  1502. var startMElement;
  1503. var maxMLeft;
  1504. var getMaxMLeft = function () {
  1505. if ($('.rightPanel.mobileMode').length == 0) return $('.leftPanel.mobileMode').last().position().left + 100;
  1506. return $('.rightPanel.mobileMode').last().position().left + 100;
  1507. }
  1508. function startM(e) {
  1509. // Android touch event does not define pageX directly
  1510. if(window.event.pageX) {
  1511. startMX = window.event.pageX;
  1512. } else {
  1513. startMX = window.event.touches[0].pageX;
  1514. }
  1515. startMElement = window.event.target.id;
  1516. var $m = $('#mHideSidebar');
  1517. startMLeft = Number($m.css('left').replace('px', ''));
  1518. $(document).bind($axure.eventNames.mouseMoveName, doMMove).bind($axure.eventNames.mouseUpName, endMMove);
  1519. // Must stop propagation on iOS; otherwise scroll position of content will be reset to (0, 0)
  1520. // (likely due to position of click being calculated as out of bounds for outerContainer -- iOS is not adding scroll offset to bounds)
  1521. if (IOS) { e.stopPropagation() };
  1522. }
  1523. function doMMove() {
  1524. var $m = $('#mHideSidebar');
  1525. if(window.event.pageX) {
  1526. currentX = window.event.pageX;
  1527. } else {
  1528. currentX = window.event.touches[0].pageX;
  1529. }
  1530. var deltaX = currentX - startMX;
  1531. if (Math.abs(deltaX) > 0 && $('.splitterMask').length == 0) {
  1532. $('<div class="splitterMask"></div>').insertAfter($m);
  1533. }
  1534. var newLeft = startMLeft + deltaX;
  1535. newLeft = Math.min(0, newLeft);
  1536. newLeft = Math.max(-getMaxMLeft(), newLeft);
  1537. $m.css('left', newLeft + 'px');
  1538. }
  1539. function endMMove(e) {
  1540. $('div.splitterMask').remove();
  1541. $(document).unbind($axure.eventNames.mouseMoveName, doMMove).unbind($axure.eventNames.mouseUpName, endMMove);
  1542. e.stopPropagation();
  1543. var $m = $('#mHideSidebar');
  1544. if(window.event.pageX) {
  1545. currentX = window.event.pageX;
  1546. } else {
  1547. currentX = window.event.changedTouches[0].pageX;
  1548. }
  1549. var deltaX = currentX - startMX;
  1550. if (deltaX != 0 || startMElement != 'mHideSidebar') {
  1551. adjustM(currentX < startMX ? 'left' : 'right', true);
  1552. }
  1553. }
  1554. function adjustM(direction, animate) {
  1555. var $m = $('#mHideSidebar');
  1556. var duration = animate ? 100 : 0;
  1557. var newLeft = Number($m.css('left').replace('px', ''));
  1558. if (!$m.is(':visible') || newLeft > -100) {
  1559. $m.animate({ 'left': '-60px' }, duration);
  1560. } else if (newLeft < -getMaxMLeft() + 100) {
  1561. $m.animate({ 'left': (-getMaxMLeft() + 125) + 'px' }, duration);
  1562. } else if (direction == 'left') {
  1563. var handled = false;
  1564. var $panels = $('.rightPanel.mobileMode, .leftPanel.mobileMode');
  1565. $panels.each(function () {
  1566. var panelX = $(this).position().left;
  1567. if (panelX > -newLeft) {
  1568. $m.animate({ 'left': (-panelX + 25) + 'px' }, duration);
  1569. handled = true;
  1570. return false;
  1571. }
  1572. });
  1573. if (!handled) {
  1574. $m.animate({ 'left': (-$panels.last().position().left + 25) + 'px' }, duration);
  1575. }
  1576. } else if (direction == 'right') {
  1577. var handled = false;
  1578. var $panels = $('.rightPanel.mobileMode, .leftPanel.mobileMode');
  1579. $($panels.get().reverse()).each(function () {
  1580. var panelRight = $(this).position().left + $(this).width();
  1581. if (panelRight < -newLeft + $(window).width()) {
  1582. $m.animate({ 'left': (-$(this).position().left + 25) + 'px' }, duration);
  1583. handled = true;
  1584. return false;
  1585. }
  1586. });
  1587. if (!handled) {
  1588. $m.animate({ 'left': '-60px' }, duration);
  1589. }
  1590. }
  1591. }
  1592. function repositionPinsOnScaleChange(data) {
  1593. var $pins = $('#existingPinsOverlay').children();
  1594. for (var i = 0; i < $pins.length; i++) {
  1595. // calculate new position of pin
  1596. const left = parseFloat($($pins[i]).css('left'));
  1597. const top = parseFloat($($pins[i]).css('top'));
  1598. const width = $($pins[i]).width();
  1599. const height = $($pins[i]).height();
  1600. // we should scale center of pin instead of left top corner
  1601. const scaledLeft = ((left + (width / 2)) * data.scaleN / data.prevScaleN) - (width / 2);
  1602. const scaledTop = ((top + (height / 2)) * data.scaleN / data.prevScaleN) - (height / 2);
  1603. $($pins[i]).css('left', scaledLeft + 'px');
  1604. $($pins[i]).css('top', scaledTop + 'px');
  1605. }
  1606. // Distance from left of project content to origin (used for pins positioning when on a centered page in Scale to Fit mode)
  1607. if (typeof data.contentOriginOffset !== "undefined") contentLeftOfOriginOffset = data.contentOriginOffset;
  1608. }
  1609. function messageCenter_message(message, data) {
  1610. if (message == 'expandFrame') expand();
  1611. else if (message == 'getCollapseFrameOnLoad' && $axure.player.settings.startCollapsed && !MOBILE_DEVICE) $axure.messageCenter.postMessage('collapseFrameOnLoad');
  1612. else if (message == 'tripleClick') {
  1613. if ($axure.player.isMobileMode() || MOBILE_DEVICE) expand();
  1614. } else if (message == 'setContentScale') {
  1615. if (data.clipToView) {
  1616. var scaleVal = $('.vpScaleOption').find('.selectedRadioButton').parent().attr('val');
  1617. if (scaleVal == '2' || scaleVal == '0') {
  1618. var scaleN = newScaleN = $('#mainPanel').width() / data.viewportWidth;
  1619. var hScaleN = ($('#mainPanel').height()) / data.viewportHeight;
  1620. if (hScaleN < scaleN) scaleN = newScaleN = hScaleN;
  1621. if(scaleVal == '0') scaleN = Math.min(1, scaleN);
  1622. var scale = 'scale(' + scaleN + ')';
  1623. $('#mainPanelContainer').css({
  1624. 'transform': scale,
  1625. 'transform-origin': ''
  1626. });
  1627. }
  1628. } else {
  1629. if (data.scaleN != 1) {
  1630. var scale = 'scale(' + data.scaleN + ')';
  1631. var width = 100 / data.scaleN + '%';
  1632. var height = Number($('#mainPanelContainer').css('height').replace('px', '')) / data.scaleN + 'px';
  1633. $('#mainPanelContainer').css({
  1634. 'transform': scale,
  1635. 'transform-origin': '0px 0px',
  1636. 'width': width,
  1637. 'height': height
  1638. });
  1639. //$('#clipFrameScroll').css('height' , height + 'px');
  1640. //$('#mainFrame').css('height' , height + 'px');
  1641. $('#clipFrameScroll').height(height);
  1642. $('#mainFrame').height(height);
  1643. }
  1644. }
  1645. repositionPinsOnScaleChange(data);
  1646. repositionClippingBoundsScroll();
  1647. // Fix for edge not redrawing content after scale change
  1648. if ($axure.browser.isEdge) {
  1649. newHeight = window.innerHeight - ((!isMobileMode() && $('#topPanel').is(':visible')) ? $('#topPanel').height() : 0);
  1650. newWidth = $(window).width();
  1651. $('#outerContainer').height(newHeight).width(newWidth);
  1652. $('#mainPanel').height(newHeight);
  1653. $('#clippingBounds').height(newHeight);
  1654. }
  1655. }
  1656. }
  1657. function getInitialUrl() {
  1658. var shortId = getHashStringVar(PAGE_ID_NAME);
  1659. var foundById = [];
  1660. if (shortId.length > 0) {
  1661. getPageUrlsById(shortId, foundById, undefined);
  1662. if (foundById.length == 1) return foundById[0];
  1663. }
  1664. var pageName = getHashStringVar(PAGE_URL_NAME);
  1665. if (pageName.length > 0) return pageName + ".html";
  1666. else {
  1667. if (foundById.length > 0) return foundById[0];
  1668. var url = getFirstPageUrl($axure.document.sitemap.rootNodes);
  1669. return (url ? url : "about:blank");
  1670. }
  1671. }
  1672. var getPageUrlsById = $axure.player.getPageUrlsById = function (packageId, foundById, nodes) {
  1673. if (!nodes) nodes = $axure.document.sitemap.rootNodes;
  1674. for (var i = 0; i < nodes.length; i++) {
  1675. var node = nodes[i];
  1676. if (node.id == packageId) foundById.push(node.url);
  1677. var hasChildren = (node.children && node.children.length > 0);
  1678. if (hasChildren) {
  1679. getPageUrlsById(packageId, foundById, node.children);
  1680. }
  1681. }
  1682. }
  1683. var getPageIdByUrl = $axure.player.getPageIdByUrl = function(url, nodes) {
  1684. if (!nodes) nodes = $axure.document.sitemap.rootNodes;
  1685. for (var i = 0; i < nodes.length; i++) {
  1686. var node = nodes[i];
  1687. if (node.url == url) return node.id;
  1688. else {
  1689. var hasChildren = (node.children && node.children.length > 0);
  1690. if (hasChildren) {
  1691. var id = getPageIdByUrl(url, node.children);
  1692. if (id) return id;
  1693. }
  1694. }
  1695. }
  1696. return null;
  1697. }
  1698. function getFirstPageUrl(nodes) {
  1699. for (var i = 0; i < nodes.length; i++) {
  1700. var node = nodes[i];
  1701. if (node.url) return node.url;
  1702. else {
  1703. var hasChildren = (node.children && node.children.length > 0);
  1704. if (hasChildren) {
  1705. var url = getFirstPageUrl(node.children);
  1706. if (url) return url;
  1707. }
  1708. }
  1709. }
  1710. return null;
  1711. }
  1712. function closePlayer() {
  1713. if ($axure.page.location) window.location.href = $axure.page.location;
  1714. else {
  1715. var pageFile = getInitialUrl();
  1716. var currentLocation = window.location.toString();
  1717. window.location.href = currentLocation.substr(0, currentLocation.lastIndexOf("/") + 1) + pageFile;
  1718. }
  1719. }
  1720. function replaceHash(newHash) {
  1721. var currentLocWithoutHash = window.location.toString().split('#')[0];
  1722. //We use replace so that every hash change doesn't get appended to the history stack.
  1723. //We use replaceState in browsers that support it, else replace the location
  1724. if (typeof window.history.replaceState != 'undefined') {
  1725. try {
  1726. //Chrome 45 (Version 45.0.2454.85 m) started throwing an error here when generated locally (this only happens with sitemap open) which broke all interactions.
  1727. //try catch breaks the url adjusting nicely when the sitemap is open, but all interactions and forward and back buttons work.
  1728. //Uncaught SecurityError: Failed to execute 'replaceState' on 'History': A history state object with URL 'file:///C:/Users/Ian/Documents/Axure/HTML/Untitled/start.html#p=home' cannot be created in a document with origin 'null'.
  1729. window.history.replaceState(null, null, currentLocWithoutHash + newHash);
  1730. } catch (ex) { }
  1731. } else {
  1732. window.location.replace(currentLocWithoutHash + newHash);
  1733. }
  1734. }
  1735. function collapse() {
  1736. if (IOS) {
  1737. $('body').off('touchstart');
  1738. $('body').off('touchend');
  1739. }
  1740. if ($axure.player.isMobileMode()) {
  1741. $('#mHideSidebar').hide();
  1742. $('#nativeAppControlFrame').hide();
  1743. $('#mobileBrowserControlFrame').hide();
  1744. } else {
  1745. $axure.player.deleteVarFromCurrentUrlHash('g');
  1746. $axure.player.setVarInCurrentUrlHash('c', 1);
  1747. if (!MOBILE_DEVICE) $('#maximizePanelContainer').show();
  1748. lastLeftPanelWidth = $('.leftPanel').width();
  1749. lastRightPanelWidth = $('.rightPanel').width();
  1750. $('.leftPanel').hide();
  1751. $('.rightPanel').hide();
  1752. $('#topPanel').hide();
  1753. $('.splitbar').hide();
  1754. $('#mainPanel').width($(window).width());
  1755. $('#clippingBounds').width($(window).width());
  1756. $('#clippingBounds').css('left', '0px');
  1757. $(window).resize();
  1758. $(document).trigger('sidebarCollapse');
  1759. $('#maximizeButton').addClass('rotated');
  1760. }
  1761. }
  1762. // This will return true if prototype is opened from version of app after update with code that sets this value
  1763. // (won't be able to distinguish between browser and outdated app)
  1764. var isShareApp = function () { return /ShareApp/.test(navigator.userAgent); }
  1765. function expand() {
  1766. if ($axure.player.isMobileMode()) {
  1767. $('#mHideSidebar').show();
  1768. $('#mobileControlFrameContainer').show();
  1769. isShareApp() ? $('#nativeAppControlFrame').show() : $('#mobileBrowserControlFrame').show();
  1770. } else {
  1771. $minimizeContainer = $('#interfaceControlFrameMinimizeContainer');
  1772. $minimizeContainer.removeClass('collapseHovered');
  1773. $axure.player.deleteVarFromCurrentUrlHash('c');
  1774. $('#maximizeButton').removeClass('rotated');
  1775. $('#maximizePanelContainer').hide();
  1776. $axure.player.restorePlugins();
  1777. $('#topPanel').show();
  1778. $(window).resize();
  1779. $(document).trigger('sidebarExpanded');
  1780. }
  1781. }
  1782. function mainFrame_onload() {
  1783. if ($axure.page.pageName) document.title = $axure.page.pageName;
  1784. }
  1785. function getQueryString(query) {
  1786. var qstring = self.location.href.split("?");
  1787. if (qstring.length < 2) return "";
  1788. return GetParameter(qstring, query);
  1789. }
  1790. function GetParameter(qstring, query) {
  1791. var prms = qstring[1].split("&");
  1792. var frmelements = new Array();
  1793. var currprmeter, querystr = "";
  1794. for (var i = 0; i < prms.length; i++) {
  1795. currprmeter = prms[i].split("=");
  1796. frmelements[i] = new Array();
  1797. frmelements[i][0] = currprmeter[0];
  1798. frmelements[i][1] = currprmeter[1];
  1799. }
  1800. for (j = 0; j < frmelements.length; j++) {
  1801. if (frmelements[j][0].toLowerCase() == query.toLowerCase()) {
  1802. querystr = frmelements[j][1];
  1803. break;
  1804. }
  1805. }
  1806. return querystr;
  1807. }
  1808. function setHashStringVar(currentHash, varName, varVal) {
  1809. var varWithEqual = varName + '=';
  1810. var poundVarWithEqual = varVal === '' ? '' : '#' + varName + '=' + varVal;
  1811. var ampVarWithEqual = varVal === '' ? '' : '&' + varName + '=' + varVal;
  1812. var hashToSet = '';
  1813. var pageIndex = currentHash.indexOf('#' + varWithEqual);
  1814. if (pageIndex == -1) pageIndex = currentHash.indexOf('&' + varWithEqual);
  1815. if (pageIndex != -1) {
  1816. var newHash = currentHash.substring(0, pageIndex);
  1817. newHash = newHash == '' ? poundVarWithEqual : newHash + ampVarWithEqual;
  1818. var ampIndex = currentHash.indexOf('&', pageIndex + 1);
  1819. if (ampIndex != -1) {
  1820. newHash = newHash == '' ? '#' + currentHash.substring(ampIndex + 1) : newHash + currentHash.substring(ampIndex);
  1821. }
  1822. hashToSet = newHash;
  1823. } else if (currentHash.indexOf('#') != -1) {
  1824. hashToSet = currentHash + ampVarWithEqual;
  1825. } else {
  1826. hashToSet = poundVarWithEqual;
  1827. }
  1828. if (hashToSet != '' || varVal == '') {
  1829. return hashToSet;
  1830. }
  1831. return null;
  1832. }
  1833. $axure.player.setVarInCurrentUrlHash = function(varName, varVal) {
  1834. var newHash = setHashStringVar(window.location.hash, varName, varVal);
  1835. if (newHash != null) {
  1836. replaceHash(newHash);
  1837. }
  1838. }
  1839. function deleteHashStringVar(currentHash, varName) {
  1840. var varWithEqual = varName + '=';
  1841. var pageIndex = currentHash.indexOf('#' + varWithEqual);
  1842. if (pageIndex == -1) pageIndex = currentHash.indexOf('&' + varWithEqual);
  1843. if (pageIndex != -1) {
  1844. var newHash = currentHash.substring(0, pageIndex);
  1845. var ampIndex = currentHash.indexOf('&', pageIndex + 1);
  1846. //IF begin of string....if none blank, ELSE # instead of & and rest
  1847. //IF in string....prefix + if none blank, ELSE &-rest
  1848. if (newHash == '') { //beginning of string
  1849. newHash = ampIndex != -1 ? '#' + currentHash.substring(ampIndex + 1) : '';
  1850. } else { //somewhere in the middle
  1851. newHash = newHash + (ampIndex != -1 ? currentHash.substring(ampIndex) : '');
  1852. }
  1853. return newHash;
  1854. }
  1855. return null;
  1856. }
  1857. $axure.player.deleteVarFromCurrentUrlHash = function(varName) {
  1858. var newHash = deleteHashStringVar(window.location.hash, varName);
  1859. if (newHash != null) {
  1860. replaceHash(newHash);
  1861. }
  1862. }
  1863. function setUpController() {
  1864. //$axure.utils = _axUtils;
  1865. var _page = {};
  1866. $axure.page = _page;
  1867. $axure.utils.makeBindable(_page, ['load']);
  1868. var _player = function () {
  1869. };
  1870. $axure.player = _player;
  1871. //-----------------------------------------
  1872. //Global Var array, getLinkUrl function and setGlobalVar listener are
  1873. //for use in setting global vars in page url string when clicking a
  1874. //page in the sitemap
  1875. //-----------------------------------------
  1876. var _globalVars = {};
  1877. //-----------------------------------------
  1878. //Used by getLinkUrl below to check if local server is running
  1879. //in order to send back the global variables as a query string
  1880. //in the page url
  1881. //-----------------------------------------
  1882. var _shouldSendVarsToServer = function () {
  1883. //If exception occurs (due to page in content frame being from a different domain, etc)
  1884. //then run the check without the url (which will end up checking against sitemap url)
  1885. try {
  1886. var mainFrame = document.getElementById("mainFrame");
  1887. return $axure.shouldSendVarsToServer(mainFrame.contentWindow.location.href);
  1888. } catch (e) {
  1889. return $axure.shouldSendVarsToServer();
  1890. }
  1891. };
  1892. var _getLinkUrl = function (baseUrl) {
  1893. var toAdd = '';
  1894. for (var globalVarName in _globalVars) {
  1895. var val = _globalVars[globalVarName];
  1896. if (val != null) {
  1897. if (toAdd.length > 0) toAdd += '&';
  1898. toAdd += globalVarName + '=' + encodeURIComponent(val);
  1899. }
  1900. }
  1901. return toAdd.length > 0 ? baseUrl + (_shouldSendVarsToServer() ? '?' : '#') + toAdd + "&CSUM=1" : baseUrl;
  1902. };
  1903. $axure.getLinkUrlWithVars = _getLinkUrl;
  1904. $axure.messageCenter.addMessageListener(function (message, data) {
  1905. if (message == 'setGlobalVar') {
  1906. _globalVars[data.globalVarName] = data.globalVarValue;
  1907. }
  1908. });
  1909. $axure.messageCenter.addStateListener('page.data', function (key, value) {
  1910. for (var subKey in value) {
  1911. _page[subKey] = value[subKey];
  1912. }
  1913. $axure.page.triggerEvent('load');
  1914. });
  1915. // ---------------------------------------------
  1916. // Navigates the main frame (setting the currently visible page). If the link is relative,
  1917. // this method should test if it is actually a axure rp page being loaded and properly set
  1918. // up all the controller for the page if it is
  1919. // ---------------------------------------------
  1920. _page.navigate = function (url, includeVariables) {
  1921. var mainFrame = document.getElementById("mainFrame");
  1922. //var mainFrame = window.parent.mainFrame;
  1923. // if this is a relative url...
  1924. var urlToLoad;
  1925. if (url.indexOf(':') < 0 || url[0] == '/') {
  1926. var winHref = window.location.href;
  1927. var page = winHref.substring(0, winHref.lastIndexOf('/') + 1) + url;
  1928. urlToLoad = page;
  1929. } else {
  1930. urlToLoad = url;
  1931. }
  1932. if (!includeVariables) {
  1933. mainFrame.contentWindow.location.href = urlToLoad;
  1934. return;
  1935. }
  1936. var urlWithVars = $axure.getLinkUrlWithVars(urlToLoad);
  1937. var currentData = $axure.messageCenter.getState('page.data');
  1938. var currentUrl = currentData && currentData.location;
  1939. if (currentUrl && currentUrl.indexOf('#') != -1) currentUrl = currentUrl.substring(0, currentUrl.indexOf('#'))
  1940. // this is so we can make sure the current frame reloads if the variables have changed
  1941. // by default, if the location is the same but the hash code is different, the browser will not
  1942. // trigger a reload
  1943. mainFrame.contentWindow.location.href =
  1944. currentUrl && urlToLoad.toLowerCase() != currentUrl.toLowerCase()
  1945. ? urlWithVars
  1946. : 'resources/reload.html#' + encodeURI(urlWithVars);
  1947. };
  1948. var pluginIds = [];
  1949. var plugins = {};
  1950. var currentVisibleHostId = {};
  1951. // ---------------------------------------------
  1952. // Adds a tool box frame from a url to the interface. This is useful for loading plugins
  1953. // settings is an object that supports the following properties:
  1954. // - id : the id of the element for the plugin
  1955. // - context : the context to create the plugin host for
  1956. // - title : the user-visible caption for the plugin
  1957. // ---------------------------------------------
  1958. _player.createPluginHost = function (settings) {
  1959. if (!settings.context || !(settings.context === 'project' || settings.context === 'inspect')) {
  1960. //throw ('unknown context type');
  1961. return false;
  1962. }
  1963. if (settings.id == 'feedbackHost')
  1964. $('#overflowMenuContainer').prepend('<div id="showCommentsOption" class="showOption" style="order: 2"><div class="overflowOptionCheckbox"></div>Show Comments</div>');
  1965. if (!settings.id) throw ('each plugin host needs an id');
  1966. if (typeof PREVIEW_INFO === 'undefined') {
  1967. // Share-Hosted Prototype
  1968. if (settings.id == 'debugHost') { return false; }
  1969. if (settings.id == 'handoffHost') { $('#handoffControlFrameHeaderContainer').show(); }
  1970. } else {
  1971. // Preview Mode
  1972. if (settings.id == 'handoffHost') { return false; }
  1973. }
  1974. pluginIds[pluginIds.length] = settings.id;
  1975. plugins[settings.id] = settings;
  1976. var hostContainerId = settings.context + 'ControlFrameHostContainer';
  1977. hostContainerId = _player.isMobileMode() ? 'mHideSidebar' : 'outerContainer';
  1978. var panelClass = 'rightPanel';
  1979. var host;
  1980. if (settings.context == 'project') {
  1981. panelClass = 'leftPanel';
  1982. if (_player.isMobileMode() && $('#' + hostContainerId).find('#projectOptionsHost').length > 0) {
  1983. host = $('<div id="' + settings.id + '" class="' + panelClass + '"></div>')
  1984. .insertAfter('#projectOptionsHost');
  1985. } else {
  1986. host = $('<div id="' + settings.id + '" class="' + panelClass + '"></div>')
  1987. .prependTo('#' + hostContainerId);
  1988. }
  1989. } else {
  1990. if (!$('#separatorContainer').hasClass('hasLeft')) $('#separatorContainer').addClass('hasLeft');
  1991. host = $('<div id="' + settings.id + '" class="' + panelClass + '"></div>')
  1992. .appendTo('#' + hostContainerId);
  1993. }
  1994. $(('#' + settings.id)).click(function (e) { e.stopPropagation(); });
  1995. var controlContainerId = getControlContainerId(settings.id);
  1996. if (!_player.isMobileMode()) host.hide();
  1997. else _player.updatePlugins();
  1998. // TODO: border radius in ie and edge causes image to be blurry (for now, just remove border-radius)
  1999. var style = (IE || $axure.browser.isEdge) ? '" style="border-radius: 0': '';
  2000. var headerLink = $('<a pluginId="' + settings.id + '" title="' + settings.title + style + '" >' + (settings.context === 'inspect' ? ('<span>' + '</span>'): '&nbsp;') + '</a>');
  2001. headerLink.mousedown($axure.utils.curry(interfaceControlHeaderButton_click, settings.id)).wrap('<li id="' + settings.id + 'Btn"' + (settings.id == "handoffHost" ? ' style="display: none"' : '') + '>');
  2002. headerLink.parent().appendTo('#' + controlContainerId);
  2003. if (_player.isMobileMode()) $axure.player.resizeContent();
  2004. $(document).trigger('pluginCreated', [settings.gid]);
  2005. };
  2006. var getControlContainerId = function (id) {
  2007. return plugins[id].context + 'ControlFrameHeader';
  2008. }
  2009. var getVisiblePlugins = function () {
  2010. var ids = '';
  2011. for (var id in plugins) {
  2012. var context = plugins[id].context;
  2013. if (currentVisibleHostId[context] == id) {
  2014. ids += plugins[id].gid;
  2015. }
  2016. }
  2017. return ids;
  2018. }
  2019. _player.pluginVisibleChanged = function(hostId, visible) {
  2020. if ($axure.player.isCloud && plugins[hostId]) {
  2021. $axure.messageCenter.postMessage('pluginVisibleChanged', { id: hostId, gid: plugins[hostId].gid, visible: visible });
  2022. }
  2023. }
  2024. var interfaceControlHeaderButton_click = function (id) {
  2025. if (_player.isAnimating) { return; }
  2026. $axure.player.closePopup();
  2027. var controlContainerId = getControlContainerId(id);
  2028. var context = plugins[id].context;
  2029. var clickedPlugin = $('#' + controlContainerId + ' a[pluginId=' + id + ']');
  2030. if (currentVisibleHostId[context] == id) {
  2031. clickedPlugin.removeClass('selected');
  2032. if (id == "sitemapHost") { $('#sitemapControlFrameContainer').removeClass('selected'); }
  2033. currentVisibleHostId[context] = -1;
  2034. _player.collapseToBar(context, id);
  2035. $(document).trigger('pluginShown', [getVisiblePlugins()]);
  2036. } else {
  2037. $('#' + controlContainerId + ' a').removeClass('selected');
  2038. clickedPlugin.addClass('selected');
  2039. if (id == "sitemapHost") { $('#sitemapControlFrameContainer').addClass('selected'); }
  2040. $('#' + currentVisibleHostId[context]).hide();
  2041. $axure.player.pluginVisibleChanged(currentVisibleHostId[context], false);
  2042. currentVisibleHostId[context] = id;
  2043. _player.expandFromBar(id, context);
  2044. $(document).trigger('pluginShown', [getVisiblePlugins()]);
  2045. }
  2046. };
  2047. _player.pluginClose = function (id) {
  2048. var controlContainerId = getControlContainerId(id);
  2049. var context = plugins[id].context;
  2050. var clickedPlugin = $('#' + controlContainerId + ' a[pluginId=' + id + ']');
  2051. if (!clickedPlugin.hasClass('selected')) { return; }
  2052. clickedPlugin.removeClass('selected');
  2053. currentVisibleHostId[context] = -1;
  2054. _player.collapseToBar(context, id);
  2055. $(document).trigger('pluginShown', [getVisiblePlugins()]);
  2056. };
  2057. _player.showPlugin = function (gid) {
  2058. for (var id in plugins) {
  2059. if (plugins[id].gid == gid) {
  2060. interfaceControlHeaderButton_click(id);
  2061. break;
  2062. }
  2063. }
  2064. };
  2065. _player.restorePlugins = function () {
  2066. var selectedPluginsCount = 0;
  2067. for (var id in plugins) {
  2068. var clickedPlugin = $('#' + getControlContainerId(id) + ' a[pluginId=' + id + ']');
  2069. if (clickedPlugin.hasClass('selected')) selectedPluginsCount++;
  2070. }
  2071. if ($axure.player.settings.isAxshare && selectedPluginsCount != 0) $('#clippingBoundsScrollContainer').hide();
  2072. var selectedPluginsSeen = 0;
  2073. for (var id in plugins) {
  2074. var controlContainerId = getControlContainerId(id);
  2075. var context = plugins[id].context;
  2076. var clickedPlugin = $('#' + controlContainerId + ' a[pluginId=' + id + ']');
  2077. if (clickedPlugin.hasClass('selected')) {
  2078. //_player.showPlugin(id);
  2079. // TODO: handoffHost would need center inspect icon highlighted and rightFrameIcon set to visible
  2080. //if (id == 'handoffHost') { }
  2081. //$('#' + id).show();
  2082. selectedPluginsSeen++;
  2083. _player.expandFromBar(id, context, selectedPluginsCount == selectedPluginsSeen);
  2084. } else {
  2085. $('#' + id).hide();
  2086. }
  2087. }
  2088. $(document).trigger('pluginShown', [getVisiblePlugins()]);
  2089. };
  2090. }
  2091. $axure.player.hideAllPlayerControllers = function(isVisible) {
  2092. // TOOD: Verify that the containers are set to the right state after re-enabling them
  2093. if(isVisible) {
  2094. $('#topPanel').css('display', '');
  2095. $('#popupContainer').css('display', '');
  2096. $('#maximizePanelContainer').css('display', '');
  2097. $('#mobileControlFrameContainer').css('display', '');
  2098. } else {
  2099. $('#topPanel').hide();
  2100. $('#popupContainer').hide();
  2101. $('#maximizePanelContainer').hide(); // TODO: This needs to have a function where it prevents itself from showing up externally
  2102. $('#mobileControlFrameContainer').hide();
  2103. }
  2104. }
  2105. // TODO: General function to add bezels/overlays if applicable
  2106. $axure.player.addDeviceFraming = function (project, isEdit) {
  2107. // Temporary
  2108. var devices = {
  2109. iPhone8: 0,
  2110. iPhone8Plus: 1,
  2111. iPhoneSE: 2,
  2112. iPhoneX: 3,
  2113. iPad4: 4,
  2114. GalaxyS8: 5,
  2115. Pixel2: 6,
  2116. Pixel2XL: 7,
  2117. Mobile: 8,
  2118. Tablet9: 9,
  2119. Tablet7: 10,
  2120. Custom: 11,
  2121. Web: 12
  2122. };
  2123. // TODO: Need to bring over some platform functionality -> function might not be present
  2124. if (!$axure.player.settings.isExpo || project.Platform.Device === 12) { return; }
  2125. // TODO: Generate html for overlay and bezel containers
  2126. // TODO: Determine if preview player or full prototype player to establish where containers will be stored
  2127. var currDevice = project.Platform.Device;
  2128. var rootPath = '../../Scripts/Expo/StaticContent/resources/images/mobile/';
  2129. var framePath, overlayPath;
  2130. var $overlayParent = $(window.parent.parent.document).find('#previewPlayerDiv');
  2131. $overlayParent = isEdit && $overlayParent.length !== 0 ? $overlayParent : $('#mainPanelContainer');
  2132. $overlayParent.css('overflow', 'visible');
  2133. // TODO: Import enum of Device types -> import via TS definitions. WILL NEED TO REMEMBER THAT WE NEED TO SYNC SERVER AND CLIENT SIDE
  2134. // TODO: Create mapping of required images to device type
  2135. // images will be stored in ../../images/mobile
  2136. // TODO: Manage resizing
  2137. // TODO: Manage pointer clicks
  2138. // TODO: Status bar -> Default or via settings
  2139. // TODO: Establish img paths
  2140. switch (currDevice) {
  2141. case devices.iPhone8:
  2142. case devices.iPhone8Plus:
  2143. framePath = rootPath + 'iphone.svg';
  2144. overlayPath = "";
  2145. break;
  2146. case devices.iPhoneSE:
  2147. break;
  2148. case devices.iPhoneX:
  2149. framePath = "";
  2150. overlayPath = "";
  2151. break;
  2152. case devices.iPad4:
  2153. break;
  2154. case devices.Pixel2:
  2155. break;
  2156. case devices.Pixel2XL:
  2157. break;
  2158. case devices.GalaxyS8:
  2159. break;
  2160. case devices.Mobile:
  2161. case devices.Tablet7:
  2162. case devices.Tablet9:
  2163. case devices.Custom:
  2164. default:
  2165. break;
  2166. }
  2167. // TODO: Append images
  2168. // TODO: Position and initial dimensions
  2169. // TODO: Add resize handlers (?)
  2170. // TODO: Add pointer event handers (?)
  2171. if (framePath != undefined) {
  2172. $overlayParent.prepend(genFrameContainer());
  2173. var $fContainer = $overlayParent.find('#deviceFrameContainer');
  2174. var $frame = $fContainer.find('#deviceFrame');
  2175. $frame.css('background-image', "url('" + framePath + "')");
  2176. $frame.css('height', '');
  2177. $frame.css('width', '');
  2178. $frame.css('top', '');
  2179. $frame.css('left', '');
  2180. if(isEdit) {
  2181. $fContainer.css('z-index', -1);
  2182. }
  2183. }
  2184. if (overlayPath != undefined) {
  2185. // TODO: Update for edit mode
  2186. // $overlayParent.append(genOverlayContainer());
  2187. var $oContainer = $overlayParent.find('#deviceOverlayContainer');
  2188. var $overlay = $oContainer.find('#deviceOverlay');
  2189. $overlay.css('background-image', "url('" + overlayPath + "')");
  2190. }
  2191. }
  2192. function genFrameContainer(bezelPath) {
  2193. var container = [
  2194. '<div id="deviceFrameContainer">',
  2195. ' <div id="deviceFrame">',
  2196. ' </div>',
  2197. '</div>'
  2198. ].join("");
  2199. return container;
  2200. }
  2201. })();