diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index 6d5534100..6bd967ffe 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -176,9 +176,12 @@ define([ } }; - var contentUpdate = function (newContent) { + var oldContent; + var contentUpdate = function (newContent, waitFor) { + if (JSONSortify(newContent) === JSONSortify(oldContent)) { return; } try { - evContentUpdate.fire(newContent); + evContentUpdate.fire(newContent, waitFor); + setTimeout(function () { oldContent = newContent; }); } catch (e) { console.log(e.stack); UI.errorLoadingScreen(e.message); @@ -197,46 +200,48 @@ define([ cpNfInner.metadataMgr.updateMetadata(meta); newContent = normalize(newContent); - contentUpdate(newContent); - - if (!readOnly) { - var newContent2NoMeta = normalize(contentGetter()); - var newContent2StrNoMeta = JSONSortify(newContent2NoMeta); - var newContentStrNoMeta = JSONSortify(newContent); - - if (newContent2StrNoMeta !== newContentStrNoMeta) { - console.error("shjson2 !== shjson"); - onLocal(); - - /* pushing back over the wire is necessary, but it can - result in a feedback loop, which we call a browser - fight */ - // what changed? - var ops = ChainPad.Diff.diff(newContentStrNoMeta, newContent2StrNoMeta); - // log the changes - console.log(newContentStrNoMeta); - console.log(ops); - var sop = JSON.stringify([ newContentStrNoMeta, ops ]); - - var fights = window.CryptPad_fights = window.CryptPad_fights || []; - var index = fights.indexOf(sop); - if (index === -1) { - fights.push(sop); - console.log("Found a new type of browser disagreement"); - console.log("You can inspect the list in your " + - "console at `REALTIME_MODULE.fights`"); - console.log(fights); - } else { - console.log("Encountered a known browser disagreement: " + - "available at `REALTIME_MODULE.fights[%s]`", index); + nThen(function (waitFor) { + contentUpdate(newContent, waitFor); + }).nThen(function () { + if (!readOnly) { + var newContent2NoMeta = normalize(contentGetter()); + var newContent2StrNoMeta = JSONSortify(newContent2NoMeta); + var newContentStrNoMeta = JSONSortify(newContent); + + if (newContent2StrNoMeta !== newContentStrNoMeta) { + console.error("shjson2 !== shjson"); + onLocal(); + + /* pushing back over the wire is necessary, but it can + result in a feedback loop, which we call a browser + fight */ + // what changed? + var ops = ChainPad.Diff.diff(newContentStrNoMeta, newContent2StrNoMeta); + // log the changes + console.log(newContentStrNoMeta); + console.log(ops); + var sop = JSON.stringify([ newContentStrNoMeta, ops ]); + + var fights = window.CryptPad_fights = window.CryptPad_fights || []; + var index = fights.indexOf(sop); + if (index === -1) { + fights.push(sop); + console.log("Found a new type of browser disagreement"); + console.log("You can inspect the list in your " + + "console at `REALTIME_MODULE.fights`"); + console.log(fights); + } else { + console.log("Encountered a known browser disagreement: " + + "available at `REALTIME_MODULE.fights[%s]`", index); + } } } - } - // Notify only when the content has changed, not when someone has joined/left - if (JSONSortify(newContent) !== JSONSortify(oldContent)) { - common.notify(); - } + // Notify only when the content has changed, not when someone has joined/left + if (JSONSortify(newContent) !== JSONSortify(oldContent)) { + common.notify(); + } + }); }; var setHistoryMode = function (bool, update) { @@ -289,58 +294,62 @@ define([ var newPad = false; if (newContentStr === '') { newPad = true; } - if (!newPad) { - var newContent = JSON.parse(newContentStr); - cpNfInner.metadataMgr.updateMetadata(extractMetadata(newContent)); - newContent = normalize(newContent); - contentUpdate(newContent); - } else { - if (!cpNfInner.metadataMgr.getPrivateData().isNewFile) { - // We're getting 'new pad' but there is an existing file - // We don't know exactly why this can happen but under no circumstances - // should we overwrite the content, so lets just try again. - console.log("userDoc is '' but this is not a new pad."); - console.log("Either this is an empty document which has not been touched"); - console.log("Or else something is terribly wrong, reloading."); - Feedback.send("NON_EMPTY_NEWDOC"); - setTimeout(function () { common.gotoURL(); }, 1000); - return; + // contentUpdate may be async so we need an nthen here + nThen(function (waitFor) { + if (!newPad) { + var newContent = JSON.parse(newContentStr); + cpNfInner.metadataMgr.updateMetadata(extractMetadata(newContent)); + newContent = normalize(newContent); + contentUpdate(newContent, waitFor); + } else { + if (!cpNfInner.metadataMgr.getPrivateData().isNewFile) { + // We're getting 'new pad' but there is an existing file + // We don't know exactly why this can happen but under no circumstances + // should we overwrite the content, so lets just try again. + console.log("userDoc is '' but this is not a new pad."); + console.log("Either this is an empty document which has not been touched"); + console.log("Or else something is terribly wrong, reloading."); + Feedback.send("NON_EMPTY_NEWDOC"); + setTimeout(function () { common.gotoURL(); }, 1000); + return; + } + console.log('updating title'); + title.updateTitle(title.defaultTitle); + evOnDefaultContentNeeded.fire(); + } + }).nThen(function () { + stateChange(STATE.READY); + firstConnection = false; + if (!readOnly) { onLocal(); } + evOnReady.fire(newPad); + + UI.removeLoadingScreen(emitResize); + + var privateDat = cpNfInner.metadataMgr.getPrivateData(); + var hash = privateDat.availableHashes.editHash || + privateDat.availableHashes.viewHash; + var href = privateDat.pathname + '#' + hash; + if (AppConfig.textAnalyzer && textContentGetter) { + AppConfig.textAnalyzer(textContentGetter, privateDat.channel); } - console.log('updating title'); - title.updateTitle(title.defaultTitle); - evOnDefaultContentNeeded.fire(); - } - stateChange(STATE.READY); - firstConnection = false; - if (!readOnly) { onLocal(); } - evOnReady.fire(newPad); - - UI.removeLoadingScreen(emitResize); - - var privateDat = cpNfInner.metadataMgr.getPrivateData(); - var hash = privateDat.availableHashes.editHash || - privateDat.availableHashes.viewHash; - var href = privateDat.pathname + '#' + hash; - if (AppConfig.textAnalyzer && textContentGetter) { - AppConfig.textAnalyzer(textContentGetter, privateDat.channel); - } - if (options.thumbnail && privateDat.thumbnails) { - if (hash) { - options.thumbnail.href = href; - options.thumbnail.getContent = function () { - if (!cpNfInner.chainpad) { return; } - return cpNfInner.chainpad.getUserDoc(); - }; - Thumb.initPadThumbnails(common, options.thumbnail); + if (options.thumbnail && privateDat.thumbnails) { + if (hash) { + options.thumbnail.href = href; + options.thumbnail.getContent = function () { + if (!cpNfInner.chainpad) { return; } + return cpNfInner.chainpad.getUserDoc(); + }; + Thumb.initPadThumbnails(common, options.thumbnail); + } } - } - var skipTemp = Util.find(privateDat, ['settings', 'general', 'creation', 'noTemplate']); - var skipCreation = Util.find(privateDat, ['settings', 'general', 'creation', 'skip']); - if (newPad && (!AppConfig.displayCreationScreen || (!skipTemp && skipCreation))) { - common.openTemplatePicker(); - } + var skipTemp = Util.find(privateDat, ['settings', 'general', 'creation', 'noTemplate']); + var skipCreation = Util.find(privateDat, ['settings', 'general', 'creation', 'skip']); + if (newPad && (!AppConfig.displayCreationScreen || (!skipTemp && skipCreation))) { + common.openTemplatePicker(); + } + }); }; var onConnectionChange = function (info) { if (state === STATE.DELETED) { return; } @@ -385,13 +394,19 @@ define([ common.createButton('import', true, options, function (c, f) { if (async) { fi(c, f, function (content) { - contentUpdate(content); - onLocal(); + nThen(function (waitFor) { + contentUpdate(content, waitFor); + }).nThen(function () { + onLocal(); + }); }); return; } - contentUpdate(fi(c, f)); - onLocal(); + nThen(function (waitFor) { + contentUpdate(fi(c, f), waitFor); + }).nThen(function () { + onLocal(); + }); }) ); }; diff --git a/www/whiteboard/inner.js b/www/whiteboard/inner.js index 4949343ba..bf57e89b7 100644 --- a/www/whiteboard/inner.js +++ b/www/whiteboard/inner.js @@ -1,81 +1,59 @@ define([ 'jquery', - '/bower_components/chainpad-crypto/crypto.js', - '/common/toolbar3.js', 'json.sortify', - '/common/common-util.js', '/bower_components/nthen/index.js', '/common/sframe-common.js', + '/common/sframe-app-framework.js', + '/common/common-util.js', + '/common/common-hash.js', '/common/common-interface.js', - '/api/config', - '/common/common-realtime.js', + '/common/common-thumbnail.js', '/customize/pages.js', '/customize/messages.js', - '/customize/application_config.js', - '/common/common-thumbnail.js', '/whiteboard/colors.js', + '/customize/application_config.js', '/bower_components/chainpad/chainpad.dist.js', '/bower_components/secure-fabric.js/dist/fabric.min.js', - '/bower_components/file-saver/FileSaver.min.js', - - 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', - 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', - 'less!/customize/src/less2/main.less', ], function ( $, - Crypto, - Toolbar, - JSONSortify, - Util, + Sortify, nThen, SFCommon, + Framework, + Util, + Hash, UI, - ApiConfig, - CommonRealtime, + Thumb, Pages, Messages, - AppConfig, - Thumb, Colors, + AppConfig, ChainPad) { - var saveAs = window.saveAs; var APP = window.APP = { $: $ }; var Fabric = APP.Fabric = window.fabric; - var stringify = function (obj) { - return JSONSortify(obj); - }; - - var toolbar; + var verbose = function (x) { console.log(x); }; + verbose = function () {}; // comment out to enable verbose logging - var andThen = function (common) { - var config = {}; - /* Initialize Fabric */ - var canvas = APP.canvas = new Fabric.Canvas('cp-app-whiteboard-canvas', { - containerClass: 'cp-app-whiteboard-canvas-container' - }); - var $canvas = $('canvas'); - var $controls = $('#cp-app-whiteboard-controls'); - var $canvasContainer = $('canvas').parents('.cp-app-whiteboard-canvas-container'); + var mkControls = function (framework, canvas) { var $pickers = $('#cp-app-whiteboard-pickers'); var $colors = $('#cp-app-whiteboard-colors'); var $cursors = $('#cp-app-whiteboard-cursors'); - var $deleteButton = $('#cp-app-whiteboard-delete'); - var $toggle = $('#cp-app-whiteboard-toggledraw'); + var $controls = $('#cp-app-whiteboard-controls'); var $width = $('#cp-app-whiteboard-width'); var $widthLabel = $('label[for="cp-app-whiteboard-width"]'); var $opacity = $('#cp-app-whiteboard-opacity'); var $opacityLabel = $('label[for="cp-app-whiteboard-opacity"]'); + var $toggle = $('#cp-app-whiteboard-toggledraw'); + var $deleteButton = $('#cp-app-whiteboard-delete'); + var metadataMgr = framework._.cpNfInner.metadataMgr; - // Brush - - var readOnly = false; var brush = { color: '#000000', opacity: 1 @@ -136,39 +114,6 @@ define([ updateBrushOpacity(); $opacity.on('change', updateBrushOpacity); - var pickColor = function (current, cb) { - var $picker = $('', { - type: 'color', - value: '#FFFFFF', - }) - .on('change', function () { - var color = this.value; - cb(color); - }).appendTo($pickers); - setTimeout(function () { - $picker.val(current); - $picker.click(); - }); - }; - var setColor = function (c) { - c = Colors.rgb2hex(c); - brush.color = c; - canvas.freeDrawingBrush.color = Colors.hex2rgba(brush.color, brush.opacity); - APP.$color.css({ - 'color': c, - }); - createCursor(); - }; - - var palette = AppConfig.whiteboardPalette || [ - 'red', 'blue', 'green', 'white', 'black', 'purple', - 'gray', 'beige', 'brown', 'cyan', 'darkcyan', 'gold', 'yellow', 'pink' - ]; - $('.cp-app-whiteboard-palette-color').on('click', function () { - var color = $(this).css('background-color'); - setColor(color); - }); - APP.draw = true; var toggleDrawMode = function () { canvas.deactivateAll().renderAll(); @@ -191,74 +136,49 @@ define([ canvas.discardActiveGroup(); } canvas.renderAll(); - config.onLocal(); + framework.localChange(); }; $deleteButton.click(deleteSelection); $(window).on('keyup', function (e) { if (e.which === 46) { deleteSelection (); } }); - var setEditable = function (bool) { - APP.editable = bool; - if (readOnly && bool) { return; } - if (bool) { $controls.css('display', 'flex'); } - else { $controls.hide(); } - canvas.isDrawingMode = bool ? APP.draw : false; - if (!bool) { - canvas.deactivateAll(); - canvas.renderAll(); - } - canvas.forEachObject(function (object) { - object.selectable = bool; - }); - $canvasContainer.find('canvas').css('border-color', bool? 'black': 'red'); - }; - - var saveImage = APP.saveImage = function () { - var defaultName = "pretty-picture.png"; - UI.prompt(Messages.exportPrompt, defaultName, function (filename) { - if (!(typeof(filename) === 'string' && filename)) { return; } - $canvas[0].toBlob(function (blob) { - saveAs(blob, filename); - }); + var pickColor = function (current, cb) { + var $picker = $('', { + type: 'color', + value: '#FFFFFF', + }) + .on('change', function () { + var color = this.value; + cb(color); + }).appendTo($pickers); + setTimeout(function () { + $picker.val(current); + $picker.click(); }); }; - - APP.FM = common.createFileManager({}); - APP.upload = function (title) { - var canvas = $canvas[0]; - APP.canvas.deactivateAll().renderAll(); - canvas.toBlob(function (blob) { - blob.name = title; - APP.FM.handleFile(blob); + var setColor = function (c) { + c = Colors.rgb2hex(c); + brush.color = c; + canvas.freeDrawingBrush.color = Colors.hex2rgba(brush.color, brush.opacity); + APP.$color.css({ + 'color': c, }); + createCursor(); }; - var initializing = true; - var $bar = $('#cp-toolbar'); - var Title; - var cpNfInner; - var metadataMgr; - - config = { - readOnly: readOnly, - patchTransformer: ChainPad.NaiveJSONTransformer, - // cryptpad debug logging (default is 1) - // logLevel: 0, - validateContent: function (content) { - try { - JSON.parse(content); - return true; - } catch (e) { - console.log("Failed to parse, rejecting patch"); - return false; - } - } - }; + var palette = AppConfig.whiteboardPalette || [ + 'red', 'blue', 'green', 'white', 'black', 'purple', + 'gray', 'beige', 'brown', 'cyan', 'darkcyan', 'gold', 'yellow', 'pink' + ]; + $('.cp-app-whiteboard-palette-color').on('click', function () { + var color = $(this).css('background-color'); + setColor(color); + }); var addColorToPalette = function (color, i) { - if (readOnly) { return; } + if (framework.isReadOnly()) { return; } var $color = $('', { 'class': 'cp-app-whiteboard-palette-color', }) @@ -271,7 +191,7 @@ define([ }) .on('dblclick', function (e) { e.preventDefault(); - if (!APP.editable) { return; } + if (framework.isLocked()) { return; } pickColor(Colors.rgb2hex($color.css('background-color')), function (c) { $color.css({ 'background-color': c, @@ -287,7 +207,7 @@ define([ var first = true; var updatePalette = function (newPalette) { - if (first || stringify(palette) !== stringify(newPalette)) { + if (first || Sortify(palette) !== Sortify(newPalette)) { palette = newPalette; $colors.html(''); palette.forEach(addColorToPalette); @@ -299,7 +219,7 @@ define([ var metadata = JSON.parse(JSON.stringify(metadataMgr.getMetadata())); metadata.palette = newPalette; metadataMgr.updateMetadata(metadata); - config.onLocal(); + framework.localChange(); }; var makeColorButton = function ($container) { @@ -312,7 +232,7 @@ define([ return; } - var $color = APP.$color = common.createButton(null, true, { + var $color = APP.$color = framework._.sfCommon.createButton(null, true, { icon: 'fa-square', title: Messages.canvas_chooseColor, name: 'color', @@ -331,33 +251,73 @@ define([ return $color; }; - var stringifyInner = function (textValue) { - var obj = { - content: textValue, - metadata: metadataMgr.getMetadataLazy() - }; - // stringify the json and send it into chainpad - return stringify(obj); + updateLocalPalette(palette); + + metadataMgr.onChange(function () { + var md = metadataMgr.getMetadata(); + if (md.palette) { + updateLocalPalette(md.palette); + } + }); + + return { + palette: palette, + makeColorButton: makeColorButton, + updateLocalPalette: updateLocalPalette, }; + }; + + var mkHelpMenu = function (framework) { + var $appContainer = $('#cp-app-whiteboard-container'); + var helpMenu = framework._.sfCommon.createHelpMenu(['whiteboard']); + $appContainer.prepend(helpMenu.menu); + framework._.toolbar.$drawer.append(helpMenu.button); + }; + + // Start of the main loop + var andThen2 = function (framework) { + var canvas = APP.canvas = new Fabric.Canvas('cp-app-whiteboard-canvas', { + containerClass: 'cp-app-whiteboard-canvas-container' + }); + var $canvas = $('canvas'); + var $canvasContainer = $('canvas').parents('.cp-app-whiteboard-canvas-container'); + var $controls = $('#cp-app-whiteboard-controls'); + var metadataMgr = framework._.cpNfInner.metadataMgr; - var onLocal = config.onLocal = function () { - if (initializing) { return; } - if (readOnly) { return; } - - var content = stringifyInner(canvas.toDatalessJSON()); - - try { - APP.realtime.contentUpdate(content); - } catch (e) { - APP.unrecoverable = true; - setEditable(false); - APP.toolbar.errorState(true, e.message); - var msg = Messages.chainpadError; - UI.errorLoadingScreen(msg, true, true); - console.error(e); + var setEditable = function (bool) { + if (bool) { $controls.css('display', 'flex'); } + else { $controls.hide(); } + + canvas.isDrawingMode = bool ? APP.draw : false; + if (!bool) { + canvas.deactivateAll(); + canvas.renderAll(); } + canvas.forEachObject(function (object) { + object.selectable = bool; + }); + $canvasContainer.find('canvas').css('border-color', bool? 'black': 'red'); }; + mkHelpMenu(framework); + + var controls = mkControls(framework, canvas); + + // --------------------------------------------- + // Whiteboard custom buttons + // --------------------------------------------- + + var $rightside = framework._.toolbar.$rightside; + + APP.FM = framework._.sfCommon.createFileManager({}); + APP.upload = function (title) { + var canvas = $canvas[0]; + APP.canvas.deactivateAll().renderAll(); + canvas.toBlob(function (blob) { + blob.name = title; + APP.FM.handleFile(blob); + }); + }; var addImageToCanvas = function (img) { var w = img.width; var h = img.height; @@ -370,313 +330,159 @@ define([ } var cImg = new Fabric.Image(img, { left:0, top:0, angle:0, }); APP.canvas.add(cImg); - onLocal(); + framework.localChange(); }; - var initThumbnails = function () { - var oldThumbnailState; - var privateDat = metadataMgr.getPrivateData(); - if (!privateDat.thumbnails) { return; } - var hash = privateDat.availableHashes.editHash || - privateDat.availableHashes.viewHash; - var href = privateDat.pathname + '#' + hash; - var mkThumbnail = function () { - if (!hash) { return; } - if (initializing) { return; } - if (!APP.realtime) { return; } - var content = APP.realtime.getUserDoc(); - if (content === oldThumbnailState) { return; } - var D = Thumb.getResizedDimensions($canvas[0], 'pad'); - Thumb.fromCanvas($canvas[0], D, function (err, b64) { - oldThumbnailState = content; - Thumb.setPadThumbnail(common, href, privateDat.channel, b64); - }); + // Export to drive as PNG + framework._.sfCommon.createButton('savetodrive', true, {}).click(function () { + var defaultName = framework._.title.getTitle(); + UI.prompt(Messages.exportPrompt, defaultName + '.png', function (name) { + if (name === null || !name.trim()) { return; } + APP.upload(name); + }); + }).appendTo($rightside); + + // Embed image + var onUpload = function (e) { + var file = e.target.files[0]; + var reader = new FileReader(); + reader.onload = function () { + var img = new Image(); + img.onload = function () { + addImageToCanvas(img); + }; + img.src = reader.result; }; - window.setInterval(mkThumbnail, Thumb.UPDATE_INTERVAL); - window.setTimeout(mkThumbnail, Thumb.UPDATE_FIRST); + reader.readAsDataURL(file); }; - - config.onInit = function (info) { - updateLocalPalette(palette); - readOnly = metadataMgr.getPrivateData().readOnly; - - Title = common.createTitle({}); - - var configTb = { - displayed: [ - 'userlist', - 'title', - 'useradmin', - 'spinner', - 'newpad', - 'share', - 'limit', - 'unpinnedWarning' - ], - title: Title.getTitleConfig(), - metadataMgr: metadataMgr, - readOnly: readOnly, - realtime: info.realtime, - sfCommon: common, - $container: $bar, - $contentContainer: $('#cp-app-whiteboard-canvas-area') + framework._.sfCommon.createButton('', true, { + title: Messages.canvas_imageEmbed, + icon: 'fa-file-image-o', + name: 'embedImage' + }).click(function () { + $('', {type:'file'}).on('change', onUpload).click(); + }).appendTo($rightside); + + if (framework._.sfCommon.isLoggedIn()) { + var fileDialogCfg = { + onSelect: function (data) { + if (data.type === 'file') { + var mt = ''; + framework._.sfCommon.displayMediatagImage($(mt), function (err, $image) { + Util.blobURLToImage($image.attr('src'), function (imgSrc) { + var img = new Image(); + img.onload = function () { addImageToCanvas(img); }; + img.src = imgSrc; + }); + }); + return; + } + } }; - toolbar = APP.toolbar = Toolbar.create(configTb); - Title.setToolbar(toolbar); - - var $rightside = toolbar.$rightside; - var $drawer = toolbar.$drawer; - - /* save as template */ - if (!metadataMgr.getPrivateData().isTemplate) { - var templateObj = { - rt: info.realtime, - getTitle: function () { return metadataMgr.getMetadata().title; } + framework._.sfCommon.initFilePicker(fileDialogCfg); + framework._.sfCommon.createButton('mediatag', true).click(function () { + var pickerCfg = { + types: ['file'], + where: ['root'], + filter: { + fileType: ['image/'] + } }; - var $templateButton = common.createButton('template', true, templateObj); - $rightside.append($templateButton); - } - - /* add an export button */ - var $export = common.createButton('export', true, {}, saveImage); - $drawer.append($export); - - if (common.isLoggedIn()) { - common.createButton('savetodrive', true, {}, function () {}) - .click(function () { - UI.prompt(Messages.exportPrompt, document.title + '.png', - function (name) { - if (name === null || !name.trim()) { return; } - APP.upload(name); - }); - }).appendTo($rightside); - - common.createButton('hashtag', true).appendTo($rightside); - } - - var $forget = common.createButton('forget', true, {}, function (err) { - if (err) { return; } - setEditable(false); - }); - $rightside.append($forget); - - var $properties = common.createButton('properties', true); - toolbar.$drawer.append($properties); - - var $appContainer = $('#cp-app-whiteboard-container'); - var helpMenu = common.createHelpMenu(['whiteboard']); - $appContainer.prepend(helpMenu.menu); - toolbar.$drawer.append(helpMenu.button); - - if (!readOnly) { - makeColorButton($rightside); - - // Embed image - var onUpload = function (e) { - var file = e.target.files[0]; - var reader = new FileReader(); - reader.onload = function () { - var img = new Image(); - img.onload = function () { - addImageToCanvas(img); - }; - img.src = reader.result; - }; - reader.readAsDataURL(file); - }; - common.createButton('', true, { - title: Messages.canvas_imageEmbed, - icon: 'fa-file-image-o', - name: 'embedImage' - }).click(function () { - $('', {type:'file'}).on('change', onUpload).click(); - }).appendTo($rightside); - - if (common.isLoggedIn()) { - var fileDialogCfg = { - onSelect: function (data) { - if (data.type === 'file') { - var mt = ''; - common.displayMediatagImage($(mt), function (err, $image) { - Util.blobURLToImage($image.attr('src'), function (imgSrc) { - var img = new Image(); - img.onload = function () { addImageToCanvas(img); }; - img.src = imgSrc; - }); - }); - return; - } - } - }; - common.initFilePicker(fileDialogCfg); - APP.$mediaTagButton = common.createButton('mediatag', true).click(function () { - var pickerCfg = { - types: ['file'], - where: ['root'], - filter: { - fileType: ['image/'] - } - }; - common.openFilePicker(pickerCfg); - }).appendTo($rightside); - } - } else { - $colors.hide(); - $controls.hide(); - } - - metadataMgr.onChange(function () { - var md = metadataMgr.getMetadata(); - if (md.palette) { - updateLocalPalette(md.palette); - } - }); - }; - - config.onReady = function (info) { - if (APP.realtime !== info.realtime) { - APP.realtime = info.realtime; - } - - var userDoc = APP.realtime.getUserDoc(); - var isNew = false; - var newDoc = ''; - if (userDoc === "" || userDoc === "{}") { isNew = true; } + framework._.sfCommon.openFilePicker(pickerCfg); + }).appendTo($rightside); + } - if (userDoc !== "") { - var hjson = JSON.parse(userDoc); - - if (hjson && hjson.metadata) { - metadataMgr.updateMetadata(hjson.metadata); - } - if (typeof (hjson) !== 'object' || Array.isArray(hjson) || - (hjson.metadata && typeof(hjson.metadata.type) !== 'undefined' && - hjson.metadata.type !== 'whiteboard')) { - var errorText = Messages.typeError; - UI.errorLoadingScreen(errorText); - throw new Error(errorText); - } - newDoc = hjson.content; - } else { - Title.updateTitle(Title.defaultTitle); - } + if (framework.isReadOnly()) { + setEditable(false); + } else { + controls.makeColorButton($rightside); + } - nThen(function (waitFor) { - if (newDoc) { - canvas.loadFromJSON(newDoc, waitFor(function () { - console.log('loaded'); - canvas.renderAll(); - })); - } - }).nThen(function () { - setEditable(!readOnly); - initializing = false; - config.onLocal(); - UI.removeLoadingScreen(); + $('#cp-app-whiteboard-clear').on('click', function () { + canvas.clear(); + framework.localChange(); + }); - initThumbnails(); + // --------------------------------------------- + // End custom + // --------------------------------------------- - if (readOnly) { return; } + framework.onEditableChange(function () { + var locked = framework.isLocked() || framework.isReadOnly(); + setEditable(!locked); + }); - var privateDat = metadataMgr.getPrivateData(); - var skipTemp = Util.find(privateDat, - ['settings', 'general', 'creation', 'noTemplate']); - var skipCreation = Util.find(privateDat, ['settings', 'general', 'creation', 'skip']); - if (isNew && (!AppConfig.displayCreationScreen || (!skipTemp && skipCreation))) { - common.openTemplatePicker(); - } + framework.setFileExporter('png', function (cb) { + $canvas[0].toBlob(function (blob) { + cb(blob); }); + }); - }; - - config.onRemote = function () { - if (initializing) { return; } - var userDoc = APP.realtime.getUserDoc(); - - var json = JSON.parse(userDoc); - var remoteDoc = json.content; + framework.setNormalizer(function (c) { + return { + content: c.content + }; + }); - canvas.loadFromJSON(remoteDoc, function () { + framework.onContentUpdate(function (newContent, waitFor) { + var content = newContent.content; + canvas.loadFromJSON(content, waitFor(function () { canvas.renderAll(); - if (json.metadata) { - metadataMgr.updateMetadata(json.metadata); - } - }); - - var content = canvas.toDatalessJSON(); - if (content !== remoteDoc) { common.notify(); } - if (readOnly) { setEditable(false); } - }; - - config.onAbort = function () { - if (APP.unrecoverable) { return; } - // inform of network disconnect - setEditable(false); - toolbar.failed(); - UI.alert(Messages.common_connectionLost, undefined, true); - }; - - config.onConnectionChange = function (info) { - if (APP.unrecoverable) { return; } - setEditable(info.state); - if (info.state) { - initializing = true; - //UI.findOKButton().click(); - } else { - //UI.alert(Messages.common_connectionLost, undefined, true); - } - }; - - config.onError = function (err) { - common.onServerError(err, toolbar, function () { - APP.unrecoverable = true; - setEditable(false); - }); - }; - - cpNfInner = common.startRealtime(config); - metadataMgr = cpNfInner.metadataMgr; - - cpNfInner.onInfiniteSpinner(function () { - if (APP.unrecoverable) { return; } - setEditable(false); - UI.confirm(Messages.realtime_unrecoverableError, function (yes) { - if (!yes) { return; } - common.gotoURL(); - }); + })); }); - canvas.on('mouse:up', onLocal); - - $('#cp-app-whiteboard-clear').on('click', function () { - canvas.clear(); - onLocal(); + framework.setContentGetter(function () { + var content = canvas.toDatalessJSON(); + return { + content: content + }; }); - $('#save').on('click', function () { - saveImage(); + framework.onReady(function () { + var oldThumbnailState; + var privateDat = metadataMgr.getPrivateData(); + if (!privateDat.thumbnails) { return; } + var hash = privateDat.availableHashes.editHash || + privateDat.availableHashes.viewHash; + var href = privateDat.pathname + '#' + hash; + var mkThumbnail = function () { + if (!hash) { return; } + if (framework.getState() !== 'READY') { return; } + if (!framework._.cpNfInner.chainpad) { return; } + var content = framework._.cpNfInner.chainpad.getUserDoc(); + if (content === oldThumbnailState) { return; } + var D = Thumb.getResizedDimensions($canvas[0], 'pad'); + Thumb.fromCanvas($canvas[0], D, function (err, b64) { + oldThumbnailState = content; + Thumb.setPadThumbnail(framework._.sfCommon, href, privateDat.channel, b64); + }); + }; + window.setInterval(mkThumbnail, Thumb.UPDATE_INTERVAL); + window.setTimeout(mkThumbnail, Thumb.UPDATE_FIRST); }); - common.onLogout(function () { setEditable(false); }); + canvas.on('mouse:up', framework.localChange); + framework.start(); }; var main = function () { - var common; - + // var framework; nThen(function (waitFor) { $(waitFor(function () { - UI.addLoadingScreen(); var $div = $('
').append(Pages['/whiteboard/']()); $('body').append($div.html()); })); - SFCommon.create(waitFor(function (c) { APP.common = common = c; })); }).nThen(function (waitFor) { - common.getSframeChannel().onReady(waitFor()); - }).nThen(function (waitFor) { - common.handleNewFile(waitFor); - }).nThen(function (/*waitFor*/) { - andThen(common); + + // Framework initialization + Framework.create({ + patchTransformer: ChainPad.NaiveJSONTransformer, + toolbarContainer: '#cp-toolbar', + contentContainer: '#cp-app-whiteboard-canvas-area', + }, waitFor(function (framework) { + andThen2(framework); + })); }); }; main();