UNPKG

silex-website-builder-goodevent

Version:

Free and easy website builder for everyone.

357 lines (326 loc) 11.3 kB
/** * Silex, live web creation * http://projects.silexlabs.org/?/silex/ * * Copyright (c) 2012 Silex Labs * http://www.silexlabs.org/ * * Silex is available under the GPL license * http://www.silexlabs.org/silex/silex-licensing/ */ /** * @fileoverview Silex Dashboard / "new website" dialog which displays templates * */ import {FileInfo} from '../../types'; import {Model} from '../../types'; import {Controller} from '../../types'; import {ModalDialog} from '../ModalDialog'; import {TipOfTheDay} from '../tip-of-the-day'; /** * Silex Dashboard dialog * @class {silex.view.dialog.Dashboard} */ export class Dashboard { // define properties readyCbk: (() => any) = null; errorCbk: ((p1?: any) => any) = null; selected: {url: string, fileInfo: FileInfo} = null; // make this a dialog modalDialog: any; /** * flag set to 'success' when the template list is loaded * and set to 'error' when the loading failed */ state: string = ''; /** * @param element container to render the UI * @param model model class which holds * the model instances - views use it for * read operation only * @param controller structure which holds * the controller instances */ constructor(protected element: HTMLElement, protected model: Model, protected controller: Controller) { this.modalDialog = new ModalDialog({ name: 'Dashboard', element, onOpen: (args) => this.redraw(), onClose: () => {}, }); } /** * render the data loaded from github into a <ul> */ renderTemplateList( ul: HTMLElement, className: string, repo: string, data: any) { // // handle previously rendered elements // const elements = ul.querySelectorAll('li.rendered-item'); // for(let idx=0; idx<elements.length; idx++) { // const el = elements[idx]; // el.parentElement.removeChild(el); // } if (Array.isArray(data)) { // render the data // add the <li> tags to the <ul> tag // make a list of <li> tags data.map((item) => { const li = document.createElement('li'); li.classList.add('rendered-item', className); // thumbnail const thumbnail = document.createElement('div'); const templateFolder = `/libs/templates/${repo}/${item}`; thumbnail.classList.add('thumbnail'); thumbnail.style.backgroundImage = `url(${templateFolder}/screenshot.png)`; thumbnail.setAttribute( 'data-editable', `${templateFolder}/editable.html`); thumbnail.setAttribute('data-is-template', 'true'); li.appendChild(thumbnail); // UI container const ui = document.createElement('div'); ui.classList.add('ui'); li.appendChild(ui); // title const h3 = document.createElement('h3'); h3.innerHTML = item; h3.setAttribute('data-editable', `${templateFolder}/editable.html`); h3.setAttribute('data-is-template', 'true'); ui.appendChild(h3); // preview const previewEl = document.createElement('a'); previewEl.classList.add('fa', 'fa-external-link'); previewEl.innerHTML = 'Preview'; previewEl.setAttribute('data-action', 'preview'); previewEl.target = '_blank'; previewEl.href = `${templateFolder}/index.html`; ui.appendChild(previewEl); // info const infoEl = document.createElement('a'); infoEl.classList.add('fa', 'fa-info'); infoEl.innerHTML = 'Info'; infoEl.target = '_blank'; infoEl.href = `https://github.com/silexlabs/${repo}/blob/gh-pages/${ item}/README.md`; infoEl.setAttribute('data-action', 'info'); ui.appendChild(infoEl); // edit const editEl = document.createElement('a'); editEl.classList.add('fa', 'fa-pencil'); editEl.innerHTML = 'Select'; editEl.setAttribute( 'data-editable', `/libs/templates/${repo}/${item}/editable.html`); editEl.setAttribute('data-is-template', 'true'); ui.appendChild(editEl); return li; }) .forEach((li) => ul.appendChild(li)); } } /** * init the menu and UIs */ buildUi() { const createList = (ul, className, repo, success, error) => { const repoUrl = `/get/${repo}`; const oReq = new XMLHttpRequest(); oReq.addEventListener('error', (e) => { ul.innerHTML = 'It looks like you are offline. I could not load data from github issues'; success(); }); oReq.addEventListener('load', (e) => { const list = JSON.parse(oReq.responseText); this.renderTemplateList(ul, className, repo, list); success(); }); oReq.open('GET', repoUrl); oReq.send(); }; // click event const body = this.element.querySelector('.body') as HTMLElement; body.onclick = (e) => { // listen for a click in the list of recent files const a = (e.target as HTMLElement); // get the attribute of the link (<a> tag) // it might be in (e.target as HTMLElement).parentElement since there are <strong> tags in the // <a> const templateUrl = a.getAttribute('data-editable') || a.parentElement.getAttribute('data-editable'); const recentFileInfo = a.getAttribute('data-file-info') || a.parentElement.getAttribute('data-file-info'); if (!!templateUrl || !!recentFileInfo) { this.selected = { fileInfo: (JSON.parse(recentFileInfo) as FileInfo), url: templateUrl, }; // close the dialog, which will trigger a call the dialog onClose // callback and then this.selected will be used to open the selected // file this.modalDialog.close(); e.preventDefault(); return false; } }; const loadNext = (toLoad) => { if (toLoad.length > 0) { const item = toLoad.pop(); createList( this.element.querySelector(item.selector), item.className, item.repo, () => loadNext(toLoad), (e) => { this.state = 'error'; if (this.errorCbk) { this.errorCbk(e); } this.readyCbk = null; this.errorCbk = null; }); } else { this.state = 'ready'; if (this.readyCbk) { this.readyCbk(); } this.readyCbk = null; this.errorCbk = null; } }; const _toLoad = [ { selector: '.general-pane ul', repo: 'silex-templates', className: 'silex-templates', }, { selector: '.general-pane ul', repo: 'silex-blank-templates', className: 'silex-blank-templates', }, ]; loadNext(_toLoad); // clear button const clearBtn = this.element.querySelector('.clear-btn') as HTMLElement; clearBtn.onclick = (e) => { this.model.file.clearLatestFiles(); this.redraw(); }; // browse / import button const moreBtn = this.element.querySelector('.more-btn') as HTMLElement; moreBtn.onclick = (e) => { this.modalDialog.close(); // note that if there is an error in CE, we keep it open so that the user // can do something, e.g. navigate awway from a non existing folder this.controller.fileMenuController.openFile( null, null, () => this.modalDialog.open()); }; // tip of the day const tipOfTheDayElement = this.element.querySelector('.tip-of-the-day') as HTMLElement; const tipOfTheDay: TipOfTheDay = new TipOfTheDay(tipOfTheDayElement); } /** * redraw UI each time the dialog opens */ redraw() { // recent files const recentFiles = this.model.file.getLatestFiles(); // buttons visibility const pane = this.element.querySelector('.open-pane'); if (recentFiles.length > 0) { pane.classList.remove('emty-list'); } else { pane.classList.add('emty-list'); } // fill the list const ul = pane.querySelector('ul.list'); ul.innerHTML = ''; // add the <li> tags to the <ul> tag recentFiles .map((blob) => { // there may be errors due to wrong data in the local storage try { const li = document.createElement('li'); li.setAttribute('data-file-info', JSON.stringify(blob)); li.classList.add('list-item'); const icon = document.createElement('span'); icon.setAttribute('data-file-info', JSON.stringify(blob)); // cloudIcon= fa-github | fa-dropbox | fa-server | fa-cloud | // fa-cloud-download const cloudIcon = (() => { switch (blob.service) { case 'github': return ['fa', 'fa-github']; case 'dropbox': return ['fa', 'fa-dropbox']; case 'webdav': return ['fa', 'fa-cloud-download']; case 'ftp': case 'sftp': return ['fa', 'fa-server']; case 'fs': return ['fa', 'fa-hdd-o']; default: return ['fa', 'fa-cloud']; } })(); icon.classList.add(...cloudIcon); li.appendChild(icon); const name = document.createElement('span'); name.setAttribute('data-file-info', JSON.stringify(blob)); name.innerHTML = blob.folder + '<strong>/' + blob.name + '</strong>/'; // the '/' is at the end because the css are "ltr" in order to have // ellipsis at the beginning li.appendChild(name); return li; } catch (e) { console.error('Catched error: ', e); } return null; }) .filter((item) => !!item) .forEach((li) => ul.appendChild(li)); } /** * open the dialog * @param options options object */ openDialog(options: { openFileInfo: (p1: FileInfo) => any, openTemplate: (p1: string) => any, ready: (() => any), error: ((p1?: any) => any), }) { // is ready callback // error callback if (this.state === 'ready') { // notify the owner if (options.ready) { options.ready(); } } else { // store them for later if (this.state === 'error') { if (options.error) { options.error(); } } else { this.readyCbk = options.ready; this.errorCbk = options.error; } } this.selected = null; this.modalDialog.onClose = () => { // notify the owner, with the url to load or nothing (will load blank // template) if (this.selected) { if (this.selected.fileInfo) { options.openFileInfo(this.selected.fileInfo); } else { options.openTemplate(this.selected.url); } } else { options.openTemplate(null); } }; this.modalDialog.open(); } }