apostrophe
Advanced tools
Comparing version 0.3.14 to 0.3.16
@@ -11,2 +11,4 @@ var oembed = require('oembed'); | ||
var lessMiddleware = require('less-middleware'); | ||
var hash_file = require('hash_file'); | ||
var path = require('path'); | ||
@@ -17,6 +19,6 @@ // MongoDB prefix queries are painful without this | ||
module.exports = function() { | ||
return new apos(); | ||
return new aposConstructor(); | ||
} | ||
function apos() { | ||
function aposConstructor() { | ||
var self = this; | ||
@@ -32,3 +34,3 @@ var app, files, areas, pages, uploadfs, nunjucksEnv, partial; | ||
self.defaultControls = [ 'style', 'bold', 'italic', 'createLink', 'insertUnorderedList', 'image', 'video', 'pullquote', 'code' ]; | ||
self.defaultControls = [ 'style', 'bold', 'italic', 'createLink', 'insertUnorderedList', 'slideshow', 'video', 'pullquote', 'code' ]; | ||
@@ -53,3 +55,3 @@ // These are the controls that map directly to standard document.executeCommand | ||
{ value: 'h5', label: 'Heading 5' }, | ||
{ value: 'h6', label: 'Heading 6' } | ||
{ value: 'h6', label: 'Heading 6' }, | ||
] | ||
@@ -88,11 +90,30 @@ }, | ||
self.scripts = [ | ||
'/apos/js/jquery-1.8.1.min.js', | ||
// VENDOR DEPENDENCIES | ||
// Makes broken browsers usable | ||
'/apos/js/underscore-min.js', | ||
// For everything | ||
'/apos/js/jquery-1.9.1.min.js', | ||
// For blueimp uploader, drag and drop reordering of anything, datepicker | ||
// & autocomplete | ||
'/apos/jquery-ui-1.10.1/js/jquery-ui-1.10.1.custom.min.js', | ||
// For the RTE | ||
'/apos/js/jquery.hotkeys/jquery.hotkeys.js', | ||
// For selections in the RTE | ||
'/apos/js/rangy-1.2.3/rangy-core.js', | ||
'/apos/js/rangy-1.2.3/rangy-selectionsaverestore.js', | ||
// For selections in ordinary textareas and inputs (part of Rangy) | ||
'/apos/js/textinputs_jquery.js', | ||
'/apos/js/jquery.cookie.js', | ||
// Graceful fallback for older browsers | ||
'/apos/blueimp/js/jquery.iframe-transport.js', | ||
// Spiffy multiple file upload | ||
'/apos/blueimp/js/jquery.fileupload.js', | ||
// OUR CODE | ||
// Editing functionality | ||
'/apos/js/editor.js', | ||
'/apos/js/content.js' | ||
// Viewers for standard content types | ||
'/apos/js/content.js', | ||
]; | ||
@@ -104,3 +125,3 @@ | ||
self.templates = [ | ||
'imageEditor', 'pullquoteEditor', 'videoEditor', 'codeEditor', 'hint' | ||
'slideshowEditor', 'pullquoteEditor', 'videoEditor', 'codeEditor', 'hint' | ||
]; | ||
@@ -171,6 +192,11 @@ | ||
// Make crucial data such as apos.uploadsUrl available to the browser | ||
aposLocals.aposSetVariables = function() { | ||
return partial('variables.html', { uploadsUrl: uploadfs.getUrl() }); | ||
}; | ||
// aposTemplates renders templates that are needed on any page that will | ||
// use apos. Examples: imageEditor.html, codeEditor.html, etc. These lie | ||
// dormant in the page until they are needed as prototypes to be cloned | ||
// by jQuery | ||
// use apos. Examples: slideshowEditor.html, codeEditor.html, | ||
// etc. These lie dormant in the page until they are needed as prototypes to | ||
// be cloned by jQuery | ||
@@ -213,2 +239,6 @@ aposLocals.aposTemplates = function(options) { | ||
options.item = item; | ||
if (options.item) { | ||
options.item.position = 'middle'; | ||
options.item.size = 'full'; | ||
} | ||
return partial('singleton.html', options); | ||
@@ -260,3 +290,3 @@ } | ||
} | ||
attributes[key] = value + ''; | ||
attributes[key] = value; | ||
}); | ||
@@ -294,2 +324,10 @@ return partial('itemNormalView.html', { item: item, itemType: itemType, options: options, attributes: attributes }); | ||
aposLocals.aposFilePath = function(file, options) { | ||
var path = uploadfs.getUrl() + '/files/' + file._id + '-' + file.name; | ||
if (options.size) { | ||
path += '.' + options.size; | ||
} | ||
return path + '.' + file.extension; | ||
} | ||
aposLocals.aposLog = function(m) { | ||
@@ -313,86 +351,48 @@ console.log(m); | ||
// An iframe with file browse and upload buttons. | ||
// We use an iframe because traditional file upload buttons | ||
// can't be AJAXed (although you can do that in Chrome and | ||
// Firefox it is still not supported in IE9). | ||
// Upload a file for slideshow purposes (TODO: extend to | ||
// accept non-image files when appropriate) | ||
app.post('/apos/upload-files', function(req, res) { | ||
var newFiles = req.files.files; | ||
if (!(newFiles instanceof Array)) { | ||
newFiles = [ newFiles ]; | ||
} | ||
var infos = []; | ||
async.map(newFiles, function(file, callback) { | ||
var info = { | ||
_id: generateId(), | ||
length: file.length, | ||
createdAt: new Date(), | ||
name: self.slugify(path.basename(file.name, path.extname(file.name))) | ||
}; | ||
app.get('/apos/file-iframe/:id', validId, function(req, res) { | ||
var id = req.params.id; | ||
return render(res, 'fileIframe.html', { id: id, error: false, uploaded: false }); | ||
}); | ||
async.series([ permissions, upload, db ], callback); | ||
// Deliver details about a previously uploaded file as a JSON response | ||
app.get('/apos/file-info/:id', validId, function(req, res) { | ||
var id = req.params.id; | ||
files.findOne({ _id: id }, gotFile); | ||
function gotFile(err, file) { | ||
if (err || (!file)) { | ||
res.statusCode = 404; | ||
res.send("Not Found"); | ||
return; | ||
function permissions(callback) { | ||
self.permissions(req, 'edit-media', null, callback); | ||
} | ||
self.permissions(req, 'edit-media', file, function(err) { | ||
if (err) { | ||
return forbid(res); | ||
} | ||
file.url = uploadfs.getUrl() + '/images/' + id; | ||
return res.send(file); | ||
}); | ||
} | ||
}); | ||
// An upload submitted via the iframe | ||
app.post('/apos/file-iframe/:id', validId, function(req, res) { | ||
var id = req.params.id; | ||
var file = req.files.file; | ||
if (!file) { | ||
return fail(req, res); | ||
} | ||
var src = file.path; | ||
files.findOne({ _id: id }, gotExisting); | ||
var info; | ||
function gotExisting(err, existing) { | ||
self.permissions(req, 'edit-media', existing, function(err) { | ||
if (err) { | ||
return forbid(res); | ||
} | ||
// Let uploadfs do the heavy lifting of scaling and storage to fs or s3 | ||
return uploadfs.copyImageIn(src, '/images/' + id, update); | ||
}); | ||
} | ||
function update(err, infoArg) { | ||
if (err) { | ||
return fail(req, res); | ||
function upload(callback) { | ||
return uploadfs.copyImageIn(file.path, '/files/' + info._id + '-' + info.name, function(err, result) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
info.extension = result.extension; | ||
return callback(null); | ||
}); | ||
} | ||
info = infoArg; | ||
info._id = id; | ||
info.name = self.slugify(file.name); | ||
info.createdAt = new Date(); | ||
// Do our best to record who owns this file to allow permissions | ||
// checks later. If req.user exists and has an _id, id or username property, | ||
// record that | ||
if (req.user) { | ||
info.owner = req.user._id || req.user.id || req.user.username; | ||
function db(callback) { | ||
console.log(info); | ||
files.insert(info, { safe: true }, function(err, docs) { | ||
if (!err) { | ||
infos.push(docs[0]); | ||
} | ||
return callback(err); | ||
}); | ||
} | ||
files.update({ _id: info._id }, info, { upsert: true, safe: true }, inserted); | ||
} | ||
function inserted(err) { | ||
info.uploaded = true; | ||
info.error = false; | ||
info.id = info._id; | ||
render(res, 'fileIframe.html', info); | ||
} | ||
function fail(req, res) { | ||
return render(res, 'fileIframe.html', { id: id, error: "Not a GIF, JPEG or PNG image file", uploaded: false }); | ||
} | ||
}, function(err) { | ||
console.log('result:'); | ||
console.log(infos); | ||
return res.send({ files: infos, status: 'ok' }); | ||
}); | ||
}); | ||
@@ -523,2 +523,5 @@ | ||
console.log('The item as I received it is:'); | ||
console.log(item); | ||
var itemType = self.itemTypes[item.type]; | ||
@@ -924,10 +927,11 @@ if (!itemType) { | ||
}, | ||
image: { | ||
slideshow: { | ||
widget: true, | ||
label: 'Image', | ||
label: 'Slideshow', | ||
icon: 'image', | ||
// icon: 'slideshow', | ||
render: function(data) { | ||
return partial('image.html', data); | ||
return partial('slideshow.html', data); | ||
}, | ||
css: 'image' | ||
css: 'slideshow' | ||
}, | ||
@@ -968,2 +972,16 @@ video: { | ||
}); | ||
nunjucksEnv.addFilter('jsonAttribute', function(data) { | ||
// Leverage jQuery's willingness to parse attributes as JSON objects and arrays | ||
// if they look like it. TODO: find out if this still works cross browser with | ||
// single quotes, all the double escaping is unfortunate | ||
if (typeof(data) === 'object') { | ||
console.log('it is an object'); | ||
return JSON.stringify(data).replace(/\&/g, '&').replace(/\</g, '<').replace(/\>/g, '>').replace(/\"/g, '"'); | ||
} else { | ||
console.log('it is not an object'); | ||
// Make it a string for sure | ||
data += ''; | ||
return data.replace(/\&/g, '&').replace(/\</g, '<').replace(/\>/g, '>').replace(/\"/g, '"'); | ||
} | ||
}); | ||
return nunjucksEnv; | ||
@@ -989,2 +1007,3 @@ } | ||
var regex = new RegExp(r, 'g'); | ||
console.log(r); | ||
s = s.replace(regex, '-'); | ||
@@ -991,0 +1010,0 @@ // Consecutive dashes become one dash |
{ | ||
"name": "apostrophe", | ||
"version": "0.3.14", | ||
"version": "0.3.16", | ||
"description": "Apostrophe is a user-friendly content management system. This core module of Apostrophe provides rich content editing and essential facilities to integrate Apostrophe into your Express project. Apostrophe also includes simple facilities for storing your rich content areas in MongoDB and fetching them back again. Additional functionality is available in modules like apostrophe-twitter and apostrophe-rss and forthcoming modules that address page trees, blog posts, events and the like.", | ||
@@ -16,3 +16,4 @@ "main": "apostrophe.js", | ||
"validator": "~0.4.19", | ||
"less-middleware": "~0.1.9" | ||
"less-middleware": "~0.1.9", | ||
"hash_file": "0.0.8" | ||
}, | ||
@@ -19,0 +20,0 @@ "devDependencies": {}, |
@@ -25,3 +25,52 @@ if (!window.apos) { | ||
// An example: the video player replaces a thumbnail with | ||
apos.widgetPlayers.slideshow = function($widget) | ||
{ | ||
// TODO take the interval from data attributes, add arrows based on | ||
// data attributes, and so on. Include controls in the form to set | ||
// those options. Consider allowing for titles and descriptions. | ||
// Support all those cool fades we love to do. Et cetera, et cetera. | ||
var interval = setInterval(function() { | ||
var $current = $widget.find('[data-slideshow-item].apos-current'); | ||
if (!$current.length) { | ||
// Widget has gone away. Kill the interval timer and go away too | ||
clearInterval(interval); | ||
return; | ||
} | ||
var $next = $current.next(); | ||
if (!$next.length) { | ||
$next = $current.closest('[data-slideshow-items]').find('[data-slideshow-item]:first'); | ||
} | ||
$current.removeClass('apos-current'); | ||
$next.addClass('apos-current'); | ||
}, 5000); | ||
// Wait until the height of all images is known, then | ||
// set the height of the entire slideshow. This prevents | ||
// the clearly unacceptable "jumping" behavior. | ||
function adjustSize() { | ||
var ready = true; | ||
var maxHeight = 0; | ||
$widget.find('[data-image]').each(function(i, item) { | ||
if (!item.complete) { | ||
ready = false; | ||
return; | ||
} | ||
var height = $(item).height(); | ||
if (height > maxHeight) { | ||
maxHeight = height; | ||
} | ||
}); | ||
if (ready) { | ||
$widget.find('[data-slideshow-items]').height(maxHeight); | ||
} else { | ||
setTimeout(adjustSize, 100); | ||
} | ||
} | ||
adjustSize(); | ||
} | ||
// The video player replaces a thumbnail with | ||
// a suitable player via apos's oembed proxy | ||
@@ -28,0 +77,0 @@ |
@@ -91,5 +91,3 @@ if (!window.apos) { | ||
apos.log(styleMenu.length); | ||
styleMenu.find('option').each(function() { | ||
apos.log(this); | ||
styleBlockElements[$(this).val()] = true; | ||
@@ -266,7 +264,5 @@ }); | ||
$(node).before(p); | ||
apos.log('prepended prev element'); | ||
} else { | ||
var p = node.previousSibling.nodeValue; | ||
if (p.substr(p.length - 1, 1) !== before) { | ||
apos.log('appended prev character'); | ||
node.previousSibling.nodeValue += before; | ||
@@ -280,3 +276,2 @@ } | ||
$(node).after(p); | ||
apos.log('appended next element'); | ||
} else { | ||
@@ -286,3 +281,2 @@ var n = node.nextSibling.nodeValue; | ||
node.nextSibling.nodeValue = after + node.nextSibling.nodeValue; | ||
apos.log('prepended character to next'); | ||
} | ||
@@ -358,3 +352,2 @@ } | ||
self.destroy = function() { | ||
apos.log('destroying editor'); | ||
_.map(self.timers, function(timer) { clearInterval(timer); }); | ||
@@ -569,3 +562,4 @@ }; | ||
var $buttons = $('<div class="apos-widget-buttons"></div>'); | ||
var $button = $('<div class="apos-widget-button apos-edit-widget">Edit ' + apos.widgetTypes[$widget.attr('data-type')].label + '</div>'); | ||
// var $button = $('<div class="apos-widget-button apos-edit-widget">Edit ' + apos.widgetTypes[$widget.attr('data-type')].label + '</div>'); | ||
var $button = $('<div class="apos-widget-button apos-edit-widget"><i class="icon-pencil"></i></div>'); | ||
$buttons.append($button); | ||
@@ -600,3 +594,2 @@ var $button = $('<div class="apos-widget-button apos-insert-before-widget">Before</div>'); | ||
self.data = options.data; | ||
apos.log(self.data); | ||
} | ||
@@ -621,32 +614,86 @@ | ||
// Make our own instance of the editor template | ||
self.$el = $(options.template + '.apos-template').clone(); | ||
self.$el.removeClass('.apos-template'); | ||
self.$previewContainer = self.$el.find('.apos-widget-preview-container'); | ||
if (self.afterCreatingEl) { | ||
self.afterCreatingEl(); | ||
} | ||
self.$el.find('[data-preview]').click(function() { | ||
self.preview(); | ||
return false; | ||
// Use apos.modalFromTemplate to manage our lifecycle as a modal | ||
self.$el = apos.modalFromTemplate(options.template, { | ||
init: function(callback) { | ||
self.$previewContainer = self.$el.find('.apos-widget-preview-container'); | ||
if (self.afterCreatingEl) { | ||
self.afterCreatingEl(); | ||
} | ||
self.$el.find('[data-preview]').click(function() { | ||
self.preview(); | ||
return false; | ||
}); | ||
if (options.fixedPositionAndSize) { | ||
self.$el.find('.apos-position,.apos-size,.apos-lorem').remove(); | ||
} else { | ||
self.$el.find('input[type=radio]').change(function() { | ||
self.changeSizeAndPosition(); | ||
}); | ||
} | ||
var sizeAndPosition = { size: 'two-thirds', position: 'middle' }; | ||
if (self.exists) { | ||
sizeAndPosition.size = self.data.size; | ||
sizeAndPosition.position = self.data.position; | ||
} | ||
self.$el.find('input[name="size"]').prop('checked', false); | ||
self.$el.find('input[name="size"][value="' + sizeAndPosition.size + '"]').prop('checked', true); | ||
self.$el.find('input[name="position"]').prop('checked', false); | ||
self.$el.find('input[name="position"][value="' + sizeAndPosition.position + '"]').prop('checked', true); | ||
self.preview(); | ||
return callback(null); | ||
}, | ||
save: function(callback) { | ||
self.preSave(function() { | ||
if (!self.exists) { | ||
alert(options.messages.missing); | ||
return callback('error'); | ||
} | ||
if (self.editor) { | ||
self.editor.undoPoint(); | ||
var _new = false; | ||
if (!self.$widget) { | ||
self.createWidget(); | ||
_new = true; | ||
} | ||
} | ||
self.updateWidget(function(err) { | ||
if (_new) { | ||
self.insertWidget(); | ||
// apos.hint('What are the arrows for?', "<p>They are there to show you where to add other content before and after your rich content.</p><p>Always type text before or after the arrows, never between them.</p><p>This is especially helpful when you are floating content next to text.</p><p>You can click your rich content to select it along with its arrows, then cut, copy or paste as usual.</p><p>Don\'t worry, the arrows automatically disappear when you save your work.</p>"); | ||
} | ||
// Widget backups are probably a bad idea since they would defeat | ||
// copy and paste between pages or at least sites | ||
// self.editor.updateWidgetBackup(self.widgetId, self.$widget); | ||
if (options.save) { | ||
// Used to implement save for singletons and non-rich-text-based areas. | ||
// Note that in this case options.data was passed in by reference, | ||
// so the end result can be read there. Pay attention to the callback so | ||
// we can allow the user a second chance | ||
options.save(function(err) { | ||
return callback(err); | ||
}); | ||
} else { | ||
return callback(null); | ||
} | ||
}); | ||
}); | ||
}, | ||
afterHide: function(callback) { | ||
self.afterHide(); | ||
return callback(null); | ||
} | ||
}); | ||
self.$el.find('[data-action="dismiss"]').click(function() { | ||
self.destroy(); | ||
}); | ||
self.$el.find('input[type=radio]').change(function() { | ||
self.changeSizeAndPosition(); | ||
}); | ||
// Default methods | ||
_.defaults(self, { | ||
destroy: function() { | ||
self.modal('hide'); | ||
afterHide: function() { | ||
_.map(self.timers, function(timer) { clearInterval(timer); }); | ||
// Let it go away pretty, then remove it from the DOM | ||
setTimeout(function() { | ||
self.$el.remove(); | ||
}, 500); | ||
// Return focus to the main editor | ||
if (self.editor) { | ||
self.editor.$editable.focus(); | ||
} | ||
}, | ||
@@ -738,4 +785,3 @@ | ||
_.each(self.data, function(val, key) { | ||
apos.log(key + ': ' + val); | ||
self.$widget.attr('data-' + key, val); | ||
self.$widget.attr('data-' + key, apos.jsonAttribute(val)); | ||
}); | ||
@@ -751,2 +797,4 @@ }, | ||
var info = self.$widget.data(); | ||
console.log('rendering these attributes:'); | ||
console.log(info); | ||
@@ -803,9 +851,2 @@ // Some widgets have content - markup that goes inside the widget | ||
modal: function(command) { | ||
// TODO: this should utilize all the good stuff in | ||
// apos.modal, not just apos._modalCore, I need to | ||
// refactor out the redundancies | ||
return apos._modalCore(self.$el, command); | ||
}, | ||
preview: function() { | ||
@@ -851,71 +892,14 @@ if (self.prePreview) { | ||
} | ||
} | ||
}); | ||
}, | ||
self.$el.find('.apos-save').click(function() { | ||
self.preSave(function() { | ||
if (!self.exists) { | ||
alert(options.messages.missing); | ||
return false; | ||
} | ||
if (self.editor) { | ||
self.editor.undoPoint(); | ||
var _new = false; | ||
if (!self.$widget) { | ||
self.createWidget(); | ||
_new = true; | ||
} | ||
} | ||
self.updateWidget(function(err) { | ||
if (_new) { | ||
self.insertWidget(); | ||
// apos.hint('What are the arrows for?', "<p>They are there to show you where to add other content before and after your rich content.</p><p>Always type text before or after the arrows, never between them.</p><p>This is especially helpful when you are floating content next to text.</p><p>You can click your rich content to select it along with its arrows, then cut, copy or paste as usual.</p><p>Don\'t worry, the arrows automatically disappear when you save your work.</p>"); | ||
} | ||
// Widget backups are probably a bad idea since they would defeat | ||
// copy and paste between pages or at least sites | ||
// self.editor.updateWidgetBackup(self.widgetId, self.$widget); | ||
if (options.save) { | ||
// Used to implement save for singletons and non-rich-text-based areas. | ||
// Note that in this case options.data was passed in by reference, | ||
// so the end result can be read there. Pay attention to the callback so | ||
// we can allow the user a second chance | ||
options.save(function(err) { | ||
if (!err) { | ||
self.destroy(); | ||
} | ||
}); | ||
} else { | ||
self.destroy(); | ||
} | ||
}); | ||
}); | ||
return false; | ||
}); | ||
// Override if you need to carry out an action such | ||
// as fetching video information before the save can | ||
// take place. Takes a callback which completes the | ||
// save operation, or gracefully refuses it if you | ||
// don't set self.exists to true. Use of a callback | ||
// allows you to asynchronously fetch video information, etc. | ||
if (!self.preSave) { | ||
self.preSave = function(callback) { | ||
// Override if you need to carry out an action such | ||
// as fetching video information before the save can | ||
// take place. Takes a callback which completes the | ||
// save operation, or gracefully refuses it if you | ||
// don't set self.exists to true. Use of a callback | ||
// allows you to asynchronously fetch video information, etc. | ||
preSave: function(callback) { | ||
callback(); | ||
} | ||
} | ||
var sizeAndPosition = { size: 'two-thirds', position: 'middle' }; | ||
if (self.exists) { | ||
sizeAndPosition.size = self.data.size; | ||
sizeAndPosition.position = self.data.position; | ||
} | ||
self.$el.find('input[name="size"]').prop('checked', false); | ||
self.$el.find('input[name="size"][value="' + sizeAndPosition.size + '"]').prop('checked', true); | ||
self.$el.find('input[name="position"]').prop('checked', false); | ||
self.$el.find('input[name="position"][value="' + sizeAndPosition.position + '"]').prop('checked', true); | ||
self.preview(); | ||
self.modal(); | ||
}); | ||
} | ||
@@ -925,6 +909,7 @@ | ||
apos.widgetTypes.image = { | ||
label: 'Image', | ||
apos.widgetTypes.slideshow = { | ||
label: 'Slideshow', | ||
editor: function(options) { | ||
var self = this; | ||
var $items; | ||
@@ -939,31 +924,90 @@ if (!options.messages) { | ||
self.afterCreatingEl = function() { | ||
self.$el.find('[data-iframe-placeholder]').replaceWith($('<iframe id="iframe-' + self.data.id + '" name="iframe-' + self.data.id + '" class="apos-file-iframe" src="/apos/file-iframe/' + self.data.id + '"></iframe>')); | ||
self.$el.bind('uploaded', function(e, id) { | ||
// Only react to events intended for us | ||
if (id === self.data.id) { | ||
self.exists = true; | ||
$items = self.$el.find('[data-items]'); | ||
$items.sortable({ | ||
update: function(event, ui) { | ||
reflect(); | ||
self.preview(); | ||
} | ||
}); | ||
self.files = []; | ||
self.$el.find('[data-uploader]').fileupload({ | ||
dataType: 'json', | ||
// This is nice in a multiuser scenario, it prevents slamming, | ||
// but I need to figure out why it's necessary to avoid issues | ||
// with node-imagemagick | ||
sequentialUploads: true, | ||
start: function (e) { | ||
$('[data-progress]').show(); | ||
$('[data-finished]').hide(); | ||
}, | ||
stop: function (e) { | ||
$('[data-progress]').hide(); | ||
$('[data-finished]').show(); | ||
}, | ||
progressall: function (e, data) { | ||
var progress = parseInt(data.loaded / data.total * 100, 10); | ||
self.$el.find('[data-progress-percentage]').text(progress); | ||
}, | ||
done: function (e, data) { | ||
if (data.result.files) { | ||
_.each(data.result.files, function (file) { | ||
addItem(file); | ||
}); | ||
reflect(); | ||
self.preview(); | ||
} | ||
} | ||
}); | ||
}; | ||
// For images, preview is triggered by an upload. We need to capture | ||
// the file extension at that point so we are not forced to constantly | ||
// look it up in a separate collection | ||
// The server will render an actual slideshow, but we also want to see | ||
// thumbnails of everything with draggability for reordering and remove buttons | ||
self.prePreview = function(callback) { | ||
if (self.exists) { | ||
$.getJSON('/apos/file-info/' + self.data.id, function(info) { | ||
self.data.extension = info.extension; | ||
callback(); | ||
}); | ||
console.log('self.data in prePreview'); | ||
console.log(self.data); | ||
$items.find('[data-item]:not(.apos-template)').remove(); | ||
var items = self.data.items; | ||
if (!items) { | ||
items = []; | ||
} | ||
else | ||
{ | ||
callback(); | ||
} | ||
_.each(items, function(item) { | ||
addItem(item); | ||
}); | ||
callback(); | ||
}; | ||
self.type = 'image'; | ||
options.template = '.apos-image-editor'; | ||
function addItem(item) { | ||
var $item = $items.find('[data-item].apos-template').clone(); | ||
$item.removeClass('apos-template'); | ||
$item.find('[data-image]').attr('src', apos.uploadsUrl + '/files/' + item._id + '-' + item.name + '.one-third.' + item.extension); | ||
$item.data('item', item); | ||
$item.click('[data-remove]', function() { | ||
$item.remove(); | ||
reflect(); | ||
self.preview(); | ||
return false; | ||
}); | ||
$items.append($item); | ||
} | ||
// Update the data attributes to match what is found in the | ||
// list of items. This is called after remove and reorder events | ||
function reflect() { | ||
$itemElements = $items.find('[data-item]:not(.apos-template)'); | ||
self.data.items = []; | ||
$.each($itemElements, function(i, item) { | ||
var $item = $(item); | ||
self.data.items.push($item.data('item')); | ||
}); | ||
// An empty slideshow is allowed, so permit it to be saved | ||
// even if nothing has been added | ||
self.exists = true; | ||
} | ||
self.type = 'slideshow'; | ||
options.template = '.apos-slideshow-editor'; | ||
// Parent class constructor shared by all widget editors | ||
@@ -992,3 +1036,3 @@ apos.widgetEditor.call(self, options); | ||
// fresh paste | ||
var last = ''; | ||
var last = self.data.video; | ||
self.timers.push(setInterval(function() { | ||
@@ -1263,3 +1307,3 @@ var next = self.$embed.val(); | ||
var editView = $('<div class="apos-edit-view"></div>'); | ||
editView.append($(data)); | ||
editView.append(data); | ||
area.append(editView); | ||
@@ -1305,3 +1349,3 @@ area.find('.apos-normal-view').hide(); | ||
var itemData = {}; | ||
var itemData = { position: 'middle', size: 'full' }; | ||
var $item = $singleton.find('.apos-content .apos-widget :first'); | ||
@@ -1312,5 +1356,5 @@ if ($item.length) { | ||
new apos.widgetTypes[type].editor({ | ||
fixedPositionAndSize: true, | ||
data: itemData, | ||
save: function(callback) { | ||
apos.log(itemData); | ||
$.post('/apos/edit-singleton', | ||
@@ -1359,14 +1403,6 @@ { | ||
item.content = apos.widgetTypes[type].getContent($(child)); | ||
apos.log(item.content); | ||
} | ||
for (var j = 0; (j < child.attributes.length); j++) { | ||
var key = child.attributes[j].name; | ||
var value = child.attributes[j].value; | ||
var matches = key.match(/^data\-(.*)$/); | ||
if (matches) { | ||
var name = matches[1]; | ||
item[name] = value; | ||
} | ||
} | ||
var data = $(child).data(); | ||
_.extend(item, data); | ||
items.push(item); | ||
@@ -1412,5 +1448,5 @@ } else { | ||
var blackout = $('<div class="apos-modal-blackout"></div>'); | ||
$('body').append(blackout); | ||
$el.offset({ top: $('body').scrollTop() + 200, left: ($(window).width() - 600) / 2 }); | ||
$('body').append(blackout); | ||
$('body').append($el); | ||
$el.offset({ top: $('body').scrollTop() + 100, left: ($(window).width() - $el.outerWidth()) / 2 }); | ||
$el.show(); | ||
@@ -1478,3 +1514,2 @@ } | ||
$el.find('.apos-save').click(); | ||
apos.log('triggered save'); | ||
return false; | ||
@@ -1640,1 +1675,15 @@ }); | ||
} | ||
// For use when storing DOM attributes meant to be compatible | ||
// with jQuery's built-in support for JSON parsing of | ||
// objects and arrays in data attributes. We'll be passing | ||
// the result to .attr, so we don't have to worry about | ||
// HTML escaping. | ||
apos.jsonAttribute = function(value) { | ||
if (typeof(value) === 'object') { | ||
return JSON.stringify(value); | ||
} else { | ||
return value; | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3915692
193
18617
11
30
+ Addedhash_file@0.0.8
+ Addedhash_file@0.0.8(transitive)