/* eslint-disable no-extra-semi */
;(function (alertsService) {
/* eslint-enable no-extra-semi */
var editur = window.editur || {};
var version = '1.7.1';
window.DEBUG = 1;
var HtmlModes = {
HTML: 'html',
MARKDOWN: 'markdown',
JADE: 'jade' // unsafe eval is put in manifest for this file
};
var CssModes = {
CSS: 'css',
SCSS: 'scss',
LESS: 'less'
};
var JsModes = {
JS: 'js',
ES6: 'es6',
COFFEESCRIPT: 'coffee'
};
var modes = {};
modes[HtmlModes.HTML] = { label: 'HTML', cmMode: 'htmlmixed', codepenVal: 'none' };
modes[HtmlModes.MARKDOWN] = { label: 'Markdown', cmMode: 'markdown', codepenVal: 'markdown' };
modes[HtmlModes.JADE] = { label: 'Jade', cmMode: 'jade', codepenVal: 'jade' };
modes[JsModes.JS] = { label: 'JS', cmMode: 'javascript', codepenVal: 'none' };
modes[JsModes.COFFEESCRIPT] = { label: 'CoffeeScript', cmMode: 'coffeescript', codepenVal: 'coffeescript' };
modes[JsModes.ES6] = { label: 'ES6 (Babel)', cmMode: 'javascript', codepenVal: 'babel' };
modes[CssModes.CSS] = { label: 'CSS', cmMode: 'css', codepenVal: 'none' };
modes[CssModes.SCSS] = { label: 'SCSS', cmMode: 'sass', codepenVal: 'scss' };
modes[CssModes.LESS] = { label: 'LESS', cmMode: 'text/x-less', codepenVal: 'less' };
var updateTimer
, updateDelay = 500
, currentLayoutMode
, hasSeenNotifications = true
, htmlMode = HtmlModes.HTML
, jsMode = JsModes.JS
, cssMode = CssModes.CSS
, sass
, currentItem
, savedItems
, minCodeWrapSize = 33
, mainSplitInstance
, codeSplitInstance
// TODO: for legacy reasons when. Will be refactored as global preferences.
, prefs = {}
// DOM nodes
, frame = $('#demo-frame')
, htmlCode = $('#js-html-code')
, cssCode = $('#js-css-code')
, jsCode = $('#js-js-code')
, layoutBtn1 = $('#js-layout-btn-1')
, layoutBtn2 = $('#js-layout-btn-2')
, layoutBtn3 = $('#js-layout-btn-3')
, helpBtn = $('#js-help-btn')
, helpModal = $('#js-help-modal')
, codepenBtn = $('#js-codepen-btn')
, codepenForm = $('#js-codepen-form')
, saveHtmlBtn = $('#js-save-html')
, settingsBtn = $('#js-settings-btn')
, notificationsBtn = $('#js-notifications-btn')
, openBtn = $('#js-open-btn')
, saveBtn = $('#js-save-btn')
, newBtn = $('#js-new-btn')
, savedItemsPane = $('#js-saved-items-pane')
, savedItemsPaneCloseBtn = $('#js-saved-items-pane-close-btn')
, notificationsModal = $('#js-notifications-modal')
, htmlModelLabel = $('#js-html-mode-label')
, cssModelLabel = $('#js-css-mode-label')
, jsModelLabel = $('#js-js-mode-label')
, titleInput = $('#js-title-input')
;
editur.cm = {};
editur.demoFrameDocument = frame.contentDocument || frame.contentWindow.document;
function resetSplitting() {
if (codeSplitInstance) {
codeSplitInstance.destroy();
}
if (mainSplitInstance) {
mainSplitInstance.destroy();
}
codeSplitInstance = Split(['#js-html-code', '#js-css-code', '#js-js-code'], {
direction: (currentLayoutMode === 2 ? 'horizontal' : 'vertical'),
minSize: minCodeWrapSize,
gutterSize: 6
});
mainSplitInstance = Split(['#js-code-side', '#js-demo-side' ], {
direction: (currentLayoutMode === 2 ? 'vertical' : 'horizontal'),
minSize: 34,
gutterSize: 6
});
}
function toggleLayout(mode) {
currentLayoutMode = mode;
$('#js-layout-btn-1').classList.remove('selected');
$('#js-layout-btn-2').classList.remove('selected');
$('#js-layout-btn-3').classList.remove('selected');
$('#js-layout-btn-' + mode).classList.add('selected');
document.body.classList.remove('layout-1');
document.body.classList.remove('layout-2');
document.body.classList.remove('layout-3');
document.body.classList.add('layout-' + mode);
resetSplitting();
trackEvent('ui', 'toggleLayout', mode);
}
function saveSetting(setting, value, cb) {
var obj = {};
obj[setting] = value;
chrome.storage.local.set(obj, cb || function(){});
}
// Save current item to storage
function saveItem() {
var isNewItem = !currentItem.id;
currentItem.id = currentItem.id || ('item-' + utils.generateRandomId());
saveCode();
// Push into the items hash if its a new item being saved
if (isNewItem) {
chrome.storage.local.get({
items: {}
}, function (result) {
result.items[currentItem.id] = true;
chrome.storage.local.set({
items: result.items
});
})
}
}
function saveCode(key) {
currentItem.title = titleInput.value;
currentItem.html = editur.cm.html.getValue();
currentItem.css = editur.cm.css.getValue();
currentItem.js = editur.cm.js.getValue();
currentItem.htmlMode = htmlMode;
currentItem.cssMode = cssMode;
currentItem.jsMode = jsMode;
currentItem.updatedOn = Date.now();
currentItem.layoutMode = currentLayoutMode;
utils.log('saving key', key || currentItem.id, currentItem)
saveSetting(key || currentItem.id, currentItem, function () {
alertsService.add('Item saved.');
});
}
function populateItemsInSavedPane(items) {
if (!items || !items.length) { return; }
var html = '';
// TODO: sort desc. by updation date
items.sort(function (a, b) {
return b.updatedOn - a.updatedOn;
});
items.forEach(function (item) {
html += ''
+ '' + item.title + '
Last updated: ' + item.updatedOn + '';
})
savedItemsPane.querySelector('#js-saved-items-wrap').innerHTML = html;
toggleSavedItemsPane();
}
function toggleSavedItemsPane(shouldOpen) {
if (shouldOpen === false) {
savedItemsPane.classList.remove('is-open');
} else {
savedItemsPane.classList.toggle('is-open');
}
document.body.classList[savedItemsPane.classList.contains('is-open') ? 'add' : 'remove']('overlay-visible');
}
function openSavedItemsPane() {
chrome.storage.local.get('items', function (result) {
var itemIds = Object.getOwnPropertyNames(result.items),
items = [];
savedItems = savedItems || [];
for (var i = 0; i < itemIds.length; i++) {
(function (index) {
chrome.storage.local.get(itemIds[index], function (itemResult) {
savedItems[itemIds[index]] = itemResult[itemIds[index]];
items.push(itemResult[itemIds[index]]);
// Check if we have all items now.
if (itemIds.length === items.length) {
populateItemsInSavedPane(items);
}
});
})(i);
}
});
}
function createNewItem() {
var d = new Date();
currentItem = {
title: 'Untitled ' + d.getDate() + '-' + d.getMonth() + '-' + d.getHours() + ':' + d.getMinutes(),
html: '',
css: '',
js: '',
layoutMode: currentLayoutMode
};
alertsService.add('New item created');
refreshEditor();
}
function openItem(itemId) {
currentItem = savedItems[itemId];
codeSplitInstance.setSizes([ 33.3, 33.3, 33.3 ]);
refreshEditor();
alertsService.add('Saved item loaded');
}
function refreshEditor() {
titleInput.value = currentItem.title || 'Untitled';
editur.cm.html.setValue(currentItem.html);
editur.cm.css.setValue(currentItem.css);
editur.cm.js.setValue(currentItem.js);
editur.cm.html.refresh();
editur.cm.css.refresh();
editur.cm.js.refresh();
updateHtmlMode(currentItem.htmlMode || prefs.htmlMode || HtmlModes.HTML);
updateJsMode(currentItem.jsMode || prefs.jsMode || JsModes.JS);
updateCssMode(currentItem.cssMode || prefs.cssMode || CssModes.CSS);
toggleLayout(currentItem.layoutMode || prefs.layoutMode);
}
/**
* Loaded the code comiler based on the mode selected
*/
function handleModeRequirements(mode) {
// Exit if already loaded
if (modes[mode].hasLoaded) { return; }
function setLoadedFlag() {
modes[mode].hasLoaded = true;
}
if (mode === HtmlModes.JADE) {
loadJS('lib/jade.js').then(setLoadedFlag);
} else if (mode === HtmlModes.MARKDOWN) {
loadJS('lib/marked.js').then(setLoadedFlag);
} else if (mode === CssModes.LESS) {
loadJS('lib/less.min.js').then(setLoadedFlag);
} else if (mode === CssModes.SCSS) {
loadJS('lib/sass.js').then(function () {
sass = new Sass('lib/sass.worker.js');
setLoadedFlag();
});
} else if (mode === JsModes.COFFEESCRIPT) {
loadJS('lib/coffee-script.js').then(setLoadedFlag);
} else if (mode === JsModes.ES6) {
loadJS('lib/babel.min.js').then(setLoadedFlag);
}
}
function updateHtmlMode(value) {
htmlMode = value;
htmlModelLabel.textContent = modes[value].label;
handleModeRequirements(value);
editur.cm.html.setOption('mode', modes[value].cmMode);
CodeMirror.autoLoadMode(editur.cm.html, modes[value].cmMode);
trackEvent('ui', 'updateCodeMode', 'html', value);
}
function updateCssMode(value) {
cssMode = value;
cssModelLabel.textContent = modes[value].label;
handleModeRequirements(value);
editur.cm.css.setOption('mode', modes[value].cmMode);
CodeMirror.autoLoadMode(editur.cm.css, modes[value].cmMode);
trackEvent('ui', 'updateCodeMode', 'css', value);
}
function updateJsMode(value) {
jsMode = value;
jsModelLabel.textContent = modes[value].label;
handleModeRequirements(value);
editur.cm.js.setOption('mode', modes[value].cmMode);
CodeMirror.autoLoadMode(editur.cm.js, modes[value].cmMode);
trackEvent('ui', 'updateCodeMode', 'js', value);
// FIXME: Will be saved as part of global settings
/*
chrome.storage.sync.set({
jsMode: value
}, function () {});
*/
}
// computeHtml, computeCss & computeJs evaluate the final code according
// to whatever mode is selected and resolve the returned promise with the code.
function computeHtml() {
var d = deferred();
var code = editur.cm.html.getValue();
if (htmlMode === HtmlModes.HTML) {
d.resolve(code);
} else if (htmlMode === HtmlModes.MARKDOWN) {
d.resolve(marked(code));
} else if (htmlMode === HtmlModes.JADE) {
d.resolve(jade.render(code));
}
return d.promise;
}
function computeCss() {
var d = deferred();
var code = editur.cm.css.getValue();
cleanupErrors('css');
if (cssMode === CssModes.CSS) {
d.resolve(code);
} else if (cssMode === CssModes.SCSS) {
sass.compile(code, function(result) {
// Something as wrong
if (result.line && result.message) {
showErrors('css', [ { lineNumber: result.line - 1, message: result.message } ]);
}
d.resolve(result.text);
});
} else if (cssMode === CssModes.LESS) {
less.render(code).then(function (result) {
d.resolve(result.css);
}, function (error) {
showErrors('css', [ { lineNumber: error.line, message: error.message } ]);
});
}
return d.promise;
}
function computeJs() {
var d = deferred();
var code = editur.cm.js.getValue();
cleanupErrors('js');
var ast;
if (jsMode === JsModes.JS) {
try {
ast = esprima.parse(code, {
tolerant: true
});
} catch (e) {
showErrors('js', [ { lineNumber: e.lineNumber - 1, message: e.description } ]);
} finally {
utils.addInfiniteLoopProtection(ast);
d.resolve(escodegen.generate(ast));
}
} else if (jsMode === JsModes.COFFEESCRIPT) {
var coffeeCode;
try {
coffeeCode = CoffeeScript.compile(code, { bare: true });
} catch (e) {
showErrors('js', [ { lineNumber: e.location.first_line, message: e.message } ]);
} finally {
ast = esprima.parse(coffeeCode, {
tolerant: true
});
utils.addInfiniteLoopProtection(ast);
d.resolve(escodegen.generate(ast));
}
} else if (jsMode === JsModes.ES6) {
try {
ast = esprima.parse(code, {
tolerant: true
});
} catch (e) {
showErrors('js', [ { lineNumber: e.lineNumber - 1, message: e.description } ]);
} finally {
utils.addInfiniteLoopProtection(ast);
d.resolve(Babel.transform(escodegen.generate(ast), { presets: ['es2015'] }).code);
}
}
return d.promise;
}
window.previewException = function (error) {
console.error('Possible infinite loop detected.', error.stack)
}
window.onunload = function () {
saveCode('code');
};
function cleanupErrors(lang) {
editur.cm[lang].clearGutter('error-gutter');
}
function showErrors(lang, errors) {
var editor = editur.cm[lang];
errors.forEach(function (e) {
editor.operation(function () {
var n = document.createElement('div');
n.setAttribute('data-title', e.message);
n.classList.add('gutter-error-marker');
editor.setGutterMarker(e.lineNumber, 'error-gutter', n);
});
});
}
function createPreviewFile(html, css, js) {
var contents = '\n