jmpress.js 74 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710
  1. /**
  2. * jmpress.js v0.4.5
  3. * http://jmpressjs.github.com/jmpress.js
  4. *
  5. * A jQuery plugin to build a website on the infinite canvas.
  6. *
  7. * Copyright 2013 Kyle Robinson Young @shama & Tobias Koppers @sokra
  8. * Licensed MIT
  9. * http://www.opensource.org/licenses/mit-license.php
  10. *
  11. * Based on the foundation laid by Bartek Szopka @bartaz
  12. *//*
  13. * core.js
  14. * The core of jmpress.js
  15. */
  16. (function( $, document, window, undefined ) {
  17. 'use strict';
  18. /**
  19. * Set supported prefixes
  20. *
  21. * @access protected
  22. * @return Function to get prefixed property
  23. */
  24. var pfx = (function () {
  25. var style = document.createElement('dummy').style,
  26. prefixes = 'Webkit Moz O ms Khtml'.split(' '),
  27. memory = {};
  28. return function ( prop ) {
  29. if ( typeof memory[ prop ] === "undefined" ) {
  30. var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1),
  31. props = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' ');
  32. memory[ prop ] = null;
  33. for ( var i in props ) {
  34. if ( style[ props[i] ] !== undefined ) {
  35. memory[ prop ] = props[i];
  36. break;
  37. }
  38. }
  39. }
  40. return memory[ prop ];
  41. };
  42. }());
  43. /**
  44. * map ex. "WebkitTransform" to "-webkit-transform"
  45. */
  46. function mapProperty( name ) {
  47. if(!name) {
  48. return;
  49. }
  50. var index = 1 + name.substr(1).search(/[A-Z]/);
  51. var prefix = name.substr(0, index).toLowerCase();
  52. var postfix = name.substr(index).toLowerCase();
  53. return "-" + prefix + "-" + postfix;
  54. }
  55. function addComma( attribute ) {
  56. if(!attribute) {
  57. return "";
  58. }
  59. return attribute + ",";
  60. }
  61. /**
  62. * Return an jquery object only if it's not empty
  63. */
  64. function ifNotEmpty(el) {
  65. if(el.length > 0) {
  66. return el;
  67. }
  68. return null;
  69. }
  70. /**
  71. * Default Settings
  72. */
  73. var defaults = {
  74. /* CLASSES */
  75. stepSelector: '.step'
  76. ,containerClass: ''
  77. ,canvasClass: ''
  78. ,areaClass: ''
  79. ,notSupportedClass: 'not-supported'
  80. /* CONFIG */
  81. ,fullscreen: true
  82. /* ANIMATION */
  83. ,animation: {
  84. transformOrigin: 'top left'
  85. ,transitionProperty: addComma(mapProperty(pfx('transform'))) + addComma(mapProperty(pfx('perspective'))) + 'opacity'
  86. ,transitionDuration: '1s'
  87. ,transitionDelay: '500ms'
  88. ,transitionTimingFunction: 'ease-in-out'
  89. ,transformStyle: "preserve-3d"
  90. }
  91. ,transitionDuration: 1500
  92. };
  93. var callbacks = {
  94. 'beforeChange': 1
  95. ,'beforeInitStep': 1
  96. ,'initStep': 1
  97. ,'beforeInit': 1
  98. ,'afterInit': 1
  99. ,'beforeDeinit': 1
  100. ,'afterDeinit': 1
  101. ,'applyStep': 1
  102. ,'unapplyStep': 1
  103. ,'setInactive': 1
  104. ,'beforeActive': 1
  105. ,'setActive': 1
  106. ,'selectInitialStep': 1
  107. ,'selectPrev': 1
  108. ,'selectNext': 1
  109. ,'selectHome': 1
  110. ,'selectEnd': 1
  111. ,'idle': 1
  112. ,'applyTarget': 1
  113. };
  114. for(var callbackName in callbacks) {
  115. defaults[callbackName] = [];
  116. }
  117. /**
  118. * Initialize jmpress
  119. */
  120. function init( args ) {
  121. args = $.extend(true, {}, args || {});
  122. // accept functions and arrays of functions as callbacks
  123. var callbackArgs = {};
  124. var callbackName = null;
  125. for (callbackName in callbacks) {
  126. callbackArgs[callbackName] = $.isFunction( args[callbackName] ) ?
  127. [ args[callbackName] ] :
  128. args[callbackName];
  129. args[callbackName] = [];
  130. }
  131. // MERGE SETTINGS
  132. var settings = $.extend(true, {}, defaults, args);
  133. for (callbackName in callbacks) {
  134. if (callbackArgs[callbackName]) {
  135. Array.prototype.push.apply(settings[callbackName], callbackArgs[callbackName]);
  136. }
  137. }
  138. /*** MEMBER VARS ***/
  139. var jmpress = $( this )
  140. ,container = null
  141. ,area = null
  142. ,oldStyle = {
  143. container: ""
  144. ,area: ""
  145. }
  146. ,canvas = null
  147. ,current = null
  148. ,active = false
  149. ,activeSubstep = null
  150. ,activeDelegated = false;
  151. /*** MEMBER FUNCTIONS ***/
  152. // functions have to be called with this
  153. /**
  154. * Init a single step
  155. *
  156. * @param element the element of the step
  157. * @param idx number of step
  158. */
  159. function doStepInit( element, idx ) {
  160. var data = dataset( element );
  161. var step = {
  162. oldStyle: $(element).attr("style") || ""
  163. };
  164. var callbackData = {
  165. data: data
  166. ,stepData: step
  167. };
  168. callCallback.call(this, 'beforeInitStep', $(element), callbackData);
  169. step.delegate = data.delegate;
  170. callCallback.call(this, 'initStep', $(element), callbackData);
  171. $(element).data('stepData', step);
  172. if ( !$(element).attr('id') ) {
  173. $(element).attr('id', 'step-' + (idx + 1));
  174. }
  175. callCallback.call(this, 'applyStep', $(element), callbackData);
  176. }
  177. /**
  178. * Deinit a single step
  179. *
  180. * @param element the element of the step
  181. */
  182. function doStepDeinit( element ) {
  183. var stepData = $(element).data('stepData');
  184. $(element).attr("style", stepData.oldStyle);
  185. callCallback.call(this, 'unapplyStep', $(element), {
  186. stepData: stepData
  187. });
  188. }
  189. /**
  190. * Reapplies stepData to the element
  191. *
  192. * @param element
  193. */
  194. function doStepReapply( element ) {
  195. callCallback.call(this, 'unapplyStep', $(element), {
  196. stepData: element.data("stepData")
  197. });
  198. callCallback.call(this, 'applyStep', $(element), {
  199. stepData: element.data("stepData")
  200. });
  201. }
  202. /**
  203. * Completly deinit jmpress
  204. *
  205. */
  206. function deinit() {
  207. if ( active ) {
  208. callCallback.call(this, 'setInactive', active, {
  209. stepData: $(active).data('stepData')
  210. ,reason: "deinit"
  211. } );
  212. }
  213. if (current.jmpressClass) {
  214. $(jmpress).removeClass(current.jmpressClass);
  215. }
  216. callCallback.call(this, 'beforeDeinit', $(this), {});
  217. $(settings.stepSelector, jmpress).each(function( idx ) {
  218. doStepDeinit.call(jmpress, this );
  219. });
  220. container.attr("style", oldStyle.container);
  221. if(settings.fullscreen) {
  222. $("html").attr("style", "");
  223. }
  224. area.attr("style", oldStyle.area);
  225. $(canvas).children().each(function() {
  226. jmpress.append( $( this ) );
  227. });
  228. if( settings.fullscreen ) {
  229. canvas.remove();
  230. } else {
  231. canvas.remove();
  232. area.remove();
  233. }
  234. callCallback.call(this, 'afterDeinit', $(this), {});
  235. $(jmpress).data("jmpressmethods", false);
  236. }
  237. /**
  238. * Call a callback
  239. *
  240. * @param callbackName String callback which should be called
  241. * @param element some arguments to the callback
  242. * @param eventData
  243. */
  244. function callCallback( callbackName, element, eventData ) {
  245. eventData.settings = settings;
  246. eventData.current = current;
  247. eventData.container = container;
  248. eventData.parents = element ? getStepParents(element) : null;
  249. eventData.current = current;
  250. eventData.jmpress = this;
  251. var result = {};
  252. $.each( settings[callbackName], function(idx, callback) {
  253. result.value = callback.call( jmpress, element, eventData ) || result.value;
  254. });
  255. return result.value;
  256. }
  257. /**
  258. *
  259. */
  260. function getStepParents( el ) {
  261. return $(el).parentsUntil(jmpress).not(jmpress).filter(settings.stepSelector);
  262. }
  263. /**
  264. * Reselect the active step
  265. *
  266. * @param String type reason of reselecting step
  267. */
  268. function reselect( type ) {
  269. return select( { step: active, substep: activeSubstep }, type);
  270. }
  271. /**
  272. * Select a given step
  273. *
  274. * @param el element to select
  275. * @param type reason of changing step
  276. * @return Object element selected
  277. */
  278. function select( el, type ) {
  279. var substep;
  280. if ( $.isPlainObject( el ) ) {
  281. substep = el.substep;
  282. el = el.step;
  283. }
  284. if ( typeof el === 'string') {
  285. el = jmpress.find( el ).first();
  286. }
  287. if ( !el || !$(el).data('stepData') ) {
  288. return false;
  289. }
  290. scrollFix.call(this);
  291. var step = $(el).data('stepData');
  292. var cancelSelect = false;
  293. callCallback.call(this, "beforeChange", el, {
  294. stepData: step
  295. ,reason: type
  296. ,cancel: function() {
  297. cancelSelect = true;
  298. }
  299. });
  300. if (cancelSelect) {
  301. return undefined;
  302. }
  303. var target = {};
  304. var delegated = el;
  305. if($(el).data("stepData").delegate) {
  306. delegated = ifNotEmpty($(el).parentsUntil(jmpress).filter(settings.stepSelector).filter(step.delegate)) ||
  307. ifNotEmpty($(el).near(step.delegate)) ||
  308. ifNotEmpty($(el).near(step.delegate, true)) ||
  309. ifNotEmpty($(step.delegate, jmpress));
  310. if(delegated) {
  311. step = delegated.data("stepData");
  312. } else {
  313. // Do not delegate if expression not found
  314. delegated = el;
  315. }
  316. }
  317. if ( activeDelegated ) {
  318. callCallback.call(this, 'setInactive', activeDelegated, {
  319. stepData: $(activeDelegated).data('stepData')
  320. ,delegatedFrom: active
  321. ,reason: type
  322. ,target: target
  323. ,nextStep: delegated
  324. ,nextSubstep: substep
  325. ,nextStepData: step
  326. } );
  327. }
  328. var callbackData = {
  329. stepData: step
  330. ,delegatedFrom: el
  331. ,reason: type
  332. ,target: target
  333. ,substep: substep
  334. ,prevStep: activeDelegated
  335. ,prevSubstep: activeSubstep
  336. ,prevStepData: activeDelegated && $(activeDelegated).data('stepData')
  337. };
  338. callCallback.call(this, 'beforeActive', delegated, callbackData);
  339. callCallback.call(this, 'setActive', delegated, callbackData);
  340. // Set on step class on root element
  341. if (current.jmpressClass) {
  342. $(jmpress).removeClass(current.jmpressClass);
  343. }
  344. $(jmpress).addClass(current.jmpressClass = 'step-' + $(delegated).attr('id') );
  345. if (current.jmpressDelegatedClass) {
  346. $(jmpress).removeClass(current.jmpressDelegatedClass);
  347. }
  348. $(jmpress).addClass(current.jmpressDelegatedClass = 'delegating-step-' + $(el).attr('id') );
  349. callCallback.call(this, "applyTarget", delegated, $.extend({
  350. canvas: canvas
  351. ,area: area
  352. ,beforeActive: activeDelegated
  353. }, callbackData));
  354. active = el;
  355. activeSubstep = callbackData.substep;
  356. activeDelegated = delegated;
  357. if(current.idleTimeout) {
  358. clearTimeout(current.idleTimeout);
  359. }
  360. current.idleTimeout = setTimeout(function() {
  361. callCallback.call(this, 'idle', delegated, callbackData);
  362. }, Math.max(1, settings.transitionDuration - 100));
  363. return delegated;
  364. }
  365. /**
  366. * This should fix ANY kind of buggy scrolling
  367. */
  368. function scrollFix() {
  369. (function fix() {
  370. if ($(container)[0].tagName === "BODY") {
  371. try {
  372. window.scrollTo(0, 0);
  373. } catch(e) {}
  374. }
  375. $(container).scrollTop(0);
  376. $(container).scrollLeft(0);
  377. function check() {
  378. if ($(container).scrollTop() !== 0 ||
  379. $(container).scrollLeft() !== 0) {
  380. fix();
  381. }
  382. }
  383. setTimeout(check, 1);
  384. setTimeout(check, 10);
  385. setTimeout(check, 100);
  386. setTimeout(check, 200);
  387. setTimeout(check, 400);
  388. }());
  389. }
  390. /**
  391. * Alias for select
  392. */
  393. function goTo( el ) {
  394. return select.call(this, el, "jump" );
  395. }
  396. /**
  397. * Goto Next Slide
  398. *
  399. * @return Object newly active slide
  400. */
  401. function next() {
  402. return select.call(this, callCallback.call(this, 'selectNext', active, {
  403. stepData: $(active).data('stepData')
  404. ,substep: activeSubstep
  405. }), "next" );
  406. }
  407. /**
  408. * Goto Previous Slide
  409. *
  410. * @return Object newly active slide
  411. */
  412. function prev() {
  413. return select.call(this, callCallback.call(this, 'selectPrev', active, {
  414. stepData: $(active).data('stepData')
  415. ,substep: activeSubstep
  416. }), "prev" );
  417. }
  418. /**
  419. * Goto First Slide
  420. *
  421. * @return Object newly active slide
  422. */
  423. function home() {
  424. return select.call(this, callCallback.call(this, 'selectHome', active, {
  425. stepData: $(active).data('stepData')
  426. }), "home" );
  427. }
  428. /**
  429. * Goto Last Slide
  430. *
  431. * @return Object newly active slide
  432. */
  433. function end() {
  434. return select.call(this, callCallback.call(this, 'selectEnd', active, {
  435. stepData: $(active).data('stepData')
  436. }), "end" );
  437. }
  438. /**
  439. * Manipulate the canvas
  440. *
  441. * @param props
  442. * @return Object
  443. */
  444. function canvasMod( props ) {
  445. css(canvas, props || {});
  446. return $(canvas);
  447. }
  448. /**
  449. * Return current step
  450. *
  451. * @return Object
  452. */
  453. function getActive() {
  454. return activeDelegated && $(activeDelegated);
  455. }
  456. /**
  457. * fire a callback
  458. *
  459. * @param callbackName
  460. * @param element
  461. * @param eventData
  462. * @return void
  463. */
  464. function fire( callbackName, element, eventData ) {
  465. if( !callbacks[callbackName] ) {
  466. $.error( "callback " + callbackName + " is not registered." );
  467. } else {
  468. return callCallback.call(this, callbackName, element, eventData);
  469. }
  470. }
  471. /**
  472. * PUBLIC METHODS LIST
  473. */
  474. jmpress.data("jmpressmethods", {
  475. select: select
  476. ,reselect: reselect
  477. ,scrollFix: scrollFix
  478. ,goTo: goTo
  479. ,next: next
  480. ,prev: prev
  481. ,home: home
  482. ,end: end
  483. ,canvas: canvasMod
  484. ,container: function() { return container; }
  485. ,settings: function() { return settings; }
  486. ,active: getActive
  487. ,current: function() { return current; }
  488. ,fire: fire
  489. ,init: function(step) {
  490. doStepInit.call(this, $(step), current.nextIdNumber++);
  491. }
  492. ,deinit: function(step) {
  493. if(step) {
  494. doStepDeinit.call(this, $(step));
  495. } else {
  496. deinit.call(this);
  497. }
  498. }
  499. ,reapply: doStepReapply
  500. });
  501. /**
  502. * Check for support
  503. * This will be removed in near future, when support is coming
  504. *
  505. * @access protected
  506. * @return void
  507. */
  508. function checkSupport() {
  509. var ua = navigator.userAgent.toLowerCase();
  510. return (ua.search(/(iphone)|(ipod)|(android)/) === -1) || (ua.search(/(chrome)/) !== -1);
  511. }
  512. // BEGIN INIT
  513. // CHECK FOR SUPPORT
  514. if (checkSupport() === false) {
  515. if (settings.notSupportedClass) {
  516. jmpress.addClass(settings.notSupportedClass);
  517. }
  518. return;
  519. } else {
  520. if (settings.notSupportedClass) {
  521. jmpress.removeClass(settings.notSupportedClass);
  522. }
  523. }
  524. // grabbing all steps
  525. var steps = $(settings.stepSelector, jmpress);
  526. // GERNERAL INIT OF FRAME
  527. container = jmpress;
  528. area = $('<div />');
  529. canvas = $('<div />');
  530. $(jmpress).children().filter(steps).each(function() {
  531. canvas.append( $( this ) );
  532. });
  533. if(settings.fullscreen) {
  534. container = $('body');
  535. $("html").css({
  536. overflow: 'hidden'
  537. });
  538. area = jmpress;
  539. }
  540. oldStyle.area = area.attr("style") || "";
  541. oldStyle.container = container.attr("style") || "";
  542. if(settings.fullscreen) {
  543. container.css({
  544. height: '100%'
  545. });
  546. jmpress.append( canvas );
  547. } else {
  548. container.css({
  549. position: "relative"
  550. });
  551. area.append( canvas );
  552. jmpress.append( area );
  553. }
  554. $(container).addClass(settings.containerClass);
  555. $(area).addClass(settings.areaClass);
  556. $(canvas).addClass(settings.canvasClass);
  557. document.documentElement.style.height = "100%";
  558. container.css({
  559. overflow: 'hidden'
  560. });
  561. var props = {
  562. position: "absolute"
  563. ,transitionDuration: '0s'
  564. };
  565. props = $.extend({}, settings.animation, props);
  566. css(area, props);
  567. css(area, {
  568. top: '50%'
  569. ,left: '50%'
  570. ,perspective: '1000px'
  571. });
  572. css(canvas, props);
  573. current = {};
  574. callCallback.call(this, 'beforeInit', null, {});
  575. // INITIALIZE EACH STEP
  576. steps.each(function( idx ) {
  577. doStepInit.call(jmpress, this, idx );
  578. });
  579. current.nextIdNumber = steps.length;
  580. callCallback.call(this, 'afterInit', null, {});
  581. // START
  582. select.call(this, callCallback.call(this, 'selectInitialStep', "init", {}) );
  583. if (settings.initClass) {
  584. $(steps).removeClass(settings.initClass);
  585. }
  586. }
  587. /**
  588. * Return default settings
  589. *
  590. * @return Object
  591. */
  592. function getDefaults() {
  593. return defaults;
  594. }
  595. /**
  596. * Register a callback or a jmpress function
  597. *
  598. * @access public
  599. * @param name String the name of the callback or function
  600. * @param func Function? the function to be added
  601. */
  602. function register(name, func) {
  603. if( $.isFunction(func) ) {
  604. if( methods[name] ) {
  605. $.error( "function " + name + " is already registered." );
  606. } else {
  607. methods[name] = func;
  608. }
  609. } else {
  610. if( callbacks[name] ) {
  611. $.error( "callback " + name + " is already registered." );
  612. } else {
  613. callbacks[name] = 1;
  614. defaults[name] = [];
  615. }
  616. }
  617. }
  618. /**
  619. * Set CSS on element w/ prefixes
  620. *
  621. * @return Object element which properties were set
  622. *
  623. * TODO: Consider bypassing pfx and blindly set as jQuery
  624. * already checks for support
  625. */
  626. function css( el, props ) {
  627. var key, pkey, cssObj = {};
  628. for ( key in props ) {
  629. if ( props.hasOwnProperty(key) ) {
  630. pkey = pfx(key);
  631. if ( pkey !== null ) {
  632. cssObj[pkey] = props[key];
  633. }
  634. }
  635. }
  636. $(el).css(cssObj);
  637. return el;
  638. }
  639. /**
  640. * Return dataset for element
  641. *
  642. * @param el element
  643. * @return Object
  644. */
  645. function dataset( el ) {
  646. if ( $(el)[0].dataset ) {
  647. return $.extend({}, $(el)[0].dataset);
  648. }
  649. function toCamelcase( str ) {
  650. str = str.split( '-' );
  651. for( var i = 1; i < str.length; i++ ) {
  652. str[i] = str[i].substr(0, 1).toUpperCase() + str[i].substr(1);
  653. }
  654. return str.join( '' );
  655. }
  656. var returnDataset = {};
  657. var attrs = $(el)[0].attributes;
  658. $.each(attrs, function ( idx, attr ) {
  659. if ( attr.nodeName.substr(0, 5) === "data-" ) {
  660. returnDataset[ toCamelcase(attr.nodeName.substr(5)) ] = attr.nodeValue;
  661. }
  662. });
  663. return returnDataset;
  664. }
  665. /**
  666. * Returns true, if jmpress is initialized
  667. *
  668. * @return bool
  669. */
  670. function initialized() {
  671. return !!$(this).data("jmpressmethods");
  672. }
  673. /**
  674. * PUBLIC STATIC METHODS LIST
  675. */
  676. var methods = {
  677. init: init
  678. ,initialized: initialized
  679. ,deinit: function() {}
  680. ,css: css
  681. ,pfx: pfx
  682. ,defaults: getDefaults
  683. ,register: register
  684. ,dataset: dataset
  685. };
  686. /**
  687. * $.jmpress()
  688. */
  689. $.fn.jmpress = function( method ) {
  690. function f() {
  691. var jmpressmethods = $(this).data("jmpressmethods");
  692. if ( jmpressmethods && jmpressmethods[method] ) {
  693. return jmpressmethods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
  694. } else if ( methods[method] ) {
  695. return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
  696. } else if ( callbacks[method] && jmpressmethods ) {
  697. var settings = jmpressmethods.settings();
  698. var func = Array.prototype.slice.call( arguments, 1 )[0];
  699. if ($.isFunction( func )) {
  700. settings[method] = settings[method] || [];
  701. settings[method].push(func);
  702. }
  703. } else if ( typeof method === 'object' || ! method ) {
  704. return init.apply( this, arguments );
  705. } else {
  706. $.error( 'Method ' + method + ' does not exist on jQuery.jmpress' );
  707. }
  708. // to allow chaining
  709. return this;
  710. }
  711. var args = arguments;
  712. var result;
  713. $(this).each(function(idx, element) {
  714. result = f.apply(element, args);
  715. });
  716. return result;
  717. };
  718. $.extend({
  719. jmpress: function( method ) {
  720. if ( methods[method] ) {
  721. return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
  722. } else if ( callbacks[method] ) {
  723. // plugin interface
  724. var func = Array.prototype.slice.call( arguments, 1 )[0];
  725. if ($.isFunction( func )) {
  726. defaults[method].push(func);
  727. } else {
  728. $.error( 'Second parameter should be a function: $.jmpress( callbackName, callbackFunction )' );
  729. }
  730. } else {
  731. $.error( 'Method ' + method + ' does not exist on jQuery.jmpress' );
  732. }
  733. }
  734. });
  735. }(jQuery, document, window));
  736. /*
  737. * near.js
  738. * Find steps near each other
  739. */
  740. (function( $, document, window, undefined ) {
  741. 'use strict';
  742. // add near( selector, backwards = false) to jquery
  743. function checkAndGo( elements, func, selector, backwards ) {
  744. var next;
  745. elements.each(function(idx, element) {
  746. if(backwards) {
  747. next = func(element, selector, backwards);
  748. if (next) {
  749. return false;
  750. }
  751. }
  752. if( $(element).is(selector) ) {
  753. next = element;
  754. return false;
  755. }
  756. if(!backwards) {
  757. next = func(element, selector, backwards);
  758. if (next) {
  759. return false;
  760. }
  761. }
  762. });
  763. return next;
  764. }
  765. function findNextInChildren(item, selector, backwards) {
  766. var children = $(item).children();
  767. if(backwards) {
  768. children = $(children.get().reverse());
  769. }
  770. return checkAndGo( children, findNextInChildren, selector, backwards );
  771. }
  772. function findNextInSiblings(item, selector, backwards) {
  773. return checkAndGo(
  774. $(item)[backwards ? "prevAll" : "nextAll"](),
  775. findNextInChildren, selector, backwards );
  776. }
  777. function findNextInParents(item, selector, backwards) {
  778. var next;
  779. var parents = $(item).parents();
  780. parents = $(parents.get());
  781. $.each(parents.get(), function(idx, element) {
  782. if( backwards && $(element).is(selector) ) {
  783. next = element;
  784. return false;
  785. }
  786. next = findNextInSiblings(element, selector, backwards);
  787. if(next) {
  788. return false;
  789. }
  790. });
  791. return next;
  792. }
  793. $.fn.near = function( selector, backwards ) {
  794. var array = [];
  795. $(this).each(function(idx, element) {
  796. var near = (backwards ?
  797. false :
  798. findNextInChildren( element, selector, backwards )) ||
  799. findNextInSiblings( element, selector, backwards ) ||
  800. findNextInParents( element, selector, backwards );
  801. if( near ) {
  802. array.push(near);
  803. }
  804. });
  805. return $(array);
  806. };
  807. }(jQuery, document, window));
  808. /*
  809. * transform.js
  810. * The engine that powers the transforms or falls back to other methods
  811. */
  812. (function( $, document, window, undefined ) {
  813. 'use strict';
  814. /* FUNCTIONS */
  815. function toCssNumber(number) {
  816. return (Math.round(10000*number)/10000)+"";
  817. }
  818. /**
  819. * 3D and 2D engines
  820. */
  821. var engines = {
  822. 3: {
  823. transform: function( el, data ) {
  824. var transform = 'translate(-50%,-50%)';
  825. $.each(data, function(idx, item) {
  826. var coord = ["X", "Y", "Z"];
  827. var i;
  828. if(item[0] === "translate") { // ["translate", x, y, z]
  829. transform += " translate3d(" + toCssNumber(item[1] || 0) + "px," + toCssNumber(item[2] || 0) + "px," + toCssNumber(item[3] || 0) + "px)";
  830. } else if(item[0] === "rotate") {
  831. var order = item[4] ? [1, 2, 3] : [3, 2, 1];
  832. for(i = 0; i < 3; i++) {
  833. transform += " rotate" + coord[order[i]-1] + "(" + toCssNumber(item[order[i]] || 0) + "deg)";
  834. }
  835. } else if(item[0] === "scale") {
  836. for(i = 0; i < 3; i++) {
  837. transform += " scale" + coord[i] + "(" + toCssNumber(item[i+1] || 1) + ")";
  838. }
  839. }
  840. });
  841. $.jmpress("css", el, $.extend({}, { transform: transform }));
  842. }
  843. }
  844. ,2: {
  845. transform: function( el, data ) {
  846. var transform = 'translate(-50%,-50%)';
  847. $.each(data, function(idx, item) {
  848. var coord = ["X", "Y"];
  849. if(item[0] === "translate") { // ["translate", x, y, z]
  850. transform += " translate(" + toCssNumber(item[1] || 0) + "px," + toCssNumber(item[2] || 0) + "px)";
  851. } else if(item[0] === "rotate") {
  852. transform += " rotate(" + toCssNumber(item[3] || 0) + "deg)";
  853. } else if(item[0] === "scale") {
  854. for(var i = 0; i < 2; i++) {
  855. transform += " scale" + coord[i] + "(" + toCssNumber(item[i+1] || 1) + ")";
  856. }
  857. }
  858. });
  859. $.jmpress("css", el, $.extend({}, { transform: transform }));
  860. }
  861. }
  862. ,1: {
  863. // CHECK IF SUPPORT IS REALLY NEEDED?
  864. // this not even work without scaling...
  865. // it may better to display the normal view
  866. transform: function( el, data ) {
  867. var anitarget = { top: 0, left: 0 };
  868. $.each(data, function(idx, item) {
  869. var coord = ["X", "Y"];
  870. if(item[0] === "translate") { // ["translate", x, y, z]
  871. anitarget.left = Math.round(item[1] || 0) + "px";
  872. anitarget.top = Math.round(item[2] || 0) + "px";
  873. }
  874. });
  875. el.animate(anitarget, 1000); // TODO: Use animation duration
  876. }
  877. }
  878. };
  879. /**
  880. * Engine to power cross-browser translate, scale and rotate.
  881. */
  882. var engine = (function() {
  883. if ($.jmpress("pfx", "perspective")) {
  884. return engines[3];
  885. } else if ($.jmpress("pfx", "transform")) {
  886. return engines[2];
  887. } else {
  888. // CHECK IF SUPPORT IS REALLY NEEDED?
  889. return engines[1];
  890. }
  891. }());
  892. $.jmpress("defaults").reasonableAnimation = {};
  893. $.jmpress("initStep", function( step, eventData ) {
  894. var data = eventData.data;
  895. var stepData = eventData.stepData;
  896. var pf = parseFloat;
  897. $.extend(stepData, {
  898. x: pf(data.x) || 0
  899. ,y: pf(data.y) || 0
  900. ,z: pf(data.z) || 0
  901. ,r: pf(data.r) || 0
  902. ,phi: pf(data.phi) || 0
  903. ,rotate: pf(data.rotate) || 0
  904. ,rotateX: pf(data.rotateX) || 0
  905. ,rotateY: pf(data.rotateY) || 0
  906. ,rotateZ: pf(data.rotateZ) || 0
  907. ,revertRotate: false
  908. ,scale: pf(data.scale) || 1
  909. ,scaleX: pf(data.scaleX) || false
  910. ,scaleY: pf(data.scaleY) || false
  911. ,scaleZ: pf(data.scaleZ) || 1
  912. });
  913. });
  914. $.jmpress("afterInit", function( nil, eventData ) {
  915. var stepSelector = eventData.settings.stepSelector,
  916. current = eventData.current;
  917. current.perspectiveScale = 1;
  918. current.maxNestedDepth = 0;
  919. var nestedSteps = $(eventData.jmpress).find(stepSelector).children(stepSelector);
  920. while(nestedSteps.length) {
  921. current.maxNestedDepth++;
  922. nestedSteps = nestedSteps.children(stepSelector);
  923. }
  924. });
  925. $.jmpress("applyStep", function( step, eventData ) {
  926. $.jmpress("css", $(step), {
  927. position: "absolute"
  928. ,transformStyle: "preserve-3d"
  929. });
  930. if ( eventData.parents.length > 0 ) {
  931. $.jmpress("css", $(step), {
  932. top: "50%"
  933. ,left: "50%"
  934. });
  935. }
  936. var sd = eventData.stepData;
  937. var transform = [
  938. ["translate",
  939. sd.x || (sd.r * Math.sin(sd.phi*Math.PI/180)),
  940. sd.y || (-sd.r * Math.cos(sd.phi*Math.PI/180)),
  941. sd.z],
  942. ["rotate",
  943. sd.rotateX,
  944. sd.rotateY,
  945. sd.rotateZ || sd.rotate,
  946. true],
  947. ["scale",
  948. sd.scaleX || sd.scale,
  949. sd.scaleY || sd.scale,
  950. sd.scaleZ || sd.scale]
  951. ];
  952. engine.transform( step, transform );
  953. });
  954. $.jmpress("setActive", function( element, eventData ) {
  955. var target = eventData.target;
  956. var step = eventData.stepData;
  957. var tf = target.transform = [];
  958. target.perspectiveScale = 1;
  959. for(var i = eventData.current.maxNestedDepth; i > (eventData.parents.length || 0); i--) {
  960. tf.push(["scale"], ["rotate"], ["translate"]);
  961. }
  962. tf.push(["scale",
  963. 1 / (step.scaleX || step.scale),
  964. 1 / (step.scaleY || step.scale),
  965. 1 / (step.scaleZ)]);
  966. tf.push(["rotate",
  967. -step.rotateX,
  968. -step.rotateY,
  969. -(step.rotateZ || step.rotate)]);
  970. tf.push(["translate",
  971. -(step.x || (step.r * Math.sin(step.phi*Math.PI/180))),
  972. -(step.y || (-step.r * Math.cos(step.phi*Math.PI/180))),
  973. -step.z]);
  974. target.perspectiveScale *= (step.scaleX || step.scale);
  975. $.each(eventData.parents, function(idx, element) {
  976. var step = $(element).data("stepData");
  977. tf.push(["scale",
  978. 1 / (step.scaleX || step.scale),
  979. 1 / (step.scaleY || step.scale),
  980. 1 / (step.scaleZ)]);
  981. tf.push(["rotate",
  982. -step.rotateX,
  983. -step.rotateY,
  984. -(step.rotateZ || step.rotate)]);
  985. tf.push(["translate",
  986. -(step.x || (step.r * Math.sin(step.phi*Math.PI/180))),
  987. -(step.y || (-step.r * Math.cos(step.phi*Math.PI/180))),
  988. -step.z]);
  989. target.perspectiveScale *= (step.scaleX || step.scale);
  990. });
  991. $.each(tf, function(idx, item) {
  992. if(item[0] !== "rotate") {
  993. return;
  994. }
  995. function lowRotate(name) {
  996. if(eventData.current["rotate"+name+"-"+idx] === undefined) {
  997. eventData.current["rotate"+name+"-"+idx] = item[name] || 0;
  998. }
  999. var cur = eventData.current["rotate"+name+"-"+idx], tar = item[name] || 0,
  1000. curmod = cur % 360, tarmod = tar % 360;
  1001. if(curmod < 0) {
  1002. curmod += 360;
  1003. }
  1004. if(tarmod < 0) {
  1005. tarmod += 360;
  1006. }
  1007. var diff = tarmod - curmod;
  1008. if(diff < -180) {
  1009. diff += 360;
  1010. } else if(diff > 180) {
  1011. diff -= 360;
  1012. }
  1013. eventData.current["rotate"+name+"-"+idx] = item[name] = cur + diff;
  1014. }
  1015. lowRotate(1);
  1016. lowRotate(2);
  1017. lowRotate(3);
  1018. });
  1019. });
  1020. $.jmpress("applyTarget", function( active, eventData ) {
  1021. var target = eventData.target,
  1022. props, step = eventData.stepData,
  1023. settings = eventData.settings,
  1024. zoomin = target.perspectiveScale * 1.3 < eventData.current.perspectiveScale,
  1025. zoomout = target.perspectiveScale > eventData.current.perspectiveScale * 1.3;
  1026. // extract first scale from transform
  1027. var lastScale = -1;
  1028. $.each(target.transform, function(idx, item) {
  1029. if(item.length <= 1) {
  1030. return;
  1031. }
  1032. if(item[0] === "rotate" &&
  1033. item[1] % 360 === 0 &&
  1034. item[2] % 360 === 0 &&
  1035. item[3] % 360 === 0) {
  1036. return;
  1037. }
  1038. if(item[0] === "scale") {
  1039. lastScale = idx;
  1040. } else {
  1041. return false;
  1042. }
  1043. });
  1044. if(lastScale !== eventData.current.oldLastScale) {
  1045. zoomin = zoomout = false;
  1046. eventData.current.oldLastScale = lastScale;
  1047. }
  1048. var extracted = [];
  1049. if(lastScale !== -1) {
  1050. while(lastScale >= 0) {
  1051. if(target.transform[lastScale][0] === "scale") {
  1052. extracted.push(target.transform[lastScale]);
  1053. target.transform[lastScale] = ["scale"];
  1054. }
  1055. lastScale--;
  1056. }
  1057. }
  1058. var animation = settings.animation;
  1059. if(settings.reasonableAnimation[eventData.reason]) {
  1060. animation = $.extend({},
  1061. animation,
  1062. settings.reasonableAnimation[eventData.reason]);
  1063. }
  1064. props = {
  1065. // to keep the perspective look similar for different scales
  1066. // we need to 'scale' the perspective, too
  1067. perspective: Math.round(target.perspectiveScale * 1000) + "px"
  1068. };
  1069. props = $.extend({}, animation, props);
  1070. if (!zoomin) {
  1071. props.transitionDelay = '0s';
  1072. }
  1073. if (!eventData.beforeActive) {
  1074. props.transitionDuration = '0s';
  1075. props.transitionDelay = '0s';
  1076. }
  1077. $.jmpress("css", eventData.area, props);
  1078. engine.transform(eventData.area, extracted);
  1079. props = $.extend({}, animation);
  1080. if (!zoomout) {
  1081. props.transitionDelay = '0s';
  1082. }
  1083. if (!eventData.beforeActive) {
  1084. props.transitionDuration = '0s';
  1085. props.transitionDelay = '0s';
  1086. }
  1087. eventData.current.perspectiveScale = target.perspectiveScale;
  1088. $.jmpress("css", eventData.canvas, props);
  1089. engine.transform(eventData.canvas, target.transform);
  1090. });
  1091. }(jQuery, document, window));
  1092. /*
  1093. * active.js
  1094. * Set the active classes on steps
  1095. */
  1096. (function( $, document, window, undefined ) {
  1097. 'use strict';
  1098. var $jmpress = $.jmpress;
  1099. /* DEFINES */
  1100. var activeClass = 'activeClass',
  1101. nestedActiveClass = 'nestedActiveClass';
  1102. /* DEFAULTS */
  1103. var defaults = $jmpress( 'defaults' );
  1104. defaults[nestedActiveClass] = "nested-active";
  1105. defaults[activeClass] = "active";
  1106. /* HOOKS */
  1107. $jmpress( 'setInactive', function( step, eventData ) {
  1108. var settings = eventData.settings,
  1109. activeClassSetting = settings[activeClass],
  1110. nestedActiveClassSettings = settings[nestedActiveClass];
  1111. if(activeClassSetting) {
  1112. $(step).removeClass( activeClassSetting );
  1113. }
  1114. if(nestedActiveClassSettings) {
  1115. $.each(eventData.parents, function(idx, element) {
  1116. $(element).removeClass(nestedActiveClassSettings);
  1117. });
  1118. }
  1119. });
  1120. $jmpress( 'setActive', function( step, eventData ) {
  1121. var settings = eventData.settings,
  1122. activeClassSetting = settings[activeClass],
  1123. nestedActiveClassSettings = settings[nestedActiveClass];
  1124. if(activeClassSetting) {
  1125. $(step).addClass( activeClassSetting );
  1126. }
  1127. if(nestedActiveClassSettings) {
  1128. $.each(eventData.parents, function(idx, element) {
  1129. $(element).addClass(nestedActiveClassSettings);
  1130. });
  1131. }
  1132. });
  1133. }(jQuery, document, window));
  1134. /*
  1135. * circular.js
  1136. * Repeat from start after end
  1137. */
  1138. (function( $, document, window, undefined ) {
  1139. 'use strict';
  1140. var $jmpress = $.jmpress;
  1141. /* FUNCTIONS */
  1142. function firstSlide( step, eventData ) {
  1143. return $(this).find(eventData.settings.stepSelector).first();
  1144. }
  1145. function prevOrNext( jmpress, step, eventData, prev) {
  1146. if (!step) {
  1147. return false;
  1148. }
  1149. var stepSelector = eventData.settings.stepSelector;
  1150. step = $(step);
  1151. do {
  1152. var item = step.near( stepSelector, prev );
  1153. if (item.length === 0 || item.closest(jmpress).length === 0) {
  1154. item = $(jmpress).find(stepSelector)[prev?"last":"first"]();
  1155. }
  1156. if (!item.length) {
  1157. return false;
  1158. }
  1159. step = item;
  1160. } while( step.data("stepData").exclude );
  1161. return step;
  1162. }
  1163. /* HOOKS */
  1164. $jmpress( 'initStep', function( step, eventData ) {
  1165. eventData.stepData.exclude = eventData.data.exclude && ["false", "no"].indexOf(eventData.data.exclude) === -1;
  1166. });
  1167. $jmpress( 'selectInitialStep', firstSlide);
  1168. $jmpress( 'selectHome', firstSlide);
  1169. $jmpress( 'selectEnd', function( step, eventData ) {
  1170. return $(this).find(eventData.settings.stepSelector).last();
  1171. });
  1172. $jmpress( 'selectPrev', function( step, eventData ) {
  1173. return prevOrNext(this, step, eventData, true);
  1174. });
  1175. $jmpress( 'selectNext', function( step, eventData ) {
  1176. return prevOrNext(this, step, eventData);
  1177. });
  1178. }(jQuery, document, window));
  1179. /*
  1180. * start.js
  1181. * Set the first step to start on
  1182. */
  1183. (function( $, document, window, undefined ) {
  1184. 'use strict';
  1185. /* HOOKS */
  1186. $.jmpress( 'selectInitialStep', function( nil, eventData ) {
  1187. return eventData.settings.start;
  1188. });
  1189. }(jQuery, document, window));
  1190. /*
  1191. * ways.js
  1192. * Control the flow of the steps
  1193. */
  1194. (function( $, document, window, undefined ) {
  1195. 'use strict';
  1196. var $jmpress = $.jmpress;
  1197. /* FUNCTIONS */
  1198. function routeFunc( jmpress, route, type ) {
  1199. for(var i = 0; i < route.length - 1; i++) {
  1200. var from = route[i];
  1201. var to = route[i+1];
  1202. if($(jmpress).jmpress("initialized")) {
  1203. $(from, jmpress).data("stepData")[type] = to;
  1204. } else {
  1205. $(from, jmpress).attr('data-' + type, to);
  1206. }
  1207. }
  1208. }
  1209. function selectPrevOrNext( step, eventData, attr, prev ) {
  1210. var stepData = eventData.stepData;
  1211. if(stepData[attr]) {
  1212. var near = $(step).near(stepData[attr], prev);
  1213. if(near && near.length) {
  1214. return near;
  1215. }
  1216. near = $(stepData[attr], this)[prev?"last":"first"]();
  1217. if(near && near.length) {
  1218. return near;
  1219. }
  1220. }
  1221. }
  1222. /* EXPORTED FUNCTIONS */
  1223. $jmpress( 'register', 'route', function( route, unidirectional, reversedRoute ) {
  1224. if( typeof route === "string" ) {
  1225. route = [route, route];
  1226. }
  1227. routeFunc(this, route, reversedRoute ? "prev" : "next");
  1228. if (!unidirectional) {
  1229. routeFunc(this, route.reverse(), reversedRoute ? "next" : "prev");
  1230. }
  1231. });
  1232. /* HOOKS */
  1233. $jmpress( 'initStep', function( step, eventData ) {
  1234. for(var attr in {next:1,prev:1}) {
  1235. eventData.stepData[attr] = eventData.data[attr];
  1236. }
  1237. });
  1238. $jmpress( 'selectNext', function( step, eventData ) {
  1239. return selectPrevOrNext.call(this, step, eventData, "next");
  1240. });
  1241. $jmpress( 'selectPrev', function( step, eventData ) {
  1242. return selectPrevOrNext.call(this, step, eventData, "prev", true);
  1243. });
  1244. }(jQuery, document, window));
  1245. /*
  1246. * ajax.js
  1247. * Load steps via ajax
  1248. */
  1249. (function( $, document, window, undefined ) {
  1250. 'use strict';
  1251. var $jmpress = $.jmpress;
  1252. /* DEFINES */
  1253. var afterStepLoaded = 'ajax:afterStepLoaded',
  1254. loadStep = 'ajax:loadStep';
  1255. /* REGISTER EVENTS */
  1256. $jmpress('register', loadStep);
  1257. $jmpress('register', afterStepLoaded);
  1258. /* DEFAULTS */
  1259. $jmpress('defaults').ajaxLoadedClass = "loaded";
  1260. /* HOOKS */
  1261. $jmpress('initStep', function( step, eventData ) {
  1262. eventData.stepData.src = $(step).attr('href') || eventData.data.src || false;
  1263. eventData.stepData.srcLoaded = false;
  1264. });
  1265. $jmpress(loadStep, function( step, eventData ) {
  1266. var stepData = eventData.stepData,
  1267. href = stepData && stepData.src,
  1268. settings = eventData.settings;
  1269. if ( href ) {
  1270. $(step).addClass( settings.ajaxLoadedClass );
  1271. stepData.srcLoaded = true;
  1272. $(step).load(href, function(response, status, xhr) {
  1273. $(eventData.jmpress).jmpress('fire', afterStepLoaded, step, $.extend({}, eventData, {
  1274. response: response
  1275. ,status: status
  1276. ,xhr: xhr
  1277. }));
  1278. });
  1279. }
  1280. });
  1281. $jmpress('idle', function( step, eventData ) {
  1282. if (!step) {
  1283. return;
  1284. }
  1285. var settings = eventData.settings,
  1286. jmpress = $(this),
  1287. stepData = eventData.stepData;
  1288. var siblings = $(step)
  1289. .add( $(step).near( settings.stepSelector ) )
  1290. .add( $(step).near( settings.stepSelector, true) )
  1291. .add( jmpress.jmpress('fire', 'selectPrev', step, {
  1292. stepData: $(step).data('stepData')
  1293. }))
  1294. .add( jmpress.jmpress('fire', 'selectNext', step, {
  1295. stepData: $(step).data('stepData')
  1296. }));
  1297. siblings.each(function() {
  1298. var step = this,
  1299. stepData = $(step).data("stepData");
  1300. if(!stepData.src || stepData.srcLoaded) {
  1301. return;
  1302. }
  1303. jmpress.jmpress('fire', loadStep, step, {
  1304. stepData: $(step).data('stepData')
  1305. });
  1306. });
  1307. });
  1308. $jmpress("setActive", function(step, eventData) {
  1309. var stepData = $(step).data("stepData");
  1310. if(!stepData.src || stepData.srcLoaded) {
  1311. return;
  1312. }
  1313. $(this).jmpress('fire', loadStep, step, {
  1314. stepData: $(step).data('stepData')
  1315. });
  1316. });
  1317. }(jQuery, document, window));
  1318. /*
  1319. * hash.js
  1320. * Detect and set the URL hash
  1321. */
  1322. (function( $, document, window, undefined ) {
  1323. 'use strict';
  1324. var $jmpress = $.jmpress,
  1325. hashLink = "a[href^='#']";
  1326. /* FUNCTIONS */
  1327. function randomString() {
  1328. return "" + Math.round(Math.random() * 100000, 0);
  1329. }
  1330. /**
  1331. * getElementFromUrl
  1332. *
  1333. * @return String or undefined
  1334. */
  1335. function getElementFromUrl(settings) {
  1336. // get id from url # by removing `#` or `#/` from the beginning,
  1337. // so both "fallback" `#slide-id` and "enhanced" `#/slide-id` will work
  1338. // TODO SECURITY check user input to be valid!
  1339. try {
  1340. var el = $( '#' + window.location.hash.replace(/^#\/?/,"") );
  1341. return el.length > 0 && el.is(settings.stepSelector) ? el : undefined;
  1342. } catch(e) {}
  1343. }
  1344. function setHash(stepid) {
  1345. var shouldBeHash = "#/" + stepid;
  1346. if(window.history && window.history.pushState) {
  1347. // shouldBeHash = "#" + stepid;
  1348. // consider this for future versions
  1349. // it has currently issues, when startup with a link with hash (webkit)
  1350. if(window.location.hash !== shouldBeHash) {
  1351. window.history.pushState({}, '', shouldBeHash);
  1352. }
  1353. } else {
  1354. if(window.location.hash !== shouldBeHash) {
  1355. window.location.hash = shouldBeHash;
  1356. }
  1357. }
  1358. }
  1359. /* DEFAULTS */
  1360. $jmpress('defaults').hash = {
  1361. use: true
  1362. ,update: true
  1363. ,bindChange: true
  1364. // NOTICE: {use: true, update: false, bindChange: true}
  1365. // will cause a error after clicking on a link to the current step
  1366. };
  1367. /* HOOKS */
  1368. $jmpress('selectInitialStep', function( step, eventData ) {
  1369. var settings = eventData.settings,
  1370. hashSettings = settings.hash,
  1371. current = eventData.current,
  1372. jmpress = $(this);
  1373. eventData.current.hashNamespace = ".jmpress-"+randomString();
  1374. // HASH CHANGE EVENT
  1375. if ( hashSettings.use ) {
  1376. if ( hashSettings.bindChange ) {
  1377. $(window).bind('hashchange'+current.hashNamespace, function(event) {
  1378. var urlItem = getElementFromUrl(settings);
  1379. if ( jmpress.jmpress('initialized') ) {
  1380. jmpress.jmpress("scrollFix");
  1381. }
  1382. if(urlItem && urlItem.length) {
  1383. if(urlItem.attr("id") !== jmpress.jmpress("active").attr("id")) {
  1384. jmpress.jmpress('select', urlItem);
  1385. }
  1386. setHash(urlItem.attr("id"));
  1387. }
  1388. event.preventDefault();
  1389. });
  1390. $(hashLink).on("click"+current.hashNamespace, function(event) {
  1391. var href = $(this).attr("href");
  1392. try {
  1393. if($(href).is(settings.stepSelector)) {
  1394. jmpress.jmpress("select", href);
  1395. event.preventDefault();
  1396. event.stopPropagation();
  1397. }
  1398. } catch(e) {}
  1399. });
  1400. }
  1401. return getElementFromUrl(settings);
  1402. }
  1403. });
  1404. $jmpress('afterDeinit', function( nil, eventData ) {
  1405. $(hashLink).off(eventData.current.hashNamespace);
  1406. $(window).unbind(eventData.current.hashNamespace);
  1407. });
  1408. $jmpress('setActive', function( step, eventData ) {
  1409. var settings = eventData.settings,
  1410. current = eventData.current;
  1411. // `#/step-id` is used instead of `#step-id` to prevent default browser
  1412. // scrolling to element in hash
  1413. if ( settings.hash.use && settings.hash.update ) {
  1414. clearTimeout(current.hashtimeout);
  1415. current.hashtimeout = setTimeout(function() {
  1416. setHash($(eventData.delegatedFrom).attr('id'));
  1417. }, settings.transitionDuration + 200);
  1418. }
  1419. });
  1420. }(jQuery, document, window));
  1421. /*
  1422. * keyboard.js
  1423. * Keyboard event mapping and default keyboard actions
  1424. */
  1425. (function( $, document, window, undefined ) {
  1426. 'use strict';
  1427. var $jmpress = $.jmpress,
  1428. jmpressNext = "next",
  1429. jmpressPrev = "prev";
  1430. /* FUNCTIONS */
  1431. function randomString() {
  1432. return "" + Math.round(Math.random() * 100000, 0);
  1433. }
  1434. function stopEvent(event) {
  1435. event.preventDefault();
  1436. event.stopPropagation();
  1437. }
  1438. /* DEFAULTS */
  1439. $jmpress('defaults').keyboard = {
  1440. use: true
  1441. ,keys: {
  1442. 33: jmpressPrev // pg up
  1443. ,37: jmpressPrev // left
  1444. ,38: jmpressPrev // up
  1445. ,9: jmpressNext+":"+jmpressPrev // tab
  1446. ,32: jmpressNext // space
  1447. ,34: jmpressNext // pg down
  1448. ,39: jmpressNext // right
  1449. ,40: jmpressNext // down
  1450. ,36: "home" // home
  1451. ,35: "end" // end
  1452. }
  1453. ,ignore: {
  1454. "INPUT": [
  1455. 32 // space
  1456. ,37 // left
  1457. ,38 // up
  1458. ,39 // right
  1459. ,40 // down
  1460. ]
  1461. ,"TEXTAREA": [
  1462. 32 // space
  1463. ,37 // left
  1464. ,38 // up
  1465. ,39 // right
  1466. ,40 // down
  1467. ]
  1468. ,"SELECT": [
  1469. 38 // up
  1470. ,40 // down
  1471. ]
  1472. }
  1473. ,tabSelector: "a[href]:visible, :input:visible"
  1474. };
  1475. /* HOOKS */
  1476. $jmpress('afterInit', function( nil, eventData ) {
  1477. var settings = eventData.settings,
  1478. keyboardSettings = settings.keyboard,
  1479. ignoreKeyboardSettings = keyboardSettings.ignore,
  1480. current = eventData.current,
  1481. jmpress = $(this);
  1482. // tabindex make it focusable so that it can receive key events
  1483. if(!settings.fullscreen) {
  1484. jmpress.attr("tabindex", 0);
  1485. }
  1486. current.keyboardNamespace = ".jmpress-"+randomString();
  1487. // KEYPRESS EVENT: this fixes a Opera bug
  1488. $(settings.fullscreen ? document : jmpress)
  1489. .bind("keypress"+current.keyboardNamespace, function( event ) {
  1490. for( var nodeName in ignoreKeyboardSettings ) {
  1491. if ( event.target.nodeName === nodeName && ignoreKeyboardSettings[nodeName].indexOf(event.which) !== -1 ) {
  1492. return;
  1493. }
  1494. }
  1495. if(event.which >= 37 && event.which <= 40 || event.which === 32) {
  1496. stopEvent(event);
  1497. }
  1498. });
  1499. // KEYDOWN EVENT
  1500. $(settings.fullscreen ? document : jmpress)
  1501. .bind("keydown"+current.keyboardNamespace, function( event ) {
  1502. var eventTarget = $(event.target);
  1503. if ( !settings.fullscreen && !eventTarget.closest(jmpress).length || !keyboardSettings.use ) {
  1504. return;
  1505. }
  1506. for( var nodeName in ignoreKeyboardSettings ) {
  1507. if ( eventTarget[0].nodeName === nodeName && ignoreKeyboardSettings[nodeName].indexOf(event.which) !== -1 ) {
  1508. return;
  1509. }
  1510. }
  1511. var reverseSelect = false;
  1512. var nextFocus;
  1513. if (event.which === 9) {
  1514. // tab
  1515. if ( !eventTarget.closest( jmpress.jmpress('active') ).length ) {
  1516. if ( !event.shiftKey ) {
  1517. nextFocus = jmpress.jmpress('active').find("a[href], :input").filter(":visible").first();
  1518. } else {
  1519. reverseSelect = true;
  1520. }
  1521. } else {
  1522. nextFocus = eventTarget.near( keyboardSettings.tabSelector, event.shiftKey );
  1523. if( !$(nextFocus)
  1524. .closest( settings.stepSelector )
  1525. .is(jmpress.jmpress('active') ) ) {
  1526. nextFocus = undefined;
  1527. }
  1528. }
  1529. if( nextFocus && nextFocus.length > 0 ) {
  1530. nextFocus.focus();
  1531. jmpress.jmpress("scrollFix");
  1532. stopEvent(event);
  1533. return;
  1534. } else {
  1535. if(event.shiftKey) {
  1536. reverseSelect = true;
  1537. }
  1538. }
  1539. }
  1540. var action = keyboardSettings.keys[ event.which ];
  1541. if ( typeof action === "string" ) {
  1542. if (action.indexOf(":") !== -1) {
  1543. action = action.split(":");
  1544. action = event.shiftKey ? action[1] : action[0];
  1545. }
  1546. jmpress.jmpress( action );
  1547. stopEvent(event);
  1548. } else if ( $.isFunction(action) ) {
  1549. action.call(jmpress, event);
  1550. } else if ( action ) {
  1551. jmpress.jmpress.apply( jmpress, action );
  1552. stopEvent(event);
  1553. }
  1554. if (reverseSelect) {
  1555. // tab
  1556. nextFocus = jmpress.jmpress('active').find("a[href], :input").filter(":visible").last();
  1557. nextFocus.focus();
  1558. jmpress.jmpress("scrollFix");
  1559. }
  1560. });
  1561. });
  1562. $jmpress('afterDeinit', function( nil, eventData ) {
  1563. $(document).unbind(eventData.current.keyboardNamespace);
  1564. });
  1565. }(jQuery, document, window));
  1566. /*
  1567. * viewport.js
  1568. * Scale to fit a given viewport
  1569. */
  1570. (function( $, document, window, undefined ) {
  1571. 'use strict';
  1572. function randomString() {
  1573. return "" + Math.round(Math.random() * 100000, 0);
  1574. }
  1575. var browser = (function() {
  1576. var ua = navigator.userAgent.toLowerCase();
  1577. var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
  1578. /(webkit)[ \/]([\w.]+)/.exec(ua) ||
  1579. /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
  1580. /(msie) ([\w.]+)/.exec(ua) ||
  1581. ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
  1582. [];
  1583. return match[1] || "";
  1584. }());
  1585. var defaults = $.jmpress("defaults");
  1586. defaults.viewPort = {
  1587. width: false
  1588. ,height: false
  1589. ,maxScale: 0
  1590. ,minScale: 0
  1591. ,zoomable: 0
  1592. ,zoomBindMove: true
  1593. ,zoomBindWheel: true
  1594. };
  1595. var keys = defaults.keyboard.keys;
  1596. keys[browser === 'mozilla' ? 107 : 187] = "zoomIn"; // +
  1597. keys[browser === 'mozilla' ? 109 : 189] = "zoomOut"; // -
  1598. defaults.reasonableAnimation.resize = {
  1599. transitionDuration: '0s'
  1600. ,transitionDelay: '0ms'
  1601. };
  1602. defaults.reasonableAnimation.zoom = {
  1603. transitionDuration: '0s'
  1604. ,transitionDelay: '0ms'
  1605. };
  1606. $.jmpress("initStep", function( step, eventData ) {
  1607. for(var variable in {"viewPortHeight":1, "viewPortWidth":1, "viewPortMinScale":1, "viewPortMaxScale":1, "viewPortZoomable":1}) {
  1608. eventData.stepData[variable] = eventData.data[variable] && parseFloat(eventData.data[variable]);
  1609. }
  1610. });
  1611. $.jmpress("afterInit", function( nil, eventData ) {
  1612. var jmpress = this;
  1613. eventData.current.viewPortNamespace = ".jmpress-"+randomString();
  1614. $(window).bind("resize"+eventData.current.viewPortNamespace, function (event) {
  1615. $(jmpress).jmpress("reselect", "resize");
  1616. });
  1617. eventData.current.userZoom = 0;
  1618. eventData.current.userTranslateX = 0;
  1619. eventData.current.userTranslateY = 0;
  1620. if(eventData.settings.viewPort.zoomBindWheel) {
  1621. $(eventData.settings.fullscreen ? document : this)
  1622. .bind("mousewheel"+eventData.current.viewPortNamespace+" DOMMouseScroll"+eventData.current.viewPortNamespace, function( event, delta ) {
  1623. delta = delta || event.originalEvent.wheelDelta || -event.originalEvent.detail /* mozilla */;
  1624. var direction = (delta / Math.abs(delta));
  1625. if(direction < 0) {
  1626. $(eventData.jmpress).jmpress("zoomOut", event.originalEvent.x, event.originalEvent.y);
  1627. } else if(direction > 0) {
  1628. $(eventData.jmpress).jmpress("zoomIn", event.originalEvent.x, event.originalEvent.y);
  1629. }
  1630. return false;
  1631. });
  1632. }
  1633. if(eventData.settings.viewPort.zoomBindMove) {
  1634. $(eventData.settings.fullscreen ? document : this).bind("mousedown"+eventData.current.viewPortNamespace, function (event) {
  1635. if(eventData.current.userZoom) {
  1636. eventData.current.userTranslating = { x: event.clientX, y: event.clientY };
  1637. event.preventDefault();
  1638. event.stopImmediatePropagation();
  1639. }
  1640. }).bind("mousemove"+eventData.current.viewPortNamespace, function (event) {
  1641. var userTranslating = eventData.current.userTranslating;
  1642. if(userTranslating) {
  1643. $(jmpress).jmpress("zoomTranslate", event.clientX - userTranslating.x, event.clientY - userTranslating.y);
  1644. userTranslating.x = event.clientX;
  1645. userTranslating.y = event.clientY;
  1646. event.preventDefault();
  1647. event.stopImmediatePropagation();
  1648. }
  1649. }).bind("mouseup"+eventData.current.viewPortNamespace, function (event) {
  1650. if(eventData.current.userTranslating) {
  1651. eventData.current.userTranslating = undefined;
  1652. event.preventDefault();
  1653. event.stopImmediatePropagation();
  1654. }
  1655. });
  1656. }
  1657. });
  1658. function maxAbs(value, range) {
  1659. return Math.max(Math.min(value, range), -range);
  1660. }
  1661. function zoom(x, y, direction) {
  1662. var current = $(this).jmpress("current"),
  1663. settings = $(this).jmpress("settings"),
  1664. stepData = $(this).jmpress("active").data("stepData"),
  1665. container = $(this).jmpress("container");
  1666. if(current.userZoom === 0 && direction < 0) {
  1667. return;
  1668. }
  1669. var zoomableSteps = stepData.viewPortZoomable || settings.viewPort.zoomable;
  1670. if(current.userZoom === zoomableSteps && direction > 0) {
  1671. return;
  1672. }
  1673. current.userZoom += direction;
  1674. var halfWidth = $(container).innerWidth()/2,
  1675. halfHeight = $(container).innerHeight()/2;
  1676. x = x ? x - halfWidth : x;
  1677. y = y ? y - halfHeight : y;
  1678. // TODO this is not perfect... too much math... :(
  1679. current.userTranslateX =
  1680. maxAbs(current.userTranslateX - direction * x / current.zoomOriginWindowScale / zoomableSteps,
  1681. halfWidth * current.userZoom * current.userZoom / zoomableSteps);
  1682. current.userTranslateY =
  1683. maxAbs(current.userTranslateY - direction * y / current.zoomOriginWindowScale / zoomableSteps,
  1684. halfHeight * current.userZoom * current.userZoom / zoomableSteps);
  1685. $(this).jmpress("reselect", "zoom");
  1686. }
  1687. $.jmpress("register", "zoomIn", function(x, y) {
  1688. zoom.call(this, x||0, y||0, 1);
  1689. });
  1690. $.jmpress("register", "zoomOut", function(x, y) {
  1691. zoom.call(this, x||0, y||0, -1);
  1692. });
  1693. $.jmpress("register", "zoomTranslate", function(x, y) {
  1694. var current = $(this).jmpress("current"),
  1695. settings = $(this).jmpress("settings"),
  1696. stepData = $(this).jmpress("active").data("stepData"),
  1697. container = $(this).jmpress("container");
  1698. var zoomableSteps = stepData.viewPortZoomable || settings.viewPort.zoomable;
  1699. var halfWidth = $(container).innerWidth(),
  1700. halfHeight = $(container).innerHeight();
  1701. current.userTranslateX =
  1702. maxAbs(current.userTranslateX + x / current.zoomOriginWindowScale,
  1703. halfWidth * current.userZoom * current.userZoom / zoomableSteps);
  1704. current.userTranslateY =
  1705. maxAbs(current.userTranslateY + y / current.zoomOriginWindowScale,
  1706. halfHeight * current.userZoom * current.userZoom / zoomableSteps);
  1707. $(this).jmpress("reselect", "zoom");
  1708. });
  1709. $.jmpress('afterDeinit', function( nil, eventData ) {
  1710. $(eventData.settings.fullscreen ? document : this).unbind(eventData.current.viewPortNamespace);
  1711. $(window).unbind(eventData.current.viewPortNamespace);
  1712. });
  1713. $.jmpress("setActive", function( step, eventData ) {
  1714. var viewPort = eventData.settings.viewPort;
  1715. var viewPortHeight = eventData.stepData.viewPortHeight || viewPort.height;
  1716. var viewPortWidth = eventData.stepData.viewPortWidth || viewPort.width;
  1717. var viewPortMaxScale = eventData.stepData.viewPortMaxScale || viewPort.maxScale;
  1718. var viewPortMinScale = eventData.stepData.viewPortMinScale || viewPort.minScale;
  1719. // Correct the scale based on the window's size
  1720. var windowScaleY = viewPortHeight && $(eventData.container).innerHeight()/viewPortHeight;
  1721. var windowScaleX = viewPortWidth && $(eventData.container).innerWidth()/viewPortWidth;
  1722. var windowScale = (windowScaleX || windowScaleY) && Math.min( windowScaleX || windowScaleY, windowScaleY || windowScaleX );
  1723. if(windowScale) {
  1724. windowScale = windowScale || 1;
  1725. if(viewPortMaxScale) {
  1726. windowScale = Math.min(windowScale, viewPortMaxScale);
  1727. }
  1728. if(viewPortMinScale) {
  1729. windowScale = Math.max(windowScale, viewPortMinScale);
  1730. }
  1731. var zoomableSteps = eventData.stepData.viewPortZoomable || eventData.settings.viewPort.zoomable;
  1732. if(zoomableSteps) {
  1733. var diff = (1/windowScale) - (1/viewPortMaxScale);
  1734. diff /= zoomableSteps;
  1735. windowScale = 1/((1/windowScale) - diff * eventData.current.userZoom);
  1736. }
  1737. eventData.target.transform.reverse();
  1738. if(eventData.current.userTranslateX && eventData.current.userTranslateY) {
  1739. eventData.target.transform.push(["translate", eventData.current.userTranslateX, eventData.current.userTranslateY, 0]);
  1740. } else {
  1741. eventData.target.transform.push(["translate"]);
  1742. }
  1743. eventData.target.transform.push(["scale",
  1744. windowScale,
  1745. windowScale,
  1746. 1]);
  1747. eventData.target.transform.reverse();
  1748. eventData.target.perspectiveScale /= windowScale;
  1749. }
  1750. eventData.current.zoomOriginWindowScale = windowScale;
  1751. });
  1752. $.jmpress("setInactive", function( step, eventData ) {
  1753. if(!eventData.nextStep || !step || $(eventData.nextStep).attr("id") !== $(step).attr("id")) {
  1754. eventData.current.userZoom = 0;
  1755. eventData.current.userTranslateX = 0;
  1756. eventData.current.userTranslateY = 0;
  1757. }
  1758. });
  1759. }(jQuery, document, window));
  1760. /*
  1761. * mouse.js
  1762. * Clicking to select a step
  1763. */
  1764. (function( $, document, window, undefined ) {
  1765. 'use strict';
  1766. var $jmpress = $.jmpress;
  1767. /* FUNCTIONS */
  1768. function randomString() {
  1769. return "" + Math.round(Math.random() * 100000, 0);
  1770. }
  1771. /* DEFAULTS */
  1772. $jmpress("defaults").mouse = {
  1773. clickSelects: true
  1774. };
  1775. /* HOOKS */
  1776. $jmpress("afterInit", function( nil, eventData ) {
  1777. var settings = eventData.settings,
  1778. stepSelector = settings.stepSelector,
  1779. current = eventData.current,
  1780. jmpress = $(this);
  1781. current.clickableStepsNamespace = ".jmpress-"+randomString();
  1782. jmpress.bind("click"+current.clickableStepsNamespace, function(event) {
  1783. if (!settings.mouse.clickSelects || current.userZoom) {
  1784. return;
  1785. }
  1786. // get clicked step
  1787. var clickedStep = $(event.target).closest(stepSelector);
  1788. // clicks on the active step do default
  1789. if ( clickedStep.is( jmpress.jmpress("active") ) ) {
  1790. return;
  1791. }
  1792. if (clickedStep.length) {
  1793. // select the clicked step
  1794. jmpress.jmpress("select", clickedStep[0], "click");
  1795. event.preventDefault();
  1796. event.stopPropagation();
  1797. }
  1798. });
  1799. });
  1800. $jmpress('afterDeinit', function( nil, eventData ) {
  1801. $(this).unbind(eventData.current.clickableStepsNamespace);
  1802. });
  1803. }(jQuery, document, window));
  1804. /*
  1805. * mobile.js
  1806. * Adds support for swipe on touch supported browsers
  1807. */
  1808. (function( $, document, window, undefined ) {
  1809. 'use strict';
  1810. var $jmpress = $.jmpress;
  1811. /* FUNCTIONS */
  1812. function randomString() {
  1813. return "" + Math.round(Math.random() * 100000, 0);
  1814. }
  1815. /* HOOKS */
  1816. $jmpress( 'afterInit', function( step, eventData ) {
  1817. var settings = eventData.settings,
  1818. current = eventData.current,
  1819. jmpress = eventData.jmpress;
  1820. current.mobileNamespace = ".jmpress-"+randomString();
  1821. var data, start = [0,0];
  1822. $(settings.fullscreen ? document : jmpress)
  1823. .bind("touchstart"+current.mobileNamespace, function( event ) {
  1824. data = event.originalEvent.touches[0];
  1825. start = [ data.pageX, data.pageY ];
  1826. }).bind("touchmove"+current.mobileNamespace, function( event ) {
  1827. data = event.originalEvent.touches[0];
  1828. event.preventDefault();
  1829. return false;
  1830. }).bind("touchend"+current.mobileNamespace, function( event ) {
  1831. var end = [ data.pageX, data.pageY ],
  1832. diff = [ end[0]-start[0], end[1]-start[1] ];
  1833. if(Math.max(Math.abs(diff[0]), Math.abs(diff[1])) > 50) {
  1834. diff = Math.abs(diff[0]) > Math.abs(diff[1]) ? diff[0] : diff[1];
  1835. $(jmpress).jmpress(diff > 0 ? "prev" : "next");
  1836. event.preventDefault();
  1837. return false;
  1838. }
  1839. });
  1840. });
  1841. $jmpress('afterDeinit', function( nil, eventData ) {
  1842. var settings = eventData.settings,
  1843. current = eventData.current,
  1844. jmpress = eventData.jmpress;
  1845. $(settings.fullscreen ? document : jmpress).unbind(current.mobileNamespace);
  1846. });
  1847. }(jQuery, document, window));
  1848. /*
  1849. * templates.js
  1850. * The amazing template engine
  1851. */
  1852. (function( $, document, window, undefined ) {
  1853. 'use strict';
  1854. var $jmpress = $.jmpress,
  1855. templateFromParentIdent = "_template_",
  1856. templateFromApplyIdent = "_applied_template_";
  1857. /* STATIC VARS */
  1858. var templates = {};
  1859. /* FUNCTIONS */
  1860. function addUndefined( target, values, prefix ) {
  1861. for( var name in values ) {
  1862. var targetName = name;
  1863. if ( prefix ) {
  1864. targetName = prefix + targetName.substr(0, 1).toUpperCase() + targetName.substr(1);
  1865. }
  1866. if ( $.isPlainObject(values[name]) ) {
  1867. addUndefined( target, values[name], targetName );
  1868. } else if( target[targetName] === undefined ) {
  1869. target[targetName] = values[name];
  1870. }
  1871. }
  1872. }
  1873. function applyChildrenTemplates( children, templateChildren ) {
  1874. if ($.isArray(templateChildren)) {
  1875. if (templateChildren.length < children.length) {
  1876. $.error("more nested steps than children in template");
  1877. } else {
  1878. children.each(function(idx, child) {
  1879. child = $(child);
  1880. var tmpl = child.data(templateFromParentIdent) || {};
  1881. addUndefined(tmpl, templateChildren[idx]);
  1882. child.data(templateFromParentIdent, tmpl);
  1883. });
  1884. }
  1885. } else if($.isFunction(templateChildren)) {
  1886. children.each(function(idx, child) {
  1887. child = $(child);
  1888. var tmpl = child.data(templateFromParentIdent) || {};
  1889. addUndefined(tmpl, templateChildren(idx, child, children));
  1890. child.data(templateFromParentIdent, tmpl);
  1891. });
  1892. } // TODO: else if(object)
  1893. }
  1894. function applyTemplate( data, element, template, eventData ) {
  1895. if (template.children) {
  1896. var children = element.children( eventData.settings.stepSelector );
  1897. applyChildrenTemplates( children, template.children );
  1898. }
  1899. applyTemplateData( data, template );
  1900. }
  1901. function applyTemplateData( data, template ) {
  1902. addUndefined(data, template);
  1903. }
  1904. /* HOOKS */
  1905. $jmpress("beforeInitStep", function( step, eventData ) {
  1906. step = $(step);
  1907. var data = eventData.data,
  1908. templateFromAttr = data.template,
  1909. templateFromApply = step.data(templateFromApplyIdent),
  1910. templateFromParent = step.data(templateFromParentIdent);
  1911. if(templateFromAttr) {
  1912. $.each(templateFromAttr.split(" "), function(idx, tmpl) {
  1913. var template = templates[tmpl];
  1914. applyTemplate( data, step, template, eventData );
  1915. });
  1916. }
  1917. if (templateFromApply) {
  1918. applyTemplate( data, step, templateFromApply, eventData );
  1919. }
  1920. if (templateFromParent) {
  1921. applyTemplate( data, step, templateFromParent, eventData );
  1922. step.data(templateFromParentIdent, null);
  1923. if(templateFromParent.template) {
  1924. $.each(templateFromParent.template.split(" "), function(idx, tmpl) {
  1925. var template = templates[tmpl];
  1926. applyTemplate( data, step, template, eventData );
  1927. });
  1928. }
  1929. }
  1930. });
  1931. $jmpress("beforeInit", function( nil, eventData ) {
  1932. var data = $jmpress("dataset", this),
  1933. dataTemplate = data.template,
  1934. stepSelector = eventData.settings.stepSelector;
  1935. if (dataTemplate) {
  1936. var template = templates[dataTemplate];
  1937. applyChildrenTemplates( $(this).find(stepSelector).filter(function() {
  1938. return !$(this).parent().is(stepSelector);
  1939. }), template.children );
  1940. }
  1941. });
  1942. /* EXPORTED FUNCTIONS */
  1943. $jmpress("register", "template", function( name, tmpl ) {
  1944. if (templates[name]) {
  1945. templates[name] = $.extend(true, {}, templates[name], tmpl);
  1946. } else {
  1947. templates[name] = $.extend(true, {}, tmpl);
  1948. }
  1949. });
  1950. $jmpress("register", "apply", function( selector, tmpl ) {
  1951. if( !tmpl ) {
  1952. // TODO ERROR because settings not found
  1953. var stepSelector = $(this).jmpress("settings").stepSelector;
  1954. applyChildrenTemplates( $(this).find(stepSelector).filter(function() {
  1955. return !$(this).parent().is(stepSelector);
  1956. }), selector );
  1957. } else if($.isArray(tmpl)) {
  1958. applyChildrenTemplates( $(selector), tmpl );
  1959. } else {
  1960. var template;
  1961. if(typeof tmpl === "string") {
  1962. template = templates[tmpl];
  1963. } else {
  1964. template = $.extend(true, {}, tmpl);
  1965. }
  1966. $(selector).each(function(idx, element) {
  1967. element = $(element);
  1968. var tmpl = element.data(templateFromApplyIdent) || {};
  1969. addUndefined(tmpl, template);
  1970. element.data(templateFromApplyIdent, tmpl);
  1971. });
  1972. }
  1973. });
  1974. }(jQuery, document, window));
  1975. /*
  1976. * jqevents.js
  1977. * Fires jQuery events
  1978. */
  1979. (function( $, document, window, undefined ) {
  1980. 'use strict';
  1981. /* HOOKS */
  1982. // the events should not bubble up the tree
  1983. // elsewise nested jmpress would cause buggy behavior
  1984. $.jmpress("setActive", function( step, eventData ) {
  1985. if(eventData.prevStep !== step) {
  1986. $(step).triggerHandler("enterStep");
  1987. }
  1988. });
  1989. $.jmpress("setInactive", function( step, eventData ) {
  1990. if(eventData.nextStep !== step) {
  1991. $(step).triggerHandler("leaveStep");
  1992. }
  1993. });
  1994. }(jQuery, document, window));
  1995. /*
  1996. * animation.js
  1997. * Apply custom animations to steps
  1998. */
  1999. (function( $, document, window, undefined ) {
  2000. 'use strict';
  2001. function parseSubstepInfo(str) {
  2002. var arr = str.split(" ");
  2003. var className = arr[0];
  2004. var config = { willClass: "will-"+className, doClass: "do-"+className, hasClass: "has-"+className };
  2005. var state = "";
  2006. for(var i = 1; i < arr.length; i++) {
  2007. var s = arr[i];
  2008. switch(state) {
  2009. case "":
  2010. if(s === "after") {
  2011. state = "after";
  2012. } else {
  2013. $.warn("unknown keyword in '"+str+"'. '"+s+"' unknown.");
  2014. }
  2015. break;
  2016. case "after":
  2017. if(s.match(/^[1-9][0-9]*m?s?/)) {
  2018. var value = parseFloat(s);
  2019. if(s.indexOf("ms") !== -1) {
  2020. value *= 1;
  2021. } else if(s.indexOf("s") !== -1) {
  2022. value *= 1000;
  2023. } else if(s.indexOf("m") !== -1) {
  2024. value *= 60000;
  2025. }
  2026. config.delay = value;
  2027. } else {
  2028. config.after = Array.prototype.slice.call(arr, i).join(" ");
  2029. i = arr.length;
  2030. }
  2031. }
  2032. }
  2033. return config;
  2034. }
  2035. function find(array, selector, start, end) {
  2036. end = end || (array.length - 1);
  2037. start = start || 0;
  2038. for(var i = start; i < end + 1; i++) {
  2039. if($(array[i].element).is(selector)) {
  2040. return i;
  2041. }
  2042. }
  2043. }
  2044. function addOn(list, substep, delay) {
  2045. $.each(substep._on, function(idx, child) {
  2046. list.push({substep: child.substep, delay: child.delay + delay});
  2047. addOn(list, child.substep, child.delay + delay);
  2048. });
  2049. }
  2050. $.jmpress("defaults").customAnimationDataAttribute = "jmpress";
  2051. $.jmpress("afterInit", function( nil, eventData ) {
  2052. eventData.current.animationTimeouts = [];
  2053. eventData.current.animationCleanupWaiting = [];
  2054. });
  2055. $.jmpress("applyStep", function( step, eventData ) {
  2056. // read custom animation from elements
  2057. var substepsData = {};
  2058. var listOfSubsteps = [];
  2059. $(step).find("[data-"+eventData.settings.customAnimationDataAttribute+"]")
  2060. .each(function(idx, element) {
  2061. if($(element).closest(eventData.settings.stepSelector).is(step)) {
  2062. listOfSubsteps.push({element: element});
  2063. }
  2064. });
  2065. if(listOfSubsteps.length === 0) {
  2066. return;
  2067. }
  2068. $.each(listOfSubsteps, function(idx, substep) {
  2069. substep.info = parseSubstepInfo(
  2070. $(substep.element).data(eventData.settings.customAnimationDataAttribute));
  2071. $(substep.element).addClass(substep.info.willClass);
  2072. substep._on = [];
  2073. substep._after = null;
  2074. });
  2075. var current = {_after: undefined, _on: [], info: {}}; // virtual zero step
  2076. $.each(listOfSubsteps, function(idx, substep) {
  2077. var other = substep.info.after;
  2078. if(other) {
  2079. if(other === "step") {
  2080. other = current;
  2081. } else if(other === "prev") {
  2082. other = listOfSubsteps[idx-1];
  2083. } else {
  2084. var index = find(listOfSubsteps, other, 0, idx - 1);
  2085. if(index === undefined) {
  2086. index = find(listOfSubsteps, other);
  2087. }
  2088. other = (index === undefined || index === idx) ? listOfSubsteps[idx-1] : listOfSubsteps[index];
  2089. }
  2090. } else {
  2091. other = listOfSubsteps[idx-1];
  2092. }
  2093. if(other) {
  2094. if(!substep.info.delay) {
  2095. if(!other._after) {
  2096. other._after = substep;
  2097. return;
  2098. }
  2099. other = other._after;
  2100. }
  2101. other._on.push({substep: substep, delay: substep.info.delay || 0});
  2102. }
  2103. });
  2104. if(current._after === undefined && current._on.length === 0) {
  2105. var startStep = find(listOfSubsteps, eventData.stepData.startSubstep) || 0;
  2106. current._after = listOfSubsteps[startStep];
  2107. }
  2108. var substepsInOrder = [];
  2109. function findNextFunc(idx, item) {
  2110. if(item.substep._after) {
  2111. current = item.substep._after;
  2112. return false;
  2113. }
  2114. }
  2115. do {
  2116. var substepList = [{substep: current, delay: 0}];
  2117. addOn(substepList, current, 0);
  2118. substepsInOrder.push(substepList);
  2119. current = null;
  2120. $.each(substepList, findNextFunc);
  2121. } while(current);
  2122. substepsData.list = substepsInOrder;
  2123. $(step).data("substepsData", substepsData);
  2124. });
  2125. $.jmpress("unapplyStep", function( step, eventData ) {
  2126. var substepsData = $(step).data("substepsData");
  2127. if(substepsData) {
  2128. $.each(substepsData.list, function(idx, activeSubsteps) {
  2129. $.each(activeSubsteps, function(idx, substep) {
  2130. if(substep.substep.info.willClass) {
  2131. $(substep.substep.element).removeClass(substep.substep.info.willClass);
  2132. }
  2133. if(substep.substep.info.hasClass) {
  2134. $(substep.substep.element).removeClass(substep.substep.info.hasClass);
  2135. }
  2136. if(substep.substep.info.doClass) {
  2137. $(substep.substep.element).removeClass(substep.substep.info.doClass);
  2138. }
  2139. });
  2140. });
  2141. }
  2142. });
  2143. $.jmpress("setActive", function(step, eventData) {
  2144. var substepsData = $(step).data("substepsData");
  2145. if(!substepsData) {
  2146. return;
  2147. }
  2148. if(eventData.substep === undefined) {
  2149. eventData.substep =
  2150. (eventData.reason === "prev" ?
  2151. substepsData.list.length-1 :
  2152. 0
  2153. );
  2154. }
  2155. var substep = eventData.substep;
  2156. $.each(eventData.current.animationTimeouts, function(idx, timeout) {
  2157. clearTimeout(timeout);
  2158. });
  2159. eventData.current.animationTimeouts = [];
  2160. $.each(substepsData.list, function(idx, activeSubsteps) {
  2161. var applyHas = idx < substep;
  2162. var applyDo = idx <= substep;
  2163. $.each(activeSubsteps, function(idx, substep) {
  2164. if(substep.substep.info.hasClass) {
  2165. $(substep.substep.element)[(applyHas?"add":"remove")+"Class"](substep.substep.info.hasClass);
  2166. }
  2167. function applyIt() {
  2168. $(substep.substep.element).addClass(substep.substep.info.doClass);
  2169. }
  2170. if(applyDo && !applyHas && substep.delay && eventData.reason !== "prev") {
  2171. if(substep.substep.info.doClass) {
  2172. $(substep.substep.element).removeClass(substep.substep.info.doClass);
  2173. eventData.current.animationTimeouts.push(setTimeout(applyIt, substep.delay));
  2174. }
  2175. } else {
  2176. if(substep.substep.info.doClass) {
  2177. $(substep.substep.element)[(applyDo?"add":"remove")+"Class"](substep.substep.info.doClass);
  2178. }
  2179. }
  2180. });
  2181. });
  2182. });
  2183. $.jmpress("setInactive", function(step, eventData) {
  2184. if(eventData.nextStep === step) {
  2185. return;
  2186. }
  2187. function cleanupAnimation( substepsData ) {
  2188. $.each(substepsData.list, function(idx, activeSubsteps) {
  2189. $.each(activeSubsteps, function(idx, substep) {
  2190. if(substep.substep.info.hasClass) {
  2191. $(substep.substep.element).removeClass(substep.substep.info.hasClass);
  2192. }
  2193. if(substep.substep.info.doClass) {
  2194. $(substep.substep.element).removeClass(substep.substep.info.doClass);
  2195. }
  2196. });
  2197. });
  2198. }
  2199. $.each(eventData.current.animationCleanupWaiting, function(idx, item) {
  2200. cleanupAnimation(item);
  2201. });
  2202. eventData.current.animationCleanupWaiting = [];
  2203. var substepsData = $(step).data("substepsData");
  2204. if(substepsData) {
  2205. eventData.current.animationCleanupWaiting.push( substepsData );
  2206. }
  2207. });
  2208. $.jmpress("selectNext", function( step, eventData ) {
  2209. if(eventData.substep === undefined) {
  2210. return;
  2211. }
  2212. var substepsData = $(step).data("substepsData");
  2213. if(!substepsData) {
  2214. return;
  2215. }
  2216. if(eventData.substep < substepsData.list.length-1) {
  2217. return {step: step, substep: eventData.substep+1};
  2218. }
  2219. });
  2220. $.jmpress("selectPrev", function( step, eventData ) {
  2221. if(eventData.substep === undefined) {
  2222. return;
  2223. }
  2224. var substepsData = $(step).data("substepsData");
  2225. if(!substepsData) {
  2226. return;
  2227. }
  2228. if(eventData.substep > 0) {
  2229. return {step: step, substep: eventData.substep-1};
  2230. }
  2231. });
  2232. }(jQuery, document, window));
  2233. /*!
  2234. * plugin for jmpress.js v0.4.5
  2235. *
  2236. * Copyright 2013 Kyle Robinson Young @shama & Tobias Koppers @sokra
  2237. * Licensed MIT
  2238. * http://www.opensource.org/licenses/mit-license.php
  2239. *//*
  2240. * jmpress.toggle plugin
  2241. * For binding a key to toggle de/initialization of jmpress.js.
  2242. */
  2243. (function( $, document, window, undefined ) {
  2244. 'use strict';
  2245. $.jmpress("register", "toggle", function( key, config, initial ) {
  2246. var jmpress = this;
  2247. $(document).bind("keydown", function( event ) {
  2248. if ( event.keyCode === key ) {
  2249. if ($(jmpress).jmpress("initialized")) {
  2250. $(jmpress).jmpress("deinit");
  2251. } else {
  2252. $(jmpress).jmpress(config);
  2253. }
  2254. }
  2255. });
  2256. if ( initial ) {
  2257. $(jmpress).jmpress(config);
  2258. }
  2259. });
  2260. }(jQuery, document, window));
  2261. /*
  2262. * jmpress.secondary plugin
  2263. * Apply a secondary animation when step is selected.
  2264. */
  2265. (function( $, document, window, undefined ) {
  2266. 'use strict';
  2267. $.jmpress("initStep", function( step, eventData ) {
  2268. for(var name in eventData.data) {
  2269. if(name.indexOf("secondary") === 0) {
  2270. eventData.stepData[name] = eventData.data[name];
  2271. }
  2272. }
  2273. });
  2274. function exchangeIf(childStepData, condition, step) {
  2275. if(childStepData.secondary &&
  2276. childStepData.secondary.split(" ").indexOf(condition) !== -1) {
  2277. for(var name in childStepData) {
  2278. if(name.length > 9 && name.indexOf("secondary") === 0) {
  2279. var tmp = childStepData[name];
  2280. var normal = name.substr(9);
  2281. normal = normal.substr(0, 1).toLowerCase() + normal.substr(1);
  2282. childStepData[name] = childStepData[normal];
  2283. childStepData[normal] = tmp;
  2284. }
  2285. }
  2286. $(this).jmpress("reapply", $(step));
  2287. }
  2288. }
  2289. $.jmpress("beforeActive", function( step, eventData ) {
  2290. exchangeIf.call(eventData.jmpress, $(step).data("stepData"), "self", step);
  2291. var parent = $(step).parent();
  2292. $(parent)
  2293. .children(eventData.settings.stepSelector)
  2294. .each(function(idx, child) {
  2295. var childStepData = $(child).data("stepData");
  2296. exchangeIf.call(eventData.jmpress, childStepData, "siblings", child);
  2297. });
  2298. function grandchildrenFunc(idx, child) {
  2299. var childStepData = $(child).data("stepData");
  2300. exchangeIf.call(eventData.jmpress, childStepData, "grandchildren", child);
  2301. }
  2302. for(var i = 1; i < eventData.parents.length; i++) {
  2303. $(eventData.parents[i])
  2304. .children(eventData.settings.stepSelector)
  2305. .each();
  2306. }
  2307. });
  2308. $.jmpress("setInactive", function( step, eventData ) {
  2309. exchangeIf.call(eventData.jmpress, $(step).data("stepData"), "self", step);
  2310. var parent = $(step).parent();
  2311. $(parent)
  2312. .children(eventData.settings.stepSelector)
  2313. .each(function(idx, child) {
  2314. var childStepData = $(child).data("stepData");
  2315. exchangeIf.call(eventData.jmpress, childStepData, "siblings", child);
  2316. });
  2317. function grandchildrenFunc(idx, child) {
  2318. var childStepData = $(child).data("stepData");
  2319. exchangeIf.call(eventData.jmpress, childStepData, "grandchildren", child);
  2320. }
  2321. for(var i = 1; i < eventData.parents.length; i++) {
  2322. $(eventData.parents[i])
  2323. .children(eventData.settings.stepSelector)
  2324. .each(grandchildrenFunc);
  2325. }
  2326. });
  2327. }(jQuery, document, window));
  2328. /*
  2329. * jmpress.duration plugin
  2330. * For auto advancing steps after a given duration and optionally displaying a
  2331. * progress bar.
  2332. */
  2333. (function( $, document, window, undefined ) {
  2334. 'use strict';
  2335. $.jmpress("defaults").duration = {
  2336. defaultValue: -1
  2337. ,defaultAction: "next"
  2338. ,barSelector: undefined
  2339. ,barProperty: "width"
  2340. ,barPropertyStart: "0"
  2341. ,barPropertyEnd: "100%"
  2342. };
  2343. $.jmpress("initStep", function( step, eventData ) {
  2344. eventData.stepData.duration = eventData.data.duration && parseInt(eventData.data.duration, 10);
  2345. eventData.stepData.durationAction = eventData.data.durationAction;
  2346. });
  2347. $.jmpress("setInactive", function( step, eventData ) {
  2348. var settings = eventData.settings,
  2349. durationSettings = settings.duration,
  2350. current = eventData.current;
  2351. var dur = eventData.stepData.duration || durationSettings.defaultValue;
  2352. if( current.durationTimeout ) {
  2353. if( durationSettings.barSelector ) {
  2354. var css = {
  2355. transitionProperty: durationSettings.barProperty
  2356. ,transitionDuration: '0'
  2357. ,transitionDelay: '0'
  2358. ,transitionTimingFunction: 'linear'
  2359. };
  2360. css[durationSettings.barProperty] = durationSettings.barPropertyStart;
  2361. var bars = $(durationSettings.barSelector);
  2362. $.jmpress("css", bars, css);
  2363. bars.each(function(idx, element) {
  2364. var next = $(element).next();
  2365. var parent = $(element).parent();
  2366. $(element).detach();
  2367. if(next.length) {
  2368. next.insertBefore(element);
  2369. } else {
  2370. parent.append(element);
  2371. }
  2372. });
  2373. }
  2374. clearTimeout(current.durationTimeout);
  2375. delete current.durationTimeout;
  2376. }
  2377. });
  2378. $.jmpress("setActive", function( step, eventData ) {
  2379. var settings = eventData.settings,
  2380. durationSettings = settings.duration,
  2381. current = eventData.current;
  2382. var dur = eventData.stepData.duration || durationSettings.defaultValue;
  2383. if( dur && dur > 0 ) {
  2384. if( durationSettings.barSelector ) {
  2385. var css = {
  2386. transitionProperty: durationSettings.barProperty
  2387. ,transitionDuration: (dur-settings.transitionDuration*2/3-100)+"ms"
  2388. ,transitionDelay: (settings.transitionDuration*2/3)+'ms'
  2389. ,transitionTimingFunction: 'linear'
  2390. };
  2391. css[durationSettings.barProperty] = durationSettings.barPropertyEnd;
  2392. $.jmpress("css", $(durationSettings.barSelector), css);
  2393. }
  2394. var jmpress = this;
  2395. if(current.durationTimeout) {
  2396. clearTimeout(current.durationTimeout);
  2397. current.durationTimeout = undefined;
  2398. }
  2399. current.durationTimeout = setTimeout(function() {
  2400. var action = eventData.stepData.durationAction || durationSettings.defaultAction;
  2401. $(jmpress).jmpress(action);
  2402. }, dur);
  2403. }
  2404. });
  2405. }(jQuery, document, window));
  2406. /*
  2407. * jmpress.presentation-mode plugin
  2408. * Display a window for the presenter with notes and a control and view of the
  2409. * presentation
  2410. */
  2411. (function( $, document, window, undefined ) {
  2412. 'use strict';
  2413. var $jmpress = $.jmpress;
  2414. var PREFIX = "jmpress-presentation-";
  2415. /* FUNCTIONS */
  2416. function randomString() {
  2417. return "" + Math.round(Math.random() * 100000, 0);
  2418. }
  2419. /* DEFAULTS */
  2420. $jmpress("defaults").presentationMode = {
  2421. use: true,
  2422. url: "presentation-screen.html",
  2423. notesUrl: false,
  2424. transferredValues: ["userZoom", "userTranslateX", "userTranslateY"]
  2425. };
  2426. $jmpress("defaults").keyboard.keys[80] = "presentationPopup"; // p key
  2427. /* HOOKS */
  2428. $jmpress("afterInit", function( nil, eventData) {
  2429. var current = eventData.current;
  2430. current.selectMessageListeners = [];
  2431. if(eventData.settings.presentationMode.use) {
  2432. window.addEventListener("message", function(event) {
  2433. // We do not test orgin, because we want to accept messages
  2434. // from all orgins
  2435. try {
  2436. if(typeof event.data !== "string" || event.data.indexOf(PREFIX) !== 0) {
  2437. return;
  2438. }
  2439. var json = JSON.parse(event.data.slice(PREFIX.length));
  2440. switch(json.type) {
  2441. case "select":
  2442. $.each(eventData.settings.presentationMode.transferredValues, function(idx, name) {
  2443. eventData.current[name] = json[name];
  2444. });
  2445. if(/[a-z0-9\-]+/i.test(json.targetId) && typeof json.substep in {number:1,undefined:1}) {
  2446. $(eventData.jmpress).jmpress("select", {step: "#"+json.targetId, substep: json.substep}, json.reason);
  2447. } else {
  2448. $.error("For security reasons the targetId must match /[a-z0-9\\-]+/i and substep must be a number.");
  2449. }
  2450. break;
  2451. case "listen":
  2452. current.selectMessageListeners.push(event.source);
  2453. break;
  2454. case "ok":
  2455. clearTimeout(current.presentationPopupTimeout);
  2456. break;
  2457. case "read":
  2458. try {
  2459. event.source.postMessage(PREFIX + JSON.stringify({type: "url", url: window.location.href, notesUrl: eventData.settings.presentationMode.notesUrl}), "*");
  2460. } catch(e) {
  2461. $.error("Cannot post message to source: " + e);
  2462. }
  2463. break;
  2464. default:
  2465. throw "Unknown message type: " + json.type;
  2466. }
  2467. } catch(e) {
  2468. $.error("Received message is malformed: " + e);
  2469. }
  2470. });
  2471. try {
  2472. if(window.parent && window.parent !== window) {
  2473. window.parent.postMessage(PREFIX + JSON.stringify({
  2474. "type": "afterInit"
  2475. }), "*");
  2476. }
  2477. } catch(e) {
  2478. $.error("Cannot post message to parent: " + e);
  2479. }
  2480. }
  2481. });
  2482. $jmpress("afterDeinit", function( nil, eventData) {
  2483. if(eventData.settings.presentationMode.use) {
  2484. try {
  2485. if(window.parent && window.parent !== window) {
  2486. window.parent.postMessage(PREFIX + JSON.stringify({
  2487. "type": "afterDeinit"
  2488. }), "*");
  2489. }
  2490. } catch(e) {
  2491. $.error("Cannot post message to parent: " + e);
  2492. }
  2493. }
  2494. });
  2495. $jmpress("setActive", function( step, eventData) {
  2496. var stepId = $(eventData.delegatedFrom).attr("id"),
  2497. substep = eventData.substep,
  2498. reason = eventData.reason;
  2499. $.each(eventData.current.selectMessageListeners, function(idx, listener) {
  2500. try {
  2501. var msg = {
  2502. "type": "select",
  2503. "targetId": stepId,
  2504. "substep": substep,
  2505. "reason": reason
  2506. };
  2507. $.each(eventData.settings.presentationMode.transferredValues, function(idx, name) {
  2508. msg[name] = eventData.current[name];
  2509. });
  2510. listener.postMessage(PREFIX + JSON.stringify(msg), "*");
  2511. } catch(e) {
  2512. $.error("Cannot post message to listener: " + e);
  2513. }
  2514. });
  2515. });
  2516. $jmpress("register", "presentationPopup", function() {
  2517. function trySend() {
  2518. jmpress.jmpress("current").presentationPopupTimeout = setTimeout(trySend, 100);
  2519. try {
  2520. popup.postMessage(PREFIX + JSON.stringify({type: "url", url: window.location.href, notesUrl: jmpress.jmpress("settings").presentationMode.notesUrl}), "*");
  2521. } catch(e) {
  2522. }
  2523. }
  2524. var jmpress = $(this),
  2525. popup;
  2526. if(jmpress.jmpress("settings").presentationMode.use) {
  2527. popup = window.open($(this).jmpress("settings").presentationMode.url);
  2528. jmpress.jmpress("current").presentationPopupTimeout = setTimeout(trySend, 100);
  2529. }
  2530. });
  2531. }(jQuery, document, window));