mirror of
https://github.com/chinchang/web-maker.git
synced 2025-07-11 09:06:23 +02:00
add basic saving feature to local. fixes #16
This commit is contained in:
@ -19,6 +19,7 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
background: rgba(0, 0, 0, 0.5);
|
background: rgba(0, 0, 0, 0.5);
|
||||||
background: #444;
|
background: #444;
|
||||||
|
color: rgba(255,255,255,0.9);
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
font-family: Helvetica, arial;
|
font-family: Helvetica, arial;
|
||||||
}
|
}
|
||||||
@ -46,11 +47,14 @@
|
|||||||
top: 5px;
|
top: 5px;
|
||||||
margin-left: 7px;
|
margin-left: 7px;
|
||||||
}
|
}
|
||||||
|
input[type="text"] {
|
||||||
|
padding: 3px 5px;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
.btn {
|
.btn {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border: 0;
|
border: 0;
|
||||||
background: #0074d9;
|
background: #0074d9;
|
||||||
/*border: 1px solid #aaa;*/
|
|
||||||
color: white;
|
color: white;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
@ -144,6 +148,7 @@
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
background: white;
|
background: white;
|
||||||
}
|
}
|
||||||
|
.main-header,
|
||||||
.footer {
|
.footer {
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
background-color: #111;
|
background-color: #111;
|
||||||
@ -151,6 +156,10 @@
|
|||||||
border-top: 1px solid rgba(255,255,255,0.14);
|
border-top: 1px solid rgba(255,255,255,0.14);
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
}
|
}
|
||||||
|
.main-header {
|
||||||
|
border: 0;
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.14);
|
||||||
|
}
|
||||||
.logo {
|
.logo {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
@ -211,6 +220,12 @@
|
|||||||
.gutter-vertical {
|
.gutter-vertical {
|
||||||
cursor: ns-resize;
|
cursor: ns-resize;
|
||||||
}
|
}
|
||||||
|
.item-title-input {
|
||||||
|
background: none;
|
||||||
|
border: 0;
|
||||||
|
color: rgba(255,255,255,0.8);
|
||||||
|
width: calc(100vw - 400px);
|
||||||
|
}
|
||||||
.modal {
|
.modal {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 5vh;
|
top: 5vh;
|
||||||
@ -257,6 +272,21 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
.saved-items-pane {
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 400px;
|
||||||
|
z-index: 3;
|
||||||
|
background-color: #111;
|
||||||
|
transition: 0.3s ease;
|
||||||
|
will-change: transform;
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
.saved-items-pane.is-open {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
.notifications-btn {
|
.notifications-btn {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
@ -359,6 +389,21 @@
|
|||||||
|
|
||||||
<body class="layout-">
|
<body class="layout-">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
|
<div class="main-header">
|
||||||
|
<div class="fr">
|
||||||
|
|
||||||
|
<a id="js-new-btn"><svg style="vertical-align:middle;width:14px;height:14px" viewBox="0 0 24 24">
|
||||||
|
<path fill="crimson" d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" />
|
||||||
|
</svg>New</a>
|
||||||
|
<a id="js-save-btn" style="margin-left:10px"><svg style="vertical-align:middle;width:14px;height:14px" viewBox="0 0 24 24">
|
||||||
|
<path fill="crimson" d="M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z" />
|
||||||
|
</svg>Save</a>
|
||||||
|
<a id="js-open-btn" style="margin-left:10px"><svg style="width:14px;height:14px;vertical-align:middle;" viewBox="0 0 24 24">
|
||||||
|
<path fill="crimson" d="M13,9V3.5L18.5,9M6,2C4.89,2 4,2.89 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2H6Z" />
|
||||||
|
</svg>Open</a>
|
||||||
|
</div>
|
||||||
|
<input type="text" id="js-title-input" class="item-title-input" value="Untitled Work">
|
||||||
|
</div>
|
||||||
<div class="content-wrap flex flex-grow">
|
<div class="content-wrap flex flex-grow">
|
||||||
<div class="code-side" id="js-code-side">
|
<div class="code-side" id="js-code-side">
|
||||||
<div id="js-html-code" data-type="html" class="code-wrap">
|
<div id="js-html-code" data-type="html" class="code-wrap">
|
||||||
@ -521,6 +566,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="js-saved-items-pane" class="saved-items-pane">
|
||||||
|
<button class="btn" id="js-saved-items-pane-close-btn">X</button>
|
||||||
|
<h3>My Items</h3>
|
||||||
|
<div id="js-saved-items-wrap">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="modal-overlay"></div>
|
<div class="modal-overlay"></div>
|
||||||
|
|
||||||
<svg width="30" height="30" viewBox="0 0 100 100" fill="rgba(255, 255, 255, 0.09)">
|
<svg width="30" height="30" viewBox="0 0 100 100" fill="rgba(255, 255, 255, 0.09)">
|
||||||
@ -555,6 +608,7 @@
|
|||||||
|
|
||||||
<script src="lib/split.js"></script>
|
<script src="lib/split.js"></script>
|
||||||
|
|
||||||
|
<script src="utils.js"></script>
|
||||||
<script src="deferred.js"></script>
|
<script src="deferred.js"></script>
|
||||||
<script src="loader.js"></script>
|
<script src="loader.js"></script>
|
||||||
<script src="script.js"></script>
|
<script src="script.js"></script>
|
||||||
|
160
src/script.js
160
src/script.js
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
window.$ = document.querySelector.bind(document);
|
window.$ = document.querySelector.bind(document);
|
||||||
window.$all = document.querySelectorAll.bind(document);
|
window.$all = document.querySelectorAll.bind(document);
|
||||||
|
window.DEBUG = 1;
|
||||||
|
|
||||||
var HtmlModes = {
|
var HtmlModes = {
|
||||||
HTML: 'html',
|
HTML: 'html',
|
||||||
@ -42,7 +43,9 @@
|
|||||||
, jsMode = JsModes.JS
|
, jsMode = JsModes.JS
|
||||||
, cssMode = CssModes.CSS
|
, cssMode = CssModes.CSS
|
||||||
, sass
|
, sass
|
||||||
|
, currentItem
|
||||||
|
|
||||||
|
// DOM nodes
|
||||||
, frame = $('#demo-frame')
|
, frame = $('#demo-frame')
|
||||||
, htmlCode = $('#js-html-code')
|
, htmlCode = $('#js-html-code')
|
||||||
, cssCode = $('#js-css-code')
|
, cssCode = $('#js-css-code')
|
||||||
@ -57,29 +60,21 @@
|
|||||||
, saveHtmlBtn = $('#js-save-html')
|
, saveHtmlBtn = $('#js-save-html')
|
||||||
, settingsBtn = $('#js-settings-btn')
|
, settingsBtn = $('#js-settings-btn')
|
||||||
, notificationsBtn = $('#js-notifications-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')
|
, notificationsModal = $('#js-notifications-modal')
|
||||||
, htmlModelLabel = $('#js-html-mode-label')
|
, htmlModelLabel = $('#js-html-mode-label')
|
||||||
, cssModelLabel = $('#js-css-mode-label')
|
, cssModelLabel = $('#js-css-mode-label')
|
||||||
, jsModelLabel = $('#js-js-mode-label')
|
, jsModelLabel = $('#js-js-mode-label')
|
||||||
|
, titleInput = $('#js-title-input')
|
||||||
;
|
;
|
||||||
|
|
||||||
editur.cm = {};
|
editur.cm = {};
|
||||||
editur.demoFrameDocument = frame.contentDocument || frame.contentWindow.document;
|
editur.demoFrameDocument = frame.contentDocument || frame.contentWindow.document;
|
||||||
|
|
||||||
// https://github.com/substack/semver-compare/blob/master/index.js
|
|
||||||
function semverCompare (a, b) {
|
|
||||||
var pa = a.split('.');
|
|
||||||
var pb = b.split('.');
|
|
||||||
for (var i = 0; i < 3; i++) {
|
|
||||||
var na = Number(pa[i]);
|
|
||||||
var nb = Number(pb[i]);
|
|
||||||
if (na > nb) { return 1; }
|
|
||||||
if (nb > na) { return -1; }
|
|
||||||
if (!isNaN(na) && isNaN(nb)) { return 1; }
|
|
||||||
if (isNaN(na) && !isNaN(nb)) { return -1; }
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetSplitting() {
|
function resetSplitting() {
|
||||||
var gutters = $all('.gutter');
|
var gutters = $all('.gutter');
|
||||||
@ -124,13 +119,94 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveCode() {
|
// Save current item to storage
|
||||||
var code = {
|
function saveItem() {
|
||||||
html: editur.cm.html.getValue(),
|
var isNewItem = !currentItem.id;
|
||||||
css: editur.cm.css.getValue(),
|
currentItem.id = currentItem.id || ('item-' + generateRandomId());
|
||||||
js: editur.cm.js.getValue()
|
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.updatedOn = Date.now();
|
||||||
|
log('saving key', key || currentItem.id, currentItem)
|
||||||
|
currentItem.hoid = currentItem.id;
|
||||||
|
saveSetting(key || currentItem.id, currentItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateItem(items) {
|
||||||
|
currentItem = savedItems[];
|
||||||
|
refreshEditor();
|
||||||
|
}
|
||||||
|
function populateItemsInSavedPane(items) {
|
||||||
|
if (!items || !items.length) return;
|
||||||
|
var html = '';
|
||||||
|
// TODO: sort desc. by updation date
|
||||||
|
items.forEach(function (item) {
|
||||||
|
html += '<div class="saved-item-tile">' + item.title + '</div>';
|
||||||
|
})
|
||||||
|
savedItemsPane.querySelector('#js-saved-items-wrap').innerHTML = html;
|
||||||
|
toggleSavedItemsPane();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleSavedItemsPane() {
|
||||||
|
savedItemsPane.classList.toggle('is-open');
|
||||||
|
}
|
||||||
|
function openSavedItemsPane() {
|
||||||
|
chrome.storage.local.get('items', function (result) {
|
||||||
|
var itemIds = Object.getOwnPropertyNames(result.items),
|
||||||
|
items = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < itemIds.length; i++) {
|
||||||
|
(function (index) {
|
||||||
|
chrome.storage.local.get(itemIds[index], function (itemResult) {
|
||||||
|
items.push(itemResult[itemIds[index]]);
|
||||||
|
// Check if we have all items now.
|
||||||
|
if (itemIds.length === items.length) {
|
||||||
|
populateItemsInSavedPane(items);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})(i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createNewItem() {
|
||||||
|
currentItem = {
|
||||||
|
title: 'Untitled ' + (new Date).getDate()
|
||||||
};
|
};
|
||||||
saveSetting('code', code);
|
editur.cm.html.setValue('');
|
||||||
|
editur.cm.css.setValue('');
|
||||||
|
editur.cm.js.setValue('');
|
||||||
|
editur.cm.html.refresh();
|
||||||
|
editur.cm.css.refresh();
|
||||||
|
editur.cm.js.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -240,11 +316,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.onunload = function () {
|
window.onunload = function () {
|
||||||
saveCode();
|
saveCode('code');
|
||||||
};
|
};
|
||||||
|
|
||||||
function createPreviewFile(html, css, js) {
|
function createPreviewFile(html, css, js) {
|
||||||
var contents = '<html>\n<head>\n<style>\n' + css + '\n</style>\n</head>\n<body>\n' + html + '\n<script>\n' + js + '\n</script></body>\n</html>';
|
var contents =
|
||||||
|
'<html>\n<head>\n' +
|
||||||
|
'<style>\n' + css + '\n</style>\n</head>\n' +
|
||||||
|
'<body>\n' + html + '\n<script>\n' + js + '\n</script></body>\n</html>';
|
||||||
|
|
||||||
var fileWritten = false;
|
var fileWritten = false;
|
||||||
|
|
||||||
@ -257,7 +336,8 @@
|
|||||||
fileEntry.createWriter(function(fileWriter) {
|
fileEntry.createWriter(function(fileWriter) {
|
||||||
function onWriteComplete() {
|
function onWriteComplete() {
|
||||||
if (fileWritten) {
|
if (fileWritten) {
|
||||||
frame.src = 'filesystem:chrome-extension://' + chrome.i18n.getMessage('@@extension_id') + '/temporary/' + 'preview.html';
|
frame.src = 'filesystem:chrome-extension://' +
|
||||||
|
chrome.i18n.getMessage('@@extension_id') + '/temporary/' + 'preview.html';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
fileWritten = true;
|
fileWritten = true;
|
||||||
@ -353,9 +433,8 @@
|
|||||||
layoutBtn2.addEventListener('click', function () { saveSetting('layoutMode', 2); toggleLayout(2); return false; });
|
layoutBtn2.addEventListener('click', function () { saveSetting('layoutMode', 2); toggleLayout(2); return false; });
|
||||||
layoutBtn3.addEventListener('click', function () { saveSetting('layoutMode', 3); toggleLayout(3); return false; });
|
layoutBtn3.addEventListener('click', function () { saveSetting('layoutMode', 3); toggleLayout(3); return false; });
|
||||||
|
|
||||||
helpBtn.addEventListener('click', function () {
|
onButtonClick(helpBtn, function () {
|
||||||
helpModal.classList.toggle('is-modal-visible');
|
helpModal.classList.toggle('is-modal-visible');
|
||||||
return false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
notificationsBtn.addEventListener('click', function () {
|
notificationsBtn.addEventListener('click', function () {
|
||||||
@ -392,9 +471,11 @@
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
saveHtmlBtn.addEventListener('click', function () {
|
onButtonClick(saveHtmlBtn, saveFile);
|
||||||
saveFile();
|
onButtonClick(openBtn, openSavedItemsPane);
|
||||||
});
|
onButtonClick(saveBtn, saveItem);
|
||||||
|
onButtonClick(newBtn, createNewItem);
|
||||||
|
onButtonClick(savedItemsPaneCloseBtn, toggleSavedItemsPane);
|
||||||
|
|
||||||
// Attach listeners on mode change menu items
|
// Attach listeners on mode change menu items
|
||||||
var modeItems = [].slice.call($all('.js-modes-menu a'));
|
var modeItems = [].slice.call($all('.js-modes-menu a'));
|
||||||
@ -429,7 +510,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
settingsBtn.addEventListener('click', function() {
|
onButtonClick(settingsBtn, function() {
|
||||||
if (!chrome.runtime.openOptionsPage) {
|
if (!chrome.runtime.openOptionsPage) {
|
||||||
// New way to open options pages, if supported (Chrome 42+).
|
// New way to open options pages, if supported (Chrome 42+).
|
||||||
// Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=601997
|
// Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=601997
|
||||||
@ -442,10 +523,8 @@
|
|||||||
url: 'chrome://extensions?options=' + chrome.i18n.getMessage('@@extension_id')
|
url: 'chrome://extensions?options=' + chrome.i18n.getMessage('@@extension_id')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
chrome.storage.local.get({
|
chrome.storage.local.get({
|
||||||
layoutMode: 1,
|
layoutMode: 1,
|
||||||
code: ''
|
code: ''
|
||||||
@ -464,12 +543,19 @@
|
|||||||
cssMode: 'css'
|
cssMode: 'css'
|
||||||
}, function syncGetCallback(result) {
|
}, function syncGetCallback(result) {
|
||||||
if (result.preserveLastCode && lastCode) {
|
if (result.preserveLastCode && lastCode) {
|
||||||
editur.cm.html.setValue(lastCode.html);
|
if (lastCode.id) {
|
||||||
editur.cm.css.setValue(lastCode.css);
|
chrome.storage.local.get(lastCode.id, function (result) {
|
||||||
editur.cm.js.setValue(lastCode.js);
|
log('Load item ', lastCode.id)
|
||||||
editur.cm.html.refresh();
|
currentItem = result[lastCode.id];
|
||||||
editur.cm.css.refresh();
|
refreshEditor();
|
||||||
editur.cm.js.refresh();
|
})
|
||||||
|
} else {
|
||||||
|
log('Load last unsaved item');
|
||||||
|
currentItem = lastCode;
|
||||||
|
refreshEditor();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
createNewItem();
|
||||||
}
|
}
|
||||||
updateHtmlMode(result.htmlMode);
|
updateHtmlMode(result.htmlMode);
|
||||||
updateJsMode(result.jsMode);
|
updateJsMode(result.jsMode);
|
||||||
|
36
src/utils.js
Normal file
36
src/utils.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// https://github.com/substack/semver-compare/blob/master/index.js
|
||||||
|
function semverCompare(a, b) {
|
||||||
|
var pa = a.split('.');
|
||||||
|
var pb = b.split('.');
|
||||||
|
for (var i = 0; i < 3; i++) {
|
||||||
|
var na = Number(pa[i]);
|
||||||
|
var nb = Number(pb[i]);
|
||||||
|
if (na > nb) { return 1; }
|
||||||
|
if (nb > na) { return -1; }
|
||||||
|
if (!isNaN(na) && isNaN(nb)) { return 1; }
|
||||||
|
if (isNaN(na) && !isNaN(nb)) { return -1; }
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateRandomId(length) {
|
||||||
|
length = length || 10;
|
||||||
|
var id = '';
|
||||||
|
for (var i = length; i--;) {
|
||||||
|
id += String.fromCharCode(~~(Math.random() * 52) + 65);
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onButtonClick(btn, listener) {
|
||||||
|
btn.addEventListener('click', function buttonClickListener() {
|
||||||
|
listener();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function log() {
|
||||||
|
if (window.DEBUG) {
|
||||||
|
console.log.apply(console, [].splice.call(arguments, 0));
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user