/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ "use strict"; // Constants var DEBUG = false; var DEBUG_WWW_URL = 'http://localhost/~ghis/momo/www/'; var ANIMATION_ENABLED = true; var ANIMATION_OUT_CLASS = 'pt-page-moveToLeftEasing pt-page-ontop'; var ANIMATION_IN_CLASS = 'pt-page-moveFromRight'; var ANIMATION_BACK_OUT_CLASS = 'pt-page-moveToRightEasing pt-page-ontop'; var ANIMATION_BACK_IN_CLASS = 'pt-page-moveFromLeft'; var ON_PULL = 'checkForUpdate'; // || 'update' var CHECK_FOR_CONNECTION_INTERVAL = 3000; // Application var app = { // Pages Registry pages: {}, // Minimal Default Manifest manifest: { meta: { 'title': 'Momo Application', 'icon': 'icon.png', 'contact': 'contact@cadoles.com', 'manifestUrl': 'index.json', 'assetsUrl': 'assets.zip', 'updateFreq': 60000, 'default': true, 'titlePersitent': true, 'titleSeparator': "
", 'menu': [], 'stylesheets': [], 'javascripts': [] }, 'menu': [], 'stylesheets': [], 'javascripts': [], 'content': tmpl('momo-first-launch-tmpl', {}), 'footer': undefined }, // Default page attributes defaultPage: { colxs: 4, colsm: 3, colmd: 2, collg: 1, menu: [], javascripts: [], stylesheets: [] }, // Misc Data online : false, current : 0, isAnimating : false, endCurrPage : false, endNextPage : false, pageIndex : 0, pageHistory : [/*window.location.hash.slice(1)*/], historyLength : window.history.length, hasStarted : false, assetsMtime : null, manifestMtime : null, updateTimeout : null, ignoreHash : false, previousPage : 'home', currentPage : 'home', parentPage : 'home', javascripts : [], stylesheets : [], rootPath : DEBUG_WWW_URL, //TODO Remove useless calls to fileSystem // Application Constructor initialize: function() { this.bindEvents(); }, // Application events bindEvents: function() { // Navigation Handler window.addEventListener('hashchange', this.onHashChange, false); // Device Handler window.isphone = false; if(document.URL.indexOf("http://") === -1 && document.URL.indexOf("https://") === -1) { window.isphone = true; } // Phone Context if( window.isphone ) { document.addEventListener("deviceready", this.onDeviceReady, false); // Testing Context } else { this.onDeviceReady(); } }, // Device ready callback onDeviceReady: function() { var request = new XMLHttpRequest(); request.open('GET', '../index.json'); request.onload = function() { if (request.status != 200) { /* this should never happen */ app.utils.setLoadingMsg("Initialisation de l'application : erreur de chargement"); } else { var new_manifest = JSON.parse(this.responseText); for (var attr in new_manifest.meta) { if (new_manifest.meta.hasOwnProperty(attr)) { app.manifest.meta[attr] = new_manifest.meta[attr]; } } app.manifest.title = new_manifest.title; app.manifest.content = new_manifest.content; app.manifest.pages = new_manifest.pages; app.manifest.footer = new_manifest.footer; app.onDefaultManifestLoaded(); } }; request.send(); }, onDefaultManifestLoaded: function() { app.initApplication(); }, initApplication: function() { // Init fileSystem app.initFileSystem(); // Backup assets app.backupAssets(); // Init search engine index app.initIndex(); // Load manifest from localStorage app.loadLocalManifest(); // Check for new updates //app.checkForUpdate(app.start, app.start); // Regulary check for connection setInterval( app.checkConnection, CHECK_FOR_CONNECTION_INTERVAL ); // Update Reminder app.updateTimeout = setTimeout( app.checkForLastUpdateCheck, app.manifest.meta.updateFreq ); // Touch events faster response patch FastClick.attach(document.body); // Start Application app.start(); }, initFileSystem: function(){ // Chrome patch window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem; // Register rootPath if(typeof FileTransfer !== 'undefined' && typeof zip !== 'undefined' && typeof window.requestFileSystem === 'function'){ if(DEBUG){ console.log('FileSystem access'); } app.rootPath = cordova.file.dataDirectory; } else { if(DEBUG){ console.log('FileSystem unavaible'); } app.rootPath = DEBUG_WWW_URL; } }, // Backup permanent assets backupAssets: function(){ var els, i, l; els = document.getElementsByTagName("script"); for(i = 0, l = els.length; i < l; i++) { app.javascripts.push(els[i].src); } els = document.getElementsByTagName("link"); for(i = 0, l = els.length; i < l; i++) { app.stylesheets.push(els[i].href); } }, // Initialize search engine index initIndex: function(){ app.index = lunr(function () { this.use(lunr.fr); this.field('title', {boost: 10}); this.field('keywords', {boost: 5}); this.field('content'); this.ref('id'); this.pipeline.add(function (token, tokenIndex, tokens) { if(token.length > 2){ return app.utils.replaceAccents(token); } }); }); }, checkConnection: function(resolve, reject){ try { switch(navigator.network.connection.type){ case Connection.ETHERNET: case Connection.WIFI: case Connection.CELL_4G: case Connection.CELL_3G: app.online = true; if(typeof resolve === "function"){ resolve(); } break; case Connection.CELL_2G: case Connection.NONE: case Connection.UNKNOWN: default: app.online = false; if(typeof reject === "function"){ reject(); } break; } } catch(e) { if(navigator.onLine){ app.online = true; if(typeof resolve === "function"){ resolve(); } } else { app.online = false; if(typeof reject === "function"){ reject(); } } } // Hide offline specific elements var i, elements; elements = document.getElementsByClassName("offline-hidden"); for (i = 0; i < elements.length; i++){ if(app.online){ elements[i].classList.remove('hidden'); } else { elements[i].classList.add('hidden'); } } // Show offline specific elements var i, elements; elements = document.getElementsByClassName("offline-visible"); for (i = 0; i < elements.length; i++){ if(app.online){ elements[i].classList.add('hidden'); } else { elements[i].classList.remove('hidden'); } } // Disable offline specific elements elements = document.getElementsByClassName("offline-disable"); for (i = 0; i < elements.length; i++){ if(app.online){ elements[i].classList.remove('disabled'); } else { elements[i].classList.add('disabled'); } } return app.online; }, // Check for last update check checkForLastUpdateCheck: function(resolve, reject){ // Checklast Update var lastUpdate = localStorage.getItem("momo-timestamp") ? new Date(localStorage.getItem("momo-timestamp")) : new Date(0); var timeDiff = ((new Date()).getTime() - lastUpdate.getTime()); var updateRequired = timeDiff > app.manifest.meta.updateFreq; if(updateRequired){ app.flash("Vérifiez si une mise à jour est disponible en tirant la page vers le bas", "info"); } if(typeof resolve === 'function'){ resolve(updateRequired); } }, checkForUpdate: function(resolve, reject) { app.utils.setLoadingMsg("Vérification des mises à jour"); app.checkConnection(); var manifestReady = false; var assetsReady = false; var updateAvailable = false; var updateError = false; if(!app.online){ var msg = "Impossible de détecter une mise à jour car votre appareil n'est pas connecté à Internet. "; var updatedAt = localStorage.getItem('momo-updated-at'); if(updatedAt){ msg += "Pour information, la dernière mise à jour date du " + app.utils.formatDate(new Date(updatedAt)) + "."; } app.utils.setLoadingMsg("Application hors-ligne"); app.flash(msg, 'danger'); if(typeof resolve === 'function'){ resolve(false); } return; } var onGetMtime = function(key, mtime, ready){ var old_mtime = localStorage.getItem("momo-"+key+"-mtime"); if (mtime) { if(mtime != old_mtime) { updateAvailable = true; } } else { updateError = true; } if(ready){ if(updateError){ if(DEBUG){ console.error('Error checking for updates'); } app.flash("Impossible de détecter des nouvelles mises à jour", 'danger'); if(typeof reject === 'function'){ reject(); } if(typeof resolve === 'function'){ resolve(false); } } else { /* record that a check for update was succesfully done */ localStorage.setItem("momo-timestamp", (new Date()).toString()); if(updateAvailable){ app.flash(tmpl('momo-update-available-tmpl', { mtime: app.utils.formatDate(mtime) }), 'success'); app.utils.setLoadingMsg("Mise à jour disponible !"); } else { app.utils.setLoadingMsg("Aucune nouvelle mises à jour"); } if(typeof resolve === 'function'){ resolve(updateAvailable); } } } }; app.utils.getModifiedTime(app.manifest.meta.manifestUrl, function(mtime) { app.manifestMtime = mtime; onGetMtime('manifest', mtime, assetsReady); manifestReady = true; }); app.utils.getModifiedTime(app.manifest.meta.assetsUrl, function(mtime) { app.assetsMtime = mtime; onGetMtime('assets', mtime, manifestReady); assetsReady = true; }); }, loadLocalManifest: function() { // In case the url is incorrect, we get the backup manifest app.safeManifest = app.manifest; var manifest = localStorage.getItem("momo-manifest"); if(manifest){ try { app.manifest = JSON.parse(manifest); // Override meta app.manifest.meta = app.utils.extend(app.safeManifest.meta, app.manifest.meta); } catch(e) { app.manifest = app.safeManifest; } } }, // JSON Manifest loading function loadManifest: function(resolve, reject){ if(DEBUG){ console.log('load '+JSON.stringify(app.manifest)); } app.utils.setLoadingMsg("Mise à jour du manifest - 0%"); // Get manifest from localStorage if it exists app.loadLocalManifest(); // Start AJAX var url = app.manifest.meta.manifestUrl; var request = new XMLHttpRequest(); request.open('GET', app.utils.addParameter(url, 'timestamp', (+new Date()), true), true); // AJAX Callback request.onload = function() { // AJAX success if (request.status >= 200 && request.status < 400 || request.status === 0 /* iOS OhMyBuddha!! */) { app.utils.setLoadingMsg("Mise à jour du manifest - 99%"); // Patch raw text response for fileSystem relative paths app.patchResponse(request.responseText, function(manifestResponse){ app.utils.setLoadingMsg("Mise à jour du manifest - 100%"); try { // Override current manifest app.manifest = JSON.parse(manifestResponse); // Override meta app.manifest.meta = app.utils.extend(app.safeManifest.meta, app.manifest.meta); // Store manifest if parsable localStorage.setItem("momo-manifest", manifestResponse); // Reload if new manifest url if(url !== app.manifest.meta.manifestUrl){ app.loadManifest(resolve, reject); } else { if(typeof resolve === "function"){ resolve(); } } } catch(e) { if(DEBUG){ console.log('Cannot parse application manifest '+url); } app.flash('Le manifest JSON comporte des erreurs', 'danger'); app.manifest = app.safeManifest; if(typeof reject === "function"){ reject(); } } }); // Handle AJAX Error } else { app.onAjaxError(url, request); if(typeof reject === "function"){ reject(); } } }; // Handle AJAX Error request.onerror = function() { if(typeof reject === "function"){ reject(); } app.onAjaxError(url); }; // Send AJAX request.send(); }, // Start Application with safe manifest onAjaxError: function(url, request){ if(DEBUG){ console.log("Cannot load "+url+" [Error "+(request ? request.status : 'Unknown')+"]. Loading local manifest instead."); } // Store proper manifest localStorage.setItem("momo-manifest", JSON.stringify(app.safeManifest)); // Restore safe manifest app.manifest = app.safeManifest; }, // Patch manifest response to set fileSystem's relative paths (offline) patchResponse: function(response, cb){ var patch = function(jsonText, path) { return jsonText.replace(/(['"\(])\/?(assets\/[^'"\)]*)(['"\)])/g, function(match, q1, p, q2){ return q1+path+p+q2; }); }; // Phone context requires 'FileTransfer' & 'Zip' plugins if(typeof FileTransfer !== 'undefined' && typeof zip !== 'undefined' && typeof window.requestFileSystem === 'function'){ if(DEBUG){ console.log('FileSystem access'); } var onFileSystemGet = function(fileSystem){ // Get fileSystem's relative cache folder var rootPath = app.rootPath = cordova.file.dataDirectory; // Callback cb(patch(response, rootPath)); }; try { window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, onFileSystemGet, function(err){ if(DEBUG){ console.log('FileSystem unreachable'); } app.flash("Impossible d'écrire sur le périphérique", 'danger'); cb(patch(response, DEBUG_WWW_URL)); }); } catch(e) { if(DEBUG){ console.log('FileSystem error'+e.message); } cb(patch(response, DEBUG_WWW_URL)); } } else { if(DEBUG){ console.log('FileSystem unavaible'); } cb(patch(response, DEBUG_WWW_URL)); } }, // Get distant zip asset archive and update local cache loadAssets: function(resolve, reject){ if(DEBUG){ console.log('fetch assets'); } app.utils.setLoadingMsg("Téléchargement des assets - 0%"); var onFileSystemGet = function(fileSystem){ var rootPath = app.rootPath = cordova.file.dataDirectory; var fileTransfer = new FileTransfer(); var uri = encodeURI(app.manifest.meta.assetsUrl); var filePath = fileSystem.root.toURL() + uri.substr(uri.lastIndexOf("/") + 1); // Fetch Assets Zip Archive fileTransfer.download( // Source uri, // Destination filePath, // Success callback function(entry) { app.utils.setLoadingMsg("Extraction de la mise à jour"); // Unzip Assets zip.unzip(filePath, rootPath, // Success callback function(ret){ if(DEBUG){ console.log('unzip success'); } app.utils.setLoadingMsg("Téléchargement des assets - 100%"); if(ret === 0) { if(typeof resolve === 'function') { resolve(); } } else if(ret === -1) { if(typeof reject === 'function') { reject(); } } }, // Progress callback function(e){ var progress = Math.round((e.loaded / e.total) * 100); app.utils.setLoadingMsg("Téléchargement des assets - "+progress+"%"); } ); }, // Error callback function(error) { if(DEBUG){ console.log("download error source " + error.source); } if(DEBUG){ console.log("download error target " + error.target); } if(DEBUG){ console.log("upload error code" + error.code); } if(typeof reject === 'function'){ reject(); } }, // Misc false, { headers: {} } ); }; if(typeof FileTransfer !== 'undefined' && typeof zip !== 'undefined' && typeof window.requestFileSystem === 'function'){ try { window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, onFileSystemGet, function(err){ if(DEBUG){ console.error('Filesystem error'); } app.utils.setLoadingMsg("Erreur du système de fichier"); app.flash("Erreur du système de fichier", 'danger'); if(typeof reject === 'function'){ reject(); } }); } catch(e) { if(DEBUG){ console.log('FileSystem error : '+e.message); } if(typeof reject === 'function'){ reject(); } } } else { if(DEBUG){ console.error('Plugins "zip" & "file-transfer" not available (local mode ?)'); } app.utils.setLoadingMsg("Plugin 'zip' indisponible"); app.flash("Plugin 'zip' indisponible", 'danger'); if(typeof reject === 'function'){ reject(); } } }, refreshAssets: function(page){ var i, j, l, m, file, found, els, el; // Refresh scripts for(i = 0, l = page.javascripts.length; i < l; i++){ file = page.javascripts[i]; found = false; els = document.getElementsByTagName("script"); for (j = 0, m = els.length; j < m; j++) { el = els[j]; if (el.src === file) { found = true; } else if(page.javascripts.indexOf(el.src) < 0 && app.javascripts.indexOf(el.src) < 0){ el.parentNode.removeChild(el); m = els.length; j--; } } if(!found){ // Script tag var script = document.createElement("script"); script.type = "text/javascript"; script.src = file; document.getElementsByTagName("head")[0].appendChild(script); } } // Refresh stylesheets for(i = 0, l = page.stylesheets.length; i < l; i++){ file = page.stylesheets[i]; found = false; els = document.getElementsByTagName("link"); for (j = 0, m = els.length; j < m; j++) { el = els[j]; if (el.href === file) { found = true; } else if(page.stylesheets.indexOf(el.href) < 0 && app.stylesheets.indexOf(el.href) < 0){ el.parentNode.removeChild(el); m = els.length; j--; } } if(!found){ // Link Tag var link = document.createElement("link"); link.type = "text/css"; link.rel = "stylesheet"; link.href = file; document.getElementsByTagName("head")[0].appendChild(link); } } }, appendAssets: function(resolve, reject){ if(typeof resolve === 'function'){ resolve(); } return; //var append = function(rootPath){ // // Link Tag // var link = document.createElement("link"); // link.type = "text/css"; // link.rel = "stylesheet"; // link.href = rootPath+"assets/index.css"; // // Script tag // var script = document.createElement("script"); // script.type = "text/javascript"; // script.src = rootPath+"assets/index.js"; // // Delete previous link tags // var els = document.getElementsByTagName("link"), // els_length = els.length; // for (var i = 0, l = els_length; i < l; i++) { // var el = els[i]; // if (el.href === link.href) { // delete el; // } // } // // Delete previous scripts tags // els = document.getElementsByTagName("script"); // els_length = els.length; // for (var i = 0, l = els_length; i < l; i++) { // var el = els[i]; // if (el.src === script.src) { // delete el; // } // } // // Reinsert // document.getElementsByTagName("head")[0].appendChild(link); // document.getElementsByTagName("head")[0].appendChild(script); // if(typeof resolve === 'function') // resolve(); //}; //if(typeof FileTransfer !== 'undefined' && typeof zip !== 'undefined'){ // window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, function (fileSystem) { // // Get fileSystem's relative cache folder // var rootPath = app.rootPath = fileSystem.root.toURL(); // append(rootPath); // }); //} else { // append(DEBUG_WWW_URL); //} }, // Application starter start: function(resolve, reject){ if(DEBUG){ console.log('start '+JSON.stringify(app.manifest)); } app.utils.setLoadingMsg("Démarrage de l'application"); app.hasStarted = true; /* if there's no footer, fallback to the default */ if (typeof app.manifest.footer === 'undefined') { app.manifest.footer = tmpl('momo-default-footer-tmpl', app.manifest); } // Default route to home app.manifest.id = app.currentPage = 'home'; // Dev page refresh : redirect to home window.location.replace('#home'); // Regiter pages tree app.registerPage(app.manifest, app.defaultPage); // Render Homepage app.render(app.manifest); // Navigate to home app.navigate('home', false, function(){ // Reset history app.pageIndex = 0; // Append assets app.appendAssets(resolve, reject); }); // Listen for search form submission var $form = document.getElementById('momo-search'); $form.addEventListener('submit', app.onSearchSubmit, false); }, // Recursive function to index page from JSON Manifest registerPage: function(data, parentPage){ if(data instanceof Object){ // Cache orginal page data.original = JSON.parse(JSON.stringify(data).replace(new RegExp(app.rootPath, 'g'), "")); // Extends default page data = app.utils.extend(app.defaultPage, data); // Set page's parent data.parent = parentPage; // Generate a page ID var id = data.id = data.id ? data.id : (data.title ? app.utils.hyphenate(data.title.stripTags()) : '_' + Math.random().toString(36).substr(2, 9)); // Make sure id is unique var i = 0, page; while(app.pages.hasOwnProperty(data.id)){ data.id = id+'_'+(i++).toString(); } // Register page app.pages[data.id] = data; // Register menu items if(data.menu instanceof Array && data.menu.length > 0){ for(i = 0; i < data.menu.length; i++){ page = data.menu[i]; if(page instanceof Object){ app.pages[data.id].menu[i] = page.id; } app.pages[data.id].menu[i] = app.registerPage(page, app.pages[data.id]); } } else { app.pages[data.id].menu = parentPage.menu || []; } app.pages[data.id].menu = app.manifest.meta.menu.concat(app.pages[data.id].menu).unique(); // Register javascript items if(data.javascripts instanceof Array && data.javascripts.length > 0){ app.pages[data.id].javascripts = data.javascripts; } else { app.pages[data.id].javascripts = parentPage.javascripts || []; } app.pages[data.id].javascripts = app.manifest.meta.javascripts.concat(app.pages[data.id].javascripts).unique(); // Register stylesheets items if(data.stylesheets instanceof Array && data.stylesheets.length > 0){ app.pages[data.id].stylesheets = data.stylesheets; } else { app.pages[data.id].stylesheets = parentPage.stylesheets || []; } app.pages[data.id].stylesheets = app.manifest.meta.stylesheets.concat(app.pages[data.id].stylesheets).unique(); // Register hidden pages childrens if(data._pages instanceof Array){ for(i = 0; i < data._pages.length; i++){ page = data._pages[i]; if(page instanceof Object){ app.pages[data.id]._pages[i] = page.id; } app.pages[data.id]._pages[i] = app.registerPage(page, app.pages[data.id]); } } // Register page childrens if(data.pages instanceof Array){ var images = []; for(i = 0; i < data.pages.length; i++){ page = data.pages[i]; if(page instanceof Object){ app.pages[data.id].pages[i] = page.id; } page = app.pages[data.id].pages[i] = app.registerPage(page, app.pages[data.id]); if(app.pages[page] && app.pages[page].url && app.pages[page].url.isImage() && !app.pages[page].external){ images.push(page); } } // Build Gallery if(images.length){ if(images.length > 1 && images[0] === images[images.length - 1]){ app.pages[data.id].pages.pop(); } for(i = 0; i < images.length; i++){ if(i > 0){ app.pages[images[i]].prev = images[i-1]; } if(i < images.length-1){ app.pages[images[i]].next = images[i+1]; } } } } // Register seealso childrens if(data.seealso instanceof Array){ for(i = 0; i < data.seealso.length; i++){ page = data.seealso[i]; if(page instanceof Object){ app.pages[data.id].seealso[i] = page.id; } app.pages[data.id].seealso[i] = app.registerPage(page, app.pages[data.id]); } } // Index page for search engine app.index.add({ id: data.id, title: data.title, body: data.content, keywords: data.keywords }); // Return ID string return data.id; } else if(typeof data === 'string' || data instanceof String || data instanceof Number){ // Return ID string return data; } }, // Render Application render: function(data){ // Render Main section var $main = tmpl("momo-main-tmpl", data); document.getElementById('momo-main').innerHTML = $main; // Render every page for(var page in app.pages){ if(app.pages.hasOwnProperty(page)){ var $page = document.createElement('div'); $page.id = app.pages[page].id; $page.className = "momo-page"; //if($page.id == 'home'){ // $page.classList.add('momo-page-current'); // $page.innerHTML = app.renderPage(app.pages[page]); // document.title = app.pages[page].title; //} document.getElementById('momo-pages').appendChild($page); } } }, // Render Page renderPage: function(page, forceMenu){ if(page instanceof Object){ if(DEBUG){ console.log('render page '+JSON.stringify(page)); } // Refresh assets app.refreshAssets(page); // Render every menu items document.getElementById('momo-menu').innerHTML = ""; for(var i = 0, l = page.menu.length; i < l; i++){ var $menuItem = document.createElement('li'); var _data = app.utils.extend(app.pages[page.menu[i]], { header: true }); $menuItem.innerHTML = tmpl("momo-list-item-tmpl", _data); document.getElementById('momo-menu').appendChild($menuItem); } // Render navigation if(page.menu !== app.pages[app.currentPage].menu || !app.nav || forceMenu){ if(app.nav){ app.nav.destroy(); } app.nav = responsiveNav(".momo-nav-collapse", { // Selector animate: true, // Boolean: Use CSS3 transitions, true or false transition: 284, // Integer: Speed of the transition, in milliseconds //label: "Menu", // String: Label for the navigation toggle //insert: "before", // String: Insert the toggle before or after the navigation customToggle: "momo-menu-toggle", // Selector: Specify the ID of a custom toggle closeOnNavClick: true, // Boolean: Close the navigation when one of the links are clicked openPos: "relative", // String: Position of the opened nav, relative or static navClass: "nav-collapse", // String: Default CSS class. If changed, you need to edit the CSS too! navActiveClass: "active", // String: Class that is added to element when nav is active jsClass: "js", // String: 'JS enabled' class which is added to element init: function(){}, // Function: Init callback open: function(){}, // Function: Open callback close: function(){} // Function: Close callback }); } // Cleanup app.nav.unbindEvents(); app.nav.bindEvents(); // Get data to display var data = app.utils.extend(page, {meta: app.manifest.meta}); // Change page header document.getElementById("momo-header").innerHTML = tmpl("momo-header-tmpl", data); // Render inner template if(data.template){ var script = document.createElement("script"); script.type = "text/x-tmpl"; script.id = data.id+"-tmpl"; script.innerHTML = data.template; document.getElementById('momo-templates').innerHTML = ""; document.getElementById('momo-templates').appendChild(script); data.content = tmpl(data.id+"-tmpl", data) + (data.content || ""); } // Render Page return tmpl("momo-page-tmpl", data); } else if(typeof page === 'string' || page instanceof String || page instanceof Number){ if(DEBUG){ console.log('render page '+page); } return app.renderPage(app.pages[page]); } }, // Navigate to page navigate: function(page, back, cb, force){ var page_obj = app.pages[page]; if(page_obj.external && back){ window.history.back(); return false; } else if(page_obj.external){ app.utils.openExternalURL(page_obj.url, page_obj.inAppBrowser); return false; } // Keep try to navigate until previous animation is done. if(app.isAnimating) { setTimeout(function(){ app.navigate(page, back, cb); }, 100); return false; } app.isAnimating = true; document.title = page_obj.title; var $outpage = document.getElementById(app.currentPage); var $inpage = document.getElementById(page); // Render page (with small hack, so it doesn't mess up the display) $inpage.innerHTML = app.renderPage(page_obj, force); // Check for connection app.checkConnection(); // Pull to update binder app.bindPagePull($inpage); if(!ANIMATION_ENABLED || app.currentPage === page){ $inpage.classList.add('momo-page-current'); app.onAnimationEnd($outpage, $inpage, back, cb); } else { var outCb = function(){ $outpage.removeEventListener('animationend', outCb); $outpage.removeEventListener('webkitAnimationEnd', outCb); $outpage.removeEventListener('oAnimationEnd', outCb); $outpage.removeEventListener('MSAnimationEnd', outCb); app.endCurrPage = true; if(app.endNextPage){ app.onAnimationEnd($outpage, $inpage, back, cb); } }; var inCb = function(){ $inpage.removeEventListener('animationend', inCb); $inpage.removeEventListener('webkitAnimationEnd', inCb); $inpage.removeEventListener('oAnimationEnd', inCb); $inpage.removeEventListener('MSAnimationEnd', inCb); app.endNextPage = true; if(app.endCurrPage){ app.onAnimationEnd($outpage, $inpage, back, cb); } }; var i; var out_classes = (back ? ANIMATION_BACK_OUT_CLASS : ANIMATION_OUT_CLASS).split(' '); for(i = 0; i < out_classes.length; i++){ $outpage.classList.add(out_classes[i]); } $outpage.addEventListener('animationend', outCb, false); $outpage.addEventListener('webkitAnimationEnd', outCb, false); $outpage.addEventListener('oAnimationEnd', outCb, false); $outpage.addEventListener('MSAnimationEnd', outCb, false); $inpage.classList.add('momo-page-current'); var in_classes = (back ? ANIMATION_BACK_IN_CLASS : ANIMATION_IN_CLASS).split(' '); for(i = 0; i < in_classes.length; i++){ $inpage.classList.add(in_classes[i]); } $inpage.addEventListener('animationend', inCb, false); $inpage.addEventListener('webkitAnimationEnd', inCb, false); $inpage.addEventListener('oAnimationEnd', inCb, false); $inpage.addEventListener('MSAnimationEnd', inCb, false); } app.currentPage = page; }, bindPagePull: function($page) { WebPullToRefresh.destroy(); WebPullToRefresh.init( { loadingFunction: function(){ if(ON_PULL === "checkForUpdate"){ return new Promise( app.checkForUpdate ); } else if(ON_PULL === "update"){ return new Promise( app.update ); } }, contentEl: $page } ); }, // Animation Callback onAnimationEnd: function($outpage, $inpage, back, cb) { app.endCurrPage = false; app.endNextPage = false; var i; var out_classes = (back ? ANIMATION_BACK_OUT_CLASS : ANIMATION_OUT_CLASS).split(' '); for(i = 0; i < out_classes.length; i++){ $outpage.classList.remove(out_classes[i]); } if($outpage !== $inpage){ $outpage.classList.remove('momo-page-current'); } var in_classes = (back ? ANIMATION_BACK_IN_CLASS : ANIMATION_IN_CLASS).split(' '); for(i = 0; i < in_classes.length; i++){ $inpage.classList.remove(in_classes[i]); } app.isAnimating = false; if(typeof cb === "function"){ cb(); } }, onTouchStart: function(e) { e = e || window.event; var targ = e.target || e.srcElement; if (targ.nodeType === 3){ targ = targ.parentNode; } //return targ.onclick(); }, // Location Hash change event (sorry & good luck) onHashChange: function(e) { var hash = window.location.hash, length = window.history.length, page = window.location.hash.slice(1), prev, next; //console.warn(app.previousPage + " -> " + page); // Hack of the century ? if(app.ignoreHash){ return false; } if(document.body.classList.contains( 'ptr-back' )){ return false; } if(document.body.classList.contains( 'ptr-forward' )){ return false; } if(page === 'momo-update'){ //window.location.replace('#'+app.pageHistory[app.pageHistory.length-1]); window.replaceHash(app.pageHistory[app.pageHistory.length-1]); document.body.classList.add('ptr-loading'); var cb = function() { setTimeout(function(){ document.body.classList.remove('ptr-loading'); document.body.classList.add('ptr-reset'); setTimeout(function() { document.body.classList.remove('ptr-reset'); }, 250); }, 1000); }; app.update(cb, cb); app.ignoreHash = true; window.history.go(-1); app.ignoreHash = false; app.previousPage = page; return false; } else if(page === 'momo-blank'){ //return window.location.replace(app.pageHistory[app.pageHistory.length-1]); //return window.history.back(); app.ignoreHash = true; window.history.go(-1); app.ignoreHash = false; app.previousPage = page; return false; } else if(page === 'momo-back'){ prev = app.pages[app.currentPage].prev; if(prev){ page = prev; window.replaceHash(app.parentPage); window.history.go(-1); app.navigate(page, true); //app.ignoreHash = true; //window.location.hash = "#"+page; //app.ignoreHash = false; } else { if(app.previousPage === 'momo-forward'){ window.history.go(-1); } else { window.history.go(-2); } } //window.replaceHash(page); app.previousPage = page; return false; } else if(page === 'momo-forward'){ prev = app.pages[app.currentPage].prev; next = app.pages[app.currentPage].next; if(next){ page = next; window.replaceHash(app.parentPage); window.history.go(-1); app.navigate(page, false); //window.replaceHash(page); //app.ignoreHash = true; //window.location.hash = "#"+page; //app.ignoreHash = false; } else if(prev) { window.replaceHash(app.parentPage); window.history.go(-1); } else { if(app.previousPage === 'momo-back'){ window.history.go(-1); } else { page = app.pageIndex+1 < app.pageHistory.length ? app.pageHistory[app.pageIndex+1] : app.pageHistory[app.pageHistory.length - 1]; if(page !== app.currentPage){ window.location.hash = "#"+app.pageHistory[app.pageHistory.length-1]; } else { window.history.go(-1); } } } //window.replaceHash(page); app.previousPage = 'momo-forward'; return false; } if(!app.pages.hasOwnProperty(page)){ page = 'home'; } var back = page === 'home'; //console.log(app.pageHistory.join(',')+ " (index = "+app.pageIndex+")"); if (app.pageHistory.length/* && app.historyLength == length*/) { // Goind Back if (app.pageHistory[app.pageIndex - 1] === page) { //app.pageHistory = app.pageHistory.slice(0, app.pageIndex); back = true; app.pageIndex--; } else // Going Forward if(app.pageIndex+1 < app.pageHistory.length && app.pageHistory[app.pageIndex + 1] === page) { //app.pageHistory = app.pageHistory.slice(0, app.pageIndex); app.pageIndex++; } else { // Going to new page app.pageHistory = app.pageHistory.slice(0, app.pageIndex+1); if(page !== app.pageHistory[app.pageHistory.length-1]){ app.pageHistory.push(page); } app.pageIndex = app.pageHistory.length - 1; app.historyLength = length; } } else { app.pageHistory.push(page); } if(app.currentPage !== page){ if(!app.pages[page].external || app.pages[page].external && app.online){ app.navigate(page, back); } else { window.history.go(-1); } } app.parentPage = page; app.previousPage = page; return false; }, onSearchSubmit: function(e){ // Stop form default action e.preventDefault(); //e.stopPropagation(); // Get search input var $searchInput = document.getElementById('momo-search-input'); var searchInput = $searchInput.value; // Close navigation + Keyboard app.nav.close(); $searchInput.value = ''; $searchInput.blur(); // Search app.search(searchInput); return false; }, search: function(query){ var id = "search-"+app.utils.hyphenate(query); // Store current query app.current_query = query; // Get search results var results = app.index.search(app.utils.replaceAccents(query)); // Register new search results page app.pages[id] = app.utils.extend(app.defaultPage, { id: id, search: query, icon: "fa fa-search", title: 'Recherche "'+query+'"', content: results.length ? null : '
Aucun résultat
', pages: results.map(function(item){ return item.ref; }), menu: app.pages[app.currentPage].menu }); var $page = document.getElementById(id); // If search query doesn't exist if(!$page){ // Generate result page view $page = document.createElement('div'); $page.id = id; $page.className = "momo-page"; document.getElementById('momo-pages').appendChild($page); } // Render page $page.innerHTML = app.renderPage(app.pages[id], app.pages[app.currentPage]); // Navigate to search result page window.location.hash = "#"+id; }, flash: function(message, type) { var elements = document.getElementsByClassName("momo-flash-messages"); var i; for (i = 0; i < elements.length; i++){ elements[i].innerHTML = ""; } setTimeout(function(){ var elements = document.getElementsByClassName("momo-flash-messages"); for (i = 0; i < elements.length; i++){ elements[i].innerHTML += tmpl('momo-flash-message-tmpl', { message: message, type: type ? type : 'info' }); } }, 200); }, update: function( resolve, reject ) { var els = document.getElementsByTagName("a"), els_length = els.length; for (var i = 0, l = els_length; i < l; i++) { var el = els[i]; if (el.href.split("#")[1] === 'momo-update') { el.classList.add('disabled'); } } var appendAssets = function(_resolve, _reject){ app.appendAssets( function(){ app.reset(_resolve, _reject); }, function(){ app.reset(_reject, _reject); } ); }; var loadAssets = function(_resolve, _reject){ app.loadAssets( function(){ appendAssets(_resolve, _reject); }, function(){ app.reset(_reject, _reject); } ); }; app.loadManifest( function(){ localStorage.setItem('momo-manifest-mtime', app.manifestMtime); localStorage.setItem('momo-assets-mtime', app.assetsMtime); localStorage.setItem('momo-updated-at', new Date()); loadAssets(resolve, reject); }, function(){ loadAssets(resolve, reject); } ); }, reset: function(resolve, reject){ // Update Reminder clearTimeout(app.updateTimeout); app.updateTimeout = setTimeout( app.checkForLastUpdateCheck, app.manifest.meta.updateFreq ); var page, $page; for(page in app.pages){ if(app.pages.hasOwnProperty(page)){ if(typeof app.pages[page].search === 'undefined'){ delete app.pages[page]; $page = document.getElementById(page); if($page){ $page.parentNode.removeChild($page); } } } } // Regiter pages tree app.manifest.id = 'home'; app.registerPage(app.manifest, app.defaultPage); // Render every page for(page in app.pages){ if(app.pages.hasOwnProperty(page)){ $page = document.getElementById(page); if(!$page){ $page = document.createElement('div'); $page.id = app.pages[page].id; $page.className = "momo-page"; document.getElementById('momo-pages').appendChild($page); } } } // Check for url change in manifest //app.checkForUpdate(function(updateAvailable){ // if(updateAvailable) // app.update(resolve, reject); // else app.utils.setLoadingMsg("Mise à jour effectuée !"); if(typeof resolve === 'function'){ resolve(); } //}, function(){ // if(typeof reject === 'function') // reject(); //}); if(typeof app.pages[app.currentPage].search !== 'undefined'){ app.search(app.current_query); } else { app.navigate(app.currentPage, false, null, true); } }, // Various Javascript Helpers utils: { removeElement: function($el){ $el.parentElement.removeChild($el); return false; }, setLoadingMsg: function(text){ var elements = document.getElementsByClassName("momo-loading-text"); for (var i = 0; i < elements.length; i++){ elements[i].innerHTML = text; } }, onExternalLinkClick: function(e){ e = e || window.event; var targ = e.target || e.srcElement; if (targ.nodeType === 3){ targ = targ.parentNode; } var url = targ.getAttribute("href"); app.utils.openExternalURL(url); return false; }, openExternalURL: function(url, inAppBrowser){ //if(inAppBrowser){ // cordova.InAppBrowser.open(url); //} else { if(navigator.app){ // Android navigator.app.loadUrl(encodeURI(url), { openExternal:true }); } else { // iOS and others window.open(encodeURI(url), "_system", 'location=yes'); // opens in the app, not in safari } //} return false; }, extend: function ( defaults, options ) { var extended = {}; var prop; for (prop in defaults) { if (Object.prototype.hasOwnProperty.call(defaults, prop)) { extended[prop] = defaults[prop]; } } for (prop in options) { if (Object.prototype.hasOwnProperty.call(options, prop)) { extended[prop] = options[prop]; } } return extended; }, trim: function(str){ return (str || '').replace(/^\s+|\s+$/g, ''); }, removeNonWord: function(str){ return (str || '').replace(/[^0-9a-zA-Z\xC0-\xFF \-]/g, ''); //remove non-word chars }, replaceAccents: function(str){ str = str || ''; // verifies if the String has accents and replace them if (str.search(/[\xC0-\xFF]/g) > -1) { str = str .replace(/[\xC0-\xC5]/g, "A") .replace(/[\xC6]/g, "AE") .replace(/[\xC7]/g, "C") .replace(/[\xC8-\xCB]/g, "E") .replace(/[\xCC-\xCF]/g, "I") .replace(/[\xD0]/g, "D") .replace(/[\xD1]/g, "N") .replace(/[\xD2-\xD6\xD8]/g, "O") .replace(/[\xD9-\xDC]/g, "U") .replace(/[\xDD]/g, "Y") .replace(/[\xDE]/g, "P") .replace(/[\xE0-\xE5]/g, "a") .replace(/[\xE6]/g, "ae") .replace(/[\xE7]/g, "c") .replace(/[\xE8-\xEB]/g, "e") .replace(/[\xEC-\xEF]/g, "i") .replace(/[\xF1]/g, "n") .replace(/[\xF2-\xF6\xF8]/g, "o") .replace(/[\xF9-\xFC]/g, "u") .replace(/[\xFE]/g, "p") .replace(/[\xFD\xFF]/g, "y"); } return str; }, slugify: function(str, delimeter){ if (delimeter === null) { delimeter = "-"; } str = app.utils.replaceAccents(str); str = app.utils.removeNonWord(str); str = app.utils.trim(str) //should come after removeNonWord .replace(/ +/g, delimeter) //replace spaces with delimeter .toLowerCase(); return str; }, unCamelCase: function(str){ return (str || '').replace(/([a-z\xE0-\xFF])([A-Z\xC0\xDF])/g, '$1 $2').toLowerCase(); //add space between camelCase text }, hyphenate: function(str){ str = app.utils.unCamelCase(str); return app.utils.slugify(str, "-"); }, addParameter: function(url, parameterName, parameterValue, atStart/*Add param before others*/){ var replaceDuplicates = true; var cl, urlhash, sourceUrl; if(url.indexOf('#') > 0){ cl = url.indexOf('#'); urlhash = url.substring(url.indexOf('#'),url.length); } else { urlhash = ''; cl = url.length; } sourceUrl = url.substring(0,cl); var urlParts = sourceUrl.split("?"); var newQueryString = ""; if (urlParts.length > 1) { var parameters = urlParts[1].split("&"); for (var i=0; (i < parameters.length); i++) { var parameterParts = parameters[i].split("="); if (!(replaceDuplicates && parameterParts[0] === parameterName)) { if (newQueryString === ""){ newQueryString = "?"; } else { newQueryString += "&"; } newQueryString += parameterParts[0] + "=" + (parameterParts[1]?parameterParts[1]:''); } } } if (newQueryString === ""){ newQueryString = "?"; } if(atStart){ newQueryString = '?'+ parameterName + "=" + parameterValue + (newQueryString.length>1?'&'+newQueryString.substring(1):''); } else { if (newQueryString !== "" && newQueryString !== '?'){ newQueryString += "&"; } newQueryString += parameterName + "=" + (parameterValue?parameterValue:''); } return urlParts[0] + newQueryString + urlhash; }, getModifiedTime: function(url, callback) { var xhr = new XMLHttpRequest(); xhr.open('HEAD', url, true); // use HEAD - we only need the headers // AJAX Callback xhr.onload = function() { // AJAX success if (xhr.status >= 200 && xhr.status < 400 || xhr.status === 0 /* iOS OhMyBuddha!! */) { var mtime = new Date(xhr.getResponseHeader('Last-Modified')); if (mtime.toString() === 'Invalid Date') { //app.onAjaxError(url); callback(); // dont want to return a bad date } else { callback(mtime); } // AJAX error } else { //app.onAjaxError(url); callback(); // dont want to return a bad date } }; // Handle AJAX Error //xhr.onerror = function() { // //app.onAjaxError(url); // callback(); //}; xhr.send(); }, formatDate: function(date){ var d = date; var months = ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre']; var days = ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi']; return days[d.getDay()] + ' ' + d.getDate() + ' ' + months[d.getMonth()] + ' ' + d.getFullYear() + ' ' + d.toLocaleTimeString(); }, clone: function (obj){ var copy; // Handle the 3 simple types, and null or undefined if (null === obj || "object" !== typeof obj){ return obj; } // Handle Date if (obj instanceof Date) { copy = new Date(); copy.setTime(obj.getTime()); return copy; } // Handle Array if (obj instanceof Array) { copy = []; for (var i = 0, len = obj.length; i < len; i++) { copy[i] = app.utils.clone(obj[i]); } return copy; } // Handle Object if (obj instanceof Object) { copy = {}; for (var attr in obj) { if (obj.hasOwnProperty(attr)){ copy[attr] = app.utils.clone(obj[attr]); } } return copy; } throw new Error("Unable to copy obj! Its type isn't supported."); }, prettyJSON: function(json) { if (typeof json !== 'string') { json = JSON.stringify(json, undefined, 2); } json = json.replace(/&/g, '&').replace(//g, '>'); json = json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { var cls = 'number'; if (/^"/.test(match)) { if (/:$/.test(match)) { cls = 'key'; } else { cls = 'string'; } } else if (/true|false/.test(match)) { cls = 'boolean'; } else if (/null/.test(match)) { cls = 'null'; } return '' + match + ''; }); return '
'+json+'
'; } } }; (function(namespace) { // Closure to protect local variable "var hash" if ('replaceState' in history) { // Yay, supported! namespace.replaceHash = function(newhash) { if ((''+newhash).charAt(0) !== '#'){ newhash = '#' + newhash; } history.replaceState('', '', newhash); }; } else { var hash = location.hash; namespace.replaceHash = function(newhash) { if (location.hash !== hash){ history.back(); } location.hash = newhash; }; } })(window); if (typeof Object.prototype.toHTML !== 'function') { Object.prototype.toHTML = function (){ return app.utils.prettyJSON(this); }; } if (typeof String.prototype.startsWith !== 'function') { String.prototype.startsWith = function (str){ return this.slice(0, str.length) === str; }; } if (typeof String.prototype.endsWith !== 'function') { String.prototype.endsWith = function (str){ return this.slice(-str.length) === str; }; } if (typeof String.prototype.stripTags !== 'function') { String.prototype.stripTags = function (){ return this.replace(/(<([^>]+)>)/ig,""); }; } if (typeof String.prototype.isImage !== 'function') { String.prototype.isImage = function (){ return this.endsWith('.png') || this.endsWith('.svg') || this.endsWith('.ico') || this.endsWith('.jpeg') || this.endsWith('.jpg'); }; } if (typeof String.prototype.isUrl !== 'function') { String.prototype.isUrl = function (){ return this.startsWith('http://') || this.startsWith('https://') || this.startsWith('file://'); }; } if (typeof String.prototype.isFramable != 'function') { String.prototype.isFramable = function (){ return this.isImage() || this.isUrl(); }; } if (typeof String.prototype.unique !== 'function') { Array.prototype.unique = function() { var a = this.concat(); for(var i=0; i