mirror of https://github.com/artf/grapesjs.git
256 changed files with 19595 additions and 6156 deletions
@ -1,5 +1,5 @@ |
|||||
language: node_js |
language: node_js |
||||
node_js: |
node_js: |
||||
- "4.0" |
- "5.6" |
||||
before_script: |
before_script: |
||||
- npm run build |
- npm run build |
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -1,52 +1,17 @@ |
|||||
define(function () { |
define(function () { |
||||
return { |
return { |
||||
|
|
||||
// Style prefix
|
|
||||
stylePrefix : 'am-', |
|
||||
|
|
||||
// Default assets
|
// Default assets
|
||||
assets : [], |
assets: [], |
||||
|
|
||||
// Indicates which storage to use. Available: local | remote
|
|
||||
storageType : 'local', |
|
||||
|
|
||||
// The name that will be used to identify assets inside storage.
|
|
||||
// If empty will be used: prefix + 'assets'
|
|
||||
storageName : 'assets', |
|
||||
|
|
||||
// Where store remote assets
|
|
||||
urlStore : 'http://localhost/assets/store', |
|
||||
|
|
||||
// Where fetch remote assets
|
|
||||
urlLoad : 'http://localhost/assets/load', |
|
||||
|
|
||||
// Custom parameters to pass with set request
|
|
||||
paramsStore : {}, |
|
||||
|
|
||||
// Custom parameters to pass with get request
|
// Style prefix
|
||||
paramsLoad : {}, |
stylePrefix: 'am-', |
||||
|
|
||||
// Callback before request
|
|
||||
beforeSend : function(jqXHR,settings){}, |
|
||||
|
|
||||
// Callback after request
|
|
||||
onComplete : function(jqXHR,status){}, |
|
||||
|
|
||||
// Url where uploads will be send
|
// Url where uploads will be send
|
||||
urlUpload : 'http://localhost/assets/upload', |
upload: 'http://localhost/assets/upload', |
||||
|
|
||||
// Text on upload input
|
// Text on upload input
|
||||
uploadText : 'Drop files here or click to upload', |
uploadText: 'Drop files here or click to upload', |
||||
|
|
||||
// Disable upload input
|
|
||||
disableUpload : false, |
|
||||
|
|
||||
// Store assets data where the new one is added or deleted
|
|
||||
storeOnChange : true, |
|
||||
|
|
||||
// It could be useful avoid to send other requests, for saving assets,
|
|
||||
// after each upload because the uploader script has already done it
|
|
||||
storeAfterUpload : false, |
|
||||
|
|
||||
}; |
}; |
||||
}); |
}); |
||||
@ -1,74 +1,222 @@ |
|||||
|
/** |
||||
|
* * [add](#add) |
||||
|
* * [get](#get) |
||||
|
* * [getAll](#getall) |
||||
|
* * [remove](#remove) |
||||
|
* * [store](#store) |
||||
|
* * [load](#load) |
||||
|
* |
||||
|
* Before using this methods you should get first the module from the editor instance, in this way: |
||||
|
* |
||||
|
* ```js
|
||||
|
* var assetManager = editor.AssetManager; |
||||
|
* ``` |
||||
|
* |
||||
|
* @module AssetManager |
||||
|
* @param {Object} config Configurations |
||||
|
* @param {Array<Object>} [config.assets=[]] Default assets |
||||
|
* @param {String} [config.uploadText='Drop files here or click to upload'] Upload text |
||||
|
* @param {String} [config.upload=''] Where to send upload data. Expects as return a JSON with asset/s object |
||||
|
* as: {data: [{src:'...'}, {src:'...'}]} |
||||
|
* @return {this} |
||||
|
* @example |
||||
|
* ... |
||||
|
* { |
||||
|
* assets: [ |
||||
|
* {src:'path/to/image.png'}, |
||||
|
* ... |
||||
|
* ], |
||||
|
* upload: 'http://dropbox/path', // Set to false to disable it
|
||||
|
* uploadText: 'Drop files here or click to upload', |
||||
|
* } |
||||
|
*/ |
||||
define(function(require) { |
define(function(require) { |
||||
/** |
|
||||
* @class AssetManager |
|
||||
* @param {Object} Configurations |
|
||||
* |
|
||||
* @return {Object} |
|
||||
* */ |
|
||||
var AssetManager = function(config) |
|
||||
{ |
|
||||
var c = config || {}, |
|
||||
defaults = require('./config/config'), |
|
||||
Assets = require('./model/Assets'), |
|
||||
AssetsView = require('./view/AssetsView'), |
|
||||
FileUpload = require('./view/FileUploader'); |
|
||||
|
|
||||
for (var name in defaults) { |
|
||||
if (!(name in c)) |
|
||||
c[name] = defaults[name]; |
|
||||
} |
|
||||
|
|
||||
this.assets = new Assets(c.assets); |
|
||||
var obj = { |
|
||||
collection : this.assets, |
|
||||
config : c, |
|
||||
}; |
|
||||
|
|
||||
this.am = new AssetsView(obj); |
return function() { |
||||
this.fu = new FileUpload(obj); |
var c = {}, |
||||
}; |
Assets = require('./model/Assets'), |
||||
|
AssetsView = require('./view/AssetsView'), |
||||
|
FileUpload = require('./view/FileUploader'), |
||||
|
assets, am, fu; |
||||
|
|
||||
|
return { |
||||
|
|
||||
|
/** |
||||
|
* Name of the module |
||||
|
* @type {String} |
||||
|
* @private |
||||
|
*/ |
||||
|
name: 'AssetManager', |
||||
|
|
||||
|
/** |
||||
|
* Mandatory for the storage manager |
||||
|
* @type {String} |
||||
|
* @private |
||||
|
*/ |
||||
|
storageKey: 'assets', |
||||
|
|
||||
|
/** |
||||
|
* Initialize module |
||||
|
* @param {Object} config Configurations |
||||
|
* @private |
||||
|
*/ |
||||
|
init: function(config){ |
||||
|
c = config || {}; |
||||
|
var defaults = require('./config/config'); |
||||
|
|
||||
AssetManager.prototype = { |
for (var name in defaults) { |
||||
|
if (!(name in c)) |
||||
|
c[name] = defaults[name]; |
||||
|
} |
||||
|
|
||||
|
var ppfx = c.pStylePrefix; |
||||
|
if(ppfx) |
||||
|
c.stylePrefix = ppfx + c.stylePrefix; |
||||
|
|
||||
|
assets = new Assets(c.assets); |
||||
|
var obj = { |
||||
|
collection: assets, |
||||
|
config: c, |
||||
|
}; |
||||
|
am = new AssetsView(obj); |
||||
|
fu = new FileUpload(obj); |
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
/** |
/** |
||||
* Get collection of assets |
* Add new asset/s to the collection. URLs are supposed to be unique |
||||
|
* @param {string|Object|Array<string>|Array<Object>} asset URL strings or an objects representing the resource. |
||||
|
* @return {Model} |
||||
|
* @example |
||||
|
* // In case of strings, would be interpreted as images
|
||||
|
* assetManager.add('http://img.jpg'); |
||||
|
* assetManager.add(['http://img.jpg', './path/to/img.png']); |
||||
* |
* |
||||
* @return {Object} |
* // Using objects you could indicate the type and other meta informations
|
||||
* */ |
* assetManager.add({ |
||||
getAssets : function(){ |
* src: 'http://img.jpg', |
||||
return this.assets; |
* //type: 'image', //image is default
|
||||
|
* height: 300, |
||||
|
* width: 200, |
||||
|
* }); |
||||
|
* assetManager.add([{ |
||||
|
* src: 'http://img.jpg', |
||||
|
* },{ |
||||
|
* src: './path/to/img.png', |
||||
|
* }]); |
||||
|
*/ |
||||
|
add: function(asset){ |
||||
|
return assets.add(asset); |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Set new target |
* Returns the asset by URL |
||||
* @param {Object} m Model |
* @param {string} src URL of the asset |
||||
* |
* @return {Object} Object representing the asset |
||||
* @return void |
* @example |
||||
* */ |
* var asset = assetManager.get('http://img.jpg'); |
||||
setTarget : function(m){ |
*/ |
||||
this.am.collection.target = m; |
get: function(src){ |
||||
|
return assets.where({src: src})[0]; |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Set callback after asset was selected |
* Return all assets |
||||
* @param {Object} f Callback function |
* @return {Collection} |
||||
|
*/ |
||||
|
getAll: function(){ |
||||
|
return assets; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Remove the asset by its URL |
||||
|
* @param {string} src URL of the asset |
||||
|
* @return {this} |
||||
|
* @example |
||||
|
* assetManager.remove('http://img.jpg'); |
||||
|
*/ |
||||
|
remove: function(src){ |
||||
|
var asset = this.get(src); |
||||
|
this.getAll().remove(asset); |
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Store assets data to the selected storage |
||||
|
* @param {Boolean} noStore If true, won't store |
||||
|
* @return {Object} Data to store |
||||
|
* @example |
||||
|
* var assets = assetManager.store(); |
||||
|
*/ |
||||
|
store: function(noStore){ |
||||
|
var obj = {}; |
||||
|
var assets = JSON.stringify(this.getAll().toJSON()); |
||||
|
obj[this.storageKey] = assets; |
||||
|
if(!noStore && c.stm) |
||||
|
c.stm.store(obj); |
||||
|
return obj; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Load data from the passed object, if the object is empty will try to fetch them |
||||
|
* autonomously from the storage manager. |
||||
|
* The fetched data will be added to the collection |
||||
|
* @param {Object} data Object of data to load |
||||
|
* @return {Object} Loaded assets |
||||
|
* @example |
||||
|
* var assets = assetManager.load(); |
||||
|
* // The format below will be used by the editor model
|
||||
|
* // to load automatically all the stuff
|
||||
|
* var assets = assetManager.load({ |
||||
|
* assets: [...] |
||||
|
* }); |
||||
* |
* |
||||
* @return void |
*/ |
||||
* */ |
load: function(data){ |
||||
onSelect : function(f){ |
var d = data || ''; |
||||
this.am.collection.onSelect = f; |
var name = this.storageKey; |
||||
|
if(!d && c.stm) |
||||
|
d = c.stm.load(name); |
||||
|
var assets = []; |
||||
|
try{ |
||||
|
assets = JSON.parse(d[name]); |
||||
|
}catch(err){} |
||||
|
this.getAll().add(assets); |
||||
|
return assets; |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Render |
* Render assets |
||||
* @param {Boolean} f Force to render |
* @param {Boolean} f Force to render, otherwise cached version will be returned |
||||
|
* @return {HTMLElement} |
||||
|
* @private |
||||
*/ |
*/ |
||||
render : function(f){ |
render: function(f){ |
||||
if(!this.rendered || f) |
if(!this.rendered || f) |
||||
this.rendered = this.am.render().$el.add(this.fu.render().$el); |
this.rendered = am.render().$el.add(fu.render().$el); |
||||
return this.rendered; |
return this.rendered; |
||||
}, |
}, |
||||
}; |
|
||||
|
|
||||
return AssetManager; |
//-------
|
||||
|
|
||||
|
/** |
||||
|
* Set new target |
||||
|
* @param {Object} m Model |
||||
|
* @private |
||||
|
* */ |
||||
|
setTarget: function(m){ |
||||
|
am.collection.target = m; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Set callback after asset was selected |
||||
|
* @param {Object} f Callback function |
||||
|
* @private |
||||
|
* */ |
||||
|
onSelect: function(f){ |
||||
|
am.collection.onSelect = f; |
||||
|
}, |
||||
|
|
||||
|
}; |
||||
|
}; |
||||
}); |
}); |
||||
@ -1,36 +1,31 @@ |
|||||
define(['backbone'], |
define(['backbone'], |
||||
function (Backbone) { |
function (Backbone) { |
||||
/** |
return Backbone.Model.extend({ |
||||
* @class Asset |
|
||||
* */ |
idAttribute: 'src', |
||||
return Backbone.Model.extend({ |
|
||||
|
|
||||
defaults: { |
defaults: { |
||||
type: 'none', //Type of the asset
|
type: '', |
||||
src: '', //Location
|
src: '', |
||||
}, |
}, |
||||
|
|
||||
initialize: function(options) { |
|
||||
this.options = options || {}; |
|
||||
}, |
|
||||
|
|
||||
/** |
/** |
||||
* Get filename of the asset |
* Get filename of the asset |
||||
* |
* @return {string} |
||||
* @return {String} |
* @private |
||||
* */ |
* */ |
||||
getFilename: function(){ |
getFilename: function(){ |
||||
return this.get('src').split('/').pop(); |
return this.get('src').split('/').pop(); |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Get extension of the asset |
* Get extension of the asset |
||||
* |
* @return {string} |
||||
* @return {String} |
* @private |
||||
* */ |
* */ |
||||
getExtension: function(){ |
getExtension: function(){ |
||||
return this.getFilename().split('.').pop(); |
return this.getFilename().split('.').pop(); |
||||
}, |
}, |
||||
|
|
||||
}); |
}); |
||||
}); |
}); |
||||
|
|||||
@ -1,18 +1,13 @@ |
|||||
define(['backbone', './Asset'], |
define(['backbone', './Asset'], |
||||
function (Backbone, Asset) { |
function (Backbone, Asset) { |
||||
/** |
return Asset.extend({ |
||||
* @class AssetImage |
|
||||
* */ |
defaults: _.extend({}, Asset.prototype.defaults, { |
||||
return Asset.extend({ |
type: 'image', |
||||
|
unitDim: 'px', |
||||
defaults: _.extend({},Asset.prototype.defaults, |
height: 0, |
||||
{ |
width: 0, |
||||
type: 'image', |
}), |
||||
unitDim: 'px', |
|
||||
height: 0, |
|
||||
width: 0, |
|
||||
} |
|
||||
), |
|
||||
|
|
||||
}); |
}); |
||||
}); |
}); |
||||
|
|||||
@ -1,11 +1,67 @@ |
|||||
define(['backbone','./Asset'], |
define(['backbone', './Asset', './AssetImage'], |
||||
function (Backbone, Asset) { |
function (Backbone, Asset, AssetImage) { |
||||
/** |
return Backbone.Collection.extend({ |
||||
* @class Assets |
|
||||
* */ |
model: AssetImage, |
||||
return Backbone.Collection.extend({ |
|
||||
|
initialize: function(models, opt){ |
||||
model: Asset, |
|
||||
|
this.model = function(attrs, options) { |
||||
|
var model; |
||||
|
switch(attrs.type){ |
||||
|
default: |
||||
|
model = new AssetImage(attrs, options); |
||||
|
} |
||||
|
return model; |
||||
|
}; |
||||
|
|
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Add new image asset to the collection |
||||
|
* @param {string} url URL of the image |
||||
|
* @param {Object} opts Options |
||||
|
* @return {this} |
||||
|
* @private |
||||
|
*/ |
||||
|
addImg: function(url, opts){ |
||||
|
this.add({ |
||||
|
type: 'image', |
||||
|
src: url, |
||||
|
}, opts); |
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Prevent inserting assets with the same 'src' |
||||
|
* Seems like idAttribute is not working with dynamic model assignament |
||||
|
* @private |
||||
|
*/ |
||||
|
add: function(models, opt) { |
||||
|
var mods = []; |
||||
|
models = models instanceof Array ? models : [models]; |
||||
|
|
||||
|
for (var i = 0, len = models.length; i < len; i++) { |
||||
|
var model = models[i]; |
||||
|
|
||||
|
if(typeof model === 'string') |
||||
|
model = {src: model, type: 'image'}; |
||||
|
|
||||
|
if(!model || !model.src) |
||||
|
continue; |
||||
|
|
||||
|
var found = this.where({src: model.src}); |
||||
|
|
||||
|
if(!found.length) |
||||
|
mods.push(model); |
||||
|
} |
||||
|
|
||||
|
if(mods.length == 1) |
||||
|
mods = mods[0]; |
||||
|
|
||||
|
return Backbone.Collection.prototype.add.apply(this, [mods, opt]); |
||||
|
}, |
||||
|
|
||||
|
|
||||
}); |
}); |
||||
}); |
}); |
||||
|
|||||
@ -0,0 +1,15 @@ |
|||||
|
<div class="<%= pfx %>assets-cont"> |
||||
|
<div class="<%= pfx %>assets-header"> |
||||
|
<form id="login-form" class="<%= pfx %>add-asset"> |
||||
|
<input class="<%= ppfx %>input" placeholder="http://path/to/the/image.jpg" /> |
||||
|
<button class="<%= ppfx %>btn-prim">Add image</button> |
||||
|
<div style="clear:both"></div> |
||||
|
</form> |
||||
|
<div class="<%= pfx %>dips" style="display:none"> |
||||
|
<button class="fa fa-th <%= ppfx %>btnt"></button> |
||||
|
<button class="fa fa-th-list <%= ppfx %>btnt"></button> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="<%= pfx %>assets"></div> |
||||
|
<div style="clear:both"></div> |
||||
|
</div> |
||||
@ -0,0 +1,7 @@ |
|||||
|
define(function () { |
||||
|
return { |
||||
|
|
||||
|
'blocks': [], |
||||
|
|
||||
|
}; |
||||
|
}); |
||||
@ -0,0 +1,122 @@ |
|||||
|
/** |
||||
|
* * [add](#add) |
||||
|
* * [get](#get) |
||||
|
* * [getAll](#getall) |
||||
|
* * [render](#render) |
||||
|
* |
||||
|
* Block manager helps managing various, draggable, piece of contents that could be easily reused inside templates. |
||||
|
* |
||||
|
* Before using methods you should get first the module from the editor instance, in this way: |
||||
|
* |
||||
|
* ```js
|
||||
|
* var blockManager = editor.BlockManager; |
||||
|
* ``` |
||||
|
* |
||||
|
* @module BlockManager |
||||
|
* @param {Object} config Configurations |
||||
|
* @param {Array<Object>} [config.blocks=[]] Default blocks |
||||
|
* @example |
||||
|
* ... |
||||
|
* { |
||||
|
* blocks: [ |
||||
|
* {id:'h1-block' label: 'Heading', content:'<h1>...</h1>'}, |
||||
|
* ... |
||||
|
* ], |
||||
|
* } |
||||
|
* ... |
||||
|
*/ |
||||
|
define(function(require) { |
||||
|
|
||||
|
return function() { |
||||
|
var c = {}, |
||||
|
defaults = require('./config/config'), |
||||
|
Blocks = require('./model/Blocks'), |
||||
|
BlocksView = require('./view/BlocksView'); |
||||
|
var blocks, view; |
||||
|
|
||||
|
return { |
||||
|
|
||||
|
/** |
||||
|
* Name of the module |
||||
|
* @type {String} |
||||
|
* @private |
||||
|
*/ |
||||
|
name: 'BlockManager', |
||||
|
|
||||
|
/** |
||||
|
* Initialize module. Automatically called with a new instance of the editor |
||||
|
* @param {Object} config Configurations |
||||
|
* @return {this} |
||||
|
* @private |
||||
|
*/ |
||||
|
init: function(config) { |
||||
|
c = config || {}; |
||||
|
for (var name in defaults) { |
||||
|
if (!(name in c)) |
||||
|
c[name] = defaults[name]; |
||||
|
} |
||||
|
blocks = new Blocks(c.blocks); |
||||
|
view = new BlocksView({ collection: blocks }, c); |
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Add new block to the collection. |
||||
|
* @param {string} id Block id |
||||
|
* @param {Object} opts Options |
||||
|
* @param {string} opts.label Name of the block |
||||
|
* @param {string} opts.content HTML content |
||||
|
* @param {Object} [opts.attributes={}] Block attributes |
||||
|
* @return {Block} Added block |
||||
|
* @example |
||||
|
* blockManager.add('h1-block', { |
||||
|
* label: 'Heading', |
||||
|
* content: '<h1>Put your title here</h1>', |
||||
|
* attributes: { |
||||
|
* title: 'Insert h1 block' |
||||
|
* } |
||||
|
* }); |
||||
|
*/ |
||||
|
add: function(id, opts){ |
||||
|
var obj = opts || {}; |
||||
|
obj.id = id; |
||||
|
return blocks.add(obj); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Return block by id |
||||
|
* @param {string} id Block id |
||||
|
* @example |
||||
|
* var block = blockManager.get('h1-block'); |
||||
|
* console.log(JSON.stringify(block)); |
||||
|
* // {label: 'Heading', content: '<h1>Put your ...', ...}
|
||||
|
*/ |
||||
|
get: function(id){ |
||||
|
return blocks.get(id); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Return all blocks |
||||
|
* @return {Collection} |
||||
|
* @example |
||||
|
* var blocks = blockManager.getAll(); |
||||
|
* console.log(JSON.stringify(blocks)); |
||||
|
* // [{label: 'Heading', content: '<h1>Put your ...'}, ...]
|
||||
|
*/ |
||||
|
getAll: function(){ |
||||
|
return blocks; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Render blocks |
||||
|
* @return {HTMLElement} |
||||
|
*/ |
||||
|
render: function(){ |
||||
|
return view.render().el; |
||||
|
}, |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
}); |
||||
@ -0,0 +1,13 @@ |
|||||
|
define(['backbone'], |
||||
|
function(Backbone){ |
||||
|
|
||||
|
return Backbone.Model.extend({ |
||||
|
|
||||
|
defaults :{ |
||||
|
label: '', |
||||
|
content: '', |
||||
|
attributes: {}, |
||||
|
}, |
||||
|
|
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,9 @@ |
|||||
|
define(['backbone','./Block'], |
||||
|
function (Backbone, Block) { |
||||
|
|
||||
|
return Backbone.Collection.extend({ |
||||
|
|
||||
|
model: Block, |
||||
|
|
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,48 @@ |
|||||
|
define(['backbone'], |
||||
|
function(Backbone) { |
||||
|
|
||||
|
return Backbone.View.extend({ |
||||
|
|
||||
|
events: { |
||||
|
mousedown: 'onDrag' |
||||
|
}, |
||||
|
|
||||
|
initialize: function(o, config) { |
||||
|
_.bindAll(this, 'onDrop'); |
||||
|
this.config = config || {}; |
||||
|
this.ppfx = this.config.pStylePrefix || ''; |
||||
|
this.listenTo(this.model, 'destroy', this.remove); |
||||
|
this.doc = $(document); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Start block dragging |
||||
|
* @private |
||||
|
*/ |
||||
|
onDrag: function(){ |
||||
|
if(!this.config.getSorter) |
||||
|
return; |
||||
|
var sorter = this.config.getSorter(); |
||||
|
//this.config.dragHelper(this.el.cloneNode(1));
|
||||
|
sorter.startSort(this.el); |
||||
|
sorter.setDropContent(this.model.get('content')); |
||||
|
this.doc.on('mouseup', this.onDrop); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Drop block |
||||
|
* @private |
||||
|
*/ |
||||
|
onDrop: function(){ |
||||
|
this.doc.off('mouseup', this.onDrop); |
||||
|
this.config.getSorter().endMove(); |
||||
|
}, |
||||
|
|
||||
|
render: function() { |
||||
|
this.el.innerHTML = this.model.get('label'); |
||||
|
this.$el.addClass(this.ppfx + 'block'); |
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,140 @@ |
|||||
|
define(['backbone', './BlockView'], |
||||
|
function(Backbone, BlockView) { |
||||
|
|
||||
|
return Backbone.View.extend({ |
||||
|
|
||||
|
initialize: function(opts, config) { |
||||
|
_.bindAll(this, 'getSorter', 'onDrag', 'onDrop', 'dragHelper', 'moveHelper'); |
||||
|
this.config = config || {}; |
||||
|
this.ppfx = this.config.pStylePrefix || ''; |
||||
|
this.listenTo(this.collection, 'add', this.addTo); |
||||
|
this.em = this.config.em; |
||||
|
this.tac = 'test-tac'; |
||||
|
|
||||
|
if(this.em){ |
||||
|
this.config.getSorter = this.getSorter; |
||||
|
this.config.dragHelper = this.dragHelper; |
||||
|
this.canvas = this.em.get('Canvas'); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Get sorter |
||||
|
* @private |
||||
|
*/ |
||||
|
getSorter: function(){ |
||||
|
if(!this.em) |
||||
|
return; |
||||
|
if(!this.sorter){ |
||||
|
var utils = this.em.get('Utils'); |
||||
|
var canvas = this.canvas; |
||||
|
this.sorter = new utils.Sorter({ |
||||
|
container: canvas.getBody(), |
||||
|
placer: canvas.getPlacerEl(), |
||||
|
containerSel: '*', |
||||
|
itemSel: '*', |
||||
|
pfx: this.ppfx, |
||||
|
onStart: this.onDrag, |
||||
|
onEndMove: this.onDrop, |
||||
|
document: canvas.getFrameEl().contentDocument, |
||||
|
direction: 'a', |
||||
|
wmargin: 1, |
||||
|
nested: 1, |
||||
|
em: this.em |
||||
|
}); |
||||
|
} |
||||
|
return this.sorter; |
||||
|
}, |
||||
|
/* |
||||
|
updateOffset: function(){ |
||||
|
if(!this.sorter) |
||||
|
return; |
||||
|
var sorter = this.sorter; |
||||
|
var offDim = this.canvas.getOffset(); |
||||
|
sorter.offTop = offDim.top; |
||||
|
sorter.offLeft = offDim.left; |
||||
|
}, |
||||
|
*/ |
||||
|
/** |
||||
|
* Callback when block is on drag |
||||
|
* @private |
||||
|
*/ |
||||
|
onDrag: function(){ |
||||
|
this.em.stopDefault(); |
||||
|
this.em.get('Canvas').getBody().style.cursor = 'grabbing'; |
||||
|
document.body.style.cursor = 'grabbing'; |
||||
|
}, |
||||
|
|
||||
|
dragHelper: function(el){ |
||||
|
el.className += ' ' + this.ppfx + 'bdrag'; |
||||
|
this.helper = el; |
||||
|
document.body.appendChild(el); |
||||
|
$(this.em.get('Canvas').getBody()).on('mousemove', this.moveHelper); |
||||
|
$(document).on('mousemove', this.moveHelper); |
||||
|
}, |
||||
|
|
||||
|
moveHelper: function(e){ |
||||
|
this.helper.style.left = e.pageX + 'px'; |
||||
|
this.helper.style.top = e.pageY + 'px'; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Callback when block is dropped |
||||
|
* @private |
||||
|
*/ |
||||
|
onDrop: function(model){ |
||||
|
this.em.runDefault(); |
||||
|
this.em.get('Canvas').getBody().style.cursor = ''; |
||||
|
document.body.style.cursor = ''; |
||||
|
if(model && model.get('activeOnRender')){ |
||||
|
model.trigger('active'); |
||||
|
model.set('activeOnRender', 0); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Add new model to the collection |
||||
|
* @param {Model} model |
||||
|
* @private |
||||
|
* */ |
||||
|
addTo: function(model){ |
||||
|
this.add(model); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Render new model inside the view |
||||
|
* @param {Model} model |
||||
|
* @param {Object} fragment Fragment collection |
||||
|
* @private |
||||
|
* */ |
||||
|
add: function(model, fragment){ |
||||
|
var frag = fragment || null; |
||||
|
var view = new BlockView({ |
||||
|
model: model, |
||||
|
attributes: model.get('attributes'), |
||||
|
}, this.config); |
||||
|
var rendered = view.render().el; |
||||
|
|
||||
|
if(frag) |
||||
|
frag.appendChild(rendered); |
||||
|
else |
||||
|
this.$el.append(rendered); |
||||
|
}, |
||||
|
|
||||
|
|
||||
|
|
||||
|
render: function() { |
||||
|
var frag = document.createDocumentFragment(); |
||||
|
this.$el.empty(); |
||||
|
|
||||
|
this.collection.each(function(model){ |
||||
|
this.add(model, frag); |
||||
|
}, this); |
||||
|
|
||||
|
this.$el.append(frag); |
||||
|
this.$el.addClass(this.ppfx + 'blocks-c'); |
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
|
}); |
||||
|
}); |
||||
@ -1,10 +1,10 @@ |
|||||
define(function () { |
define(function () { |
||||
return { |
return { |
||||
|
|
||||
stylePrefix : 'cv-', |
stylePrefix: 'cv-', |
||||
|
|
||||
// Coming soon
|
// Coming soon
|
||||
rulers : false, |
rulers: false, |
||||
|
|
||||
}; |
}; |
||||
}); |
}); |
||||
@ -1,60 +1,185 @@ |
|||||
define(function(require) { |
define(function(require) { |
||||
/** |
/** |
||||
* @class Canvas |
* @class Canvas} |
||||
* @param {Object} Configurations |
|
||||
* |
|
||||
* @return {Object} |
|
||||
* */ |
* */ |
||||
var Canvas = function(config) |
return function() { |
||||
{ |
var c = {}, |
||||
var c = config || {}, |
defaults = require('./config/config'), |
||||
defaults = require('./config/config'), |
Canvas = require('./model/Canvas'), |
||||
Canvas = require('./model/Canvas'), |
CanvasView = require('./view/CanvasView'); |
||||
CanvasView = require('./view/CanvasView'); |
var canvas; |
||||
|
|
||||
for (var name in defaults) { |
return { |
||||
if (!(name in c)) |
|
||||
c[name] = defaults[name]; |
/** |
||||
} |
* Used inside RTE |
||||
|
* @private |
||||
this.canvas = new Canvas(config); |
*/ |
||||
var obj = { |
getCanvasView: function() { |
||||
model : this.canvas, |
return CanvasView; |
||||
config : c, |
}, |
||||
}; |
|
||||
|
/** |
||||
this.CanvasView = new CanvasView(obj); |
* Name of the module |
||||
}; |
* @type {String} |
||||
|
* @private |
||||
Canvas.prototype = { |
*/ |
||||
|
name: 'Canvas', |
||||
|
|
||||
|
/** |
||||
|
* Initialize module. Automatically called with a new instance of the editor |
||||
|
* @param {Object} config Configurations |
||||
|
*/ |
||||
|
init: function(config) { |
||||
|
c = config || {}; |
||||
|
for (var name in defaults) { |
||||
|
if (!(name in c)) |
||||
|
c[name] = defaults[name]; |
||||
|
} |
||||
|
|
||||
|
var ppfx = c.pStylePrefix; |
||||
|
if(ppfx) |
||||
|
c.stylePrefix = ppfx + c.stylePrefix; |
||||
|
|
||||
|
canvas = new Canvas(config); |
||||
|
CanvasView = new CanvasView({ |
||||
|
model: canvas, |
||||
|
config: c, |
||||
|
}); |
||||
|
|
||||
|
var cm = c.em.get('DomComponents'); |
||||
|
if(cm) |
||||
|
this.setWrapper(cm); |
||||
|
|
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
/** |
/** |
||||
* Add wrapper |
* Add wrapper |
||||
* @param {Object} wrp Wrapper |
* @param {Object} wrp Wrapper |
||||
* |
* |
||||
* */ |
* */ |
||||
setWrapper : function(wrp) |
setWrapper: function(wrp) { |
||||
{ |
canvas.set('wrapper', wrp); |
||||
this.canvas.set('wrapper', wrp); |
|
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Get wrapper |
* Returns canvas element |
||||
* |
* @return {HTMLElement} |
||||
* @return {Object} |
*/ |
||||
* */ |
getElement: function(){ |
||||
getWrapper : function() |
return CanvasView.el; |
||||
{ |
}, |
||||
return this.canvas.get('wrapper').getComponent(); |
|
||||
|
/** |
||||
|
* Returns frame element of the canvas |
||||
|
* @return {HTMLElement} |
||||
|
*/ |
||||
|
getFrameEl: function(){ |
||||
|
return CanvasView.frame.el; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns body element of the frame |
||||
|
* @return {HTMLElement} |
||||
|
*/ |
||||
|
getBody: function(){ |
||||
|
return CanvasView.frame.el.contentDocument.body; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns body wrapper element of the frame |
||||
|
* @return {HTMLElement} |
||||
|
*/ |
||||
|
getWrapperEl: function(){ |
||||
|
return this.getBody().querySelector('#wrapper'); |
||||
}, |
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns element containing canvas tools |
||||
|
* @return {HTMLElement} |
||||
|
*/ |
||||
|
getToolsEl: function(){ |
||||
|
return CanvasView.toolsEl; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns highlighter element |
||||
|
* @return {HTMLElement} |
||||
|
*/ |
||||
|
getHighlighter: function(){ |
||||
|
return CanvasView.hlEl; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns badge element |
||||
|
* @return {HTMLElement} |
||||
|
*/ |
||||
|
getBadgeEl: function(){ |
||||
|
return CanvasView.badgeEl; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns placer element |
||||
|
* @return {HTMLElement} |
||||
|
*/ |
||||
|
getPlacerEl: function(){ |
||||
|
return CanvasView.placerEl; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns ghost element |
||||
|
* @return {HTMLElement} |
||||
|
* @private |
||||
|
*/ |
||||
|
getGhostEl: function(){ |
||||
|
return CanvasView.ghostEl; |
||||
|
}, |
||||
|
|
||||
/** |
/** |
||||
* Render canvas |
* Render canvas |
||||
* */ |
* */ |
||||
render : function() |
render: function() { |
||||
{ |
return CanvasView.render().el; |
||||
return this.CanvasView.render().$el; |
}, |
||||
|
|
||||
|
/** |
||||
|
* Get frame position |
||||
|
* @return {Object} |
||||
|
* @private |
||||
|
*/ |
||||
|
getOffset: function() { |
||||
|
var frameOff = this.offset(this.getFrameEl()); |
||||
|
var canvasOff = this.offset(this.getElement()); |
||||
|
return { |
||||
|
top: frameOff.top - canvasOff.top, |
||||
|
left: frameOff.left - canvasOff.left |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Get the offset of the element |
||||
|
* @param {HTMLElement} el |
||||
|
* @return {Object} |
||||
|
* @private |
||||
|
*/ |
||||
|
offset: function(el){ |
||||
|
var rect = el.getBoundingClientRect(); |
||||
|
return { |
||||
|
top: rect.top + document.body.scrollTop, |
||||
|
left: rect.left + document.body.scrollLeft |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns wrapper element |
||||
|
* @return {HTMLElement} |
||||
|
* ???? |
||||
|
*/ |
||||
|
getFrameWrapperEl: function(){ |
||||
|
return CanvasView.frame.getWrapper(); |
||||
}, |
}, |
||||
|
}; |
||||
}; |
}; |
||||
|
|
||||
return Canvas; |
|
||||
}); |
}); |
||||
@ -1,14 +1,18 @@ |
|||||
define(['backbone'], |
define(['backbone', './Frame'], |
||||
function(Backbone){ |
function(Backbone, Frame){ |
||||
/** |
|
||||
* @class Canvas |
|
||||
* */ |
|
||||
return Backbone.Model.extend({ |
return Backbone.Model.extend({ |
||||
|
|
||||
defaults :{ |
defaults :{ |
||||
wrapper : '', |
frame: '', |
||||
rulers : false, |
wrapper: '', |
||||
|
rulers: false, |
||||
}, |
}, |
||||
|
|
||||
|
initialize: function(config) { |
||||
|
var conf = this.conf || {}; |
||||
|
this.set('frame', new Frame(conf.frame)); |
||||
|
}, |
||||
|
|
||||
}); |
}); |
||||
}); |
}); |
||||
|
|||||
@ -0,0 +1,14 @@ |
|||||
|
define(['backbone'], |
||||
|
function(Backbone){ |
||||
|
|
||||
|
return Backbone.Model.extend({ |
||||
|
|
||||
|
defaults :{ |
||||
|
wrapper: '', |
||||
|
width: '', |
||||
|
height: '', |
||||
|
attributes: {}, |
||||
|
}, |
||||
|
|
||||
|
}); |
||||
|
}); |
||||
@ -1,25 +1,159 @@ |
|||||
define(['backbone'], |
define(['backbone','./FrameView'], |
||||
function(Backbone) { |
function(Backbone, FrameView) { |
||||
/** |
/** |
||||
* @class CanvasView |
* @class CanvasView |
||||
* */ |
* */ |
||||
return Backbone.View.extend({ |
return Backbone.View.extend({ |
||||
|
|
||||
//id: 'canvas',
|
|
||||
|
|
||||
initialize: function(o) { |
initialize: function(o) { |
||||
this.config = o.config; |
_.bindAll(this, 'renderBody', 'onFrameScroll', 'clearOff'); |
||||
|
this.config = o.config || {}; |
||||
|
this.em = this.config.em || {}; |
||||
|
this.ppfx = this.config.pStylePrefix || ''; |
||||
this.className = this.config.stylePrefix + 'canvas'; |
this.className = this.config.stylePrefix + 'canvas'; |
||||
|
this.listenTo(this.em, 'change:canvasOffset', this.clearOff); |
||||
|
this.frame = new FrameView({ |
||||
|
model: this.model.get('frame'), |
||||
|
config: this.config |
||||
|
}); |
||||
}, |
}, |
||||
|
|
||||
|
/** |
||||
|
* Update tools position |
||||
|
* @private |
||||
|
*/ |
||||
|
onFrameScroll: function(){ |
||||
|
var u = 'px'; |
||||
|
var body = this.frame.el.contentDocument.body; |
||||
|
this.toolsEl.style.top = '-' + body.scrollTop + u; |
||||
|
this.toolsEl.style.left = '-' + body.scrollLeft + u; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Render inside frame's body |
||||
|
* @private |
||||
|
*/ |
||||
|
renderBody: function(){ |
||||
|
var wrap = this.model.get('frame').get('wrapper'); |
||||
|
if(wrap){ |
||||
|
var body = this.frame.$el.contents().find('body'); |
||||
|
var cssc = this.config.em.get('CssComposer'); |
||||
|
var conf = this.config.em.get('Config'); |
||||
|
body.append(wrap.render()).append(cssc.render()); |
||||
|
var protCss = conf.protectedCss; |
||||
|
var frameCss = '.' + this.ppfx + 'dashed *{outline: 1px dashed rgba(170,170,170,0.7); outline-offset: -2px}' + |
||||
|
'.' + this.ppfx + 'comp-selected{outline: 3px solid #3b97e3 !important}' + |
||||
|
'.' + this.ppfx + 'no-select{user-select: none; -webkit-user-select:none; -moz-user-select: none}'+ |
||||
|
'.' + this.ppfx + 'freezed{opacity: 0.5; pointer-events: none}' + |
||||
|
'.' + this.ppfx + 'plh-image{background:#f5f5f5; border:none; height:50px; width:50px; display:block; outline:3px solid #ffca6f; cursor:pointer}'; |
||||
|
if(protCss) |
||||
|
body.append('<style>' + frameCss + protCss + '</style>'); |
||||
|
this.config.em.trigger('loaded'); |
||||
|
this.frame.el.contentWindow.onscroll = this.onFrameScroll; |
||||
|
this.frame.udpateOffset(); |
||||
|
|
||||
|
// When the iframe is focused the event dispatcher is not the same so
|
||||
|
// I need to delegate all events to the parent document
|
||||
|
var doc = document; |
||||
|
var fdoc = this.frame.el.contentDocument; |
||||
|
fdoc.addEventListener('keydown', function(e){ |
||||
|
doc.dispatchEvent(new KeyboardEvent(e.type, e)); |
||||
|
}); |
||||
|
fdoc.addEventListener('keyup', function(e){ |
||||
|
doc.dispatchEvent(new KeyboardEvent(e.type, e)); |
||||
|
}); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Get the offset of the element |
||||
|
* @param {HTMLElement} el |
||||
|
* @return {Object} |
||||
|
*/ |
||||
|
offset: function(el){ |
||||
|
var rect = el.getBoundingClientRect(); |
||||
|
var docBody = el.ownerDocument.body; |
||||
|
return { |
||||
|
top: rect.top + docBody.scrollTop, |
||||
|
left: rect.left + docBody.scrollLeft |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Cleare cached offsets |
||||
|
* @private |
||||
|
*/ |
||||
|
clearOff: function(){ |
||||
|
this.frmOff = null; |
||||
|
this.cvsOff = null; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns element's data info |
||||
|
* @param {HTMLElement} el |
||||
|
* @return {Object} |
||||
|
* @private |
||||
|
*/ |
||||
|
getElementPos: function(el){ |
||||
|
if(!this.frmOff) |
||||
|
this.frmOff = this.offset(this.frame.el); |
||||
|
if(!this.cvsOff) |
||||
|
this.cvsOff = this.offset(this.el); |
||||
|
var eo = this.offset(el); |
||||
|
var top = eo.top + this.frmOff.top - this.cvsOff.top; |
||||
|
var left = eo.left + this.frmOff.left - this.cvsOff.left; |
||||
|
return { |
||||
|
top: top, |
||||
|
left: left, |
||||
|
height: el.offsetHeight, |
||||
|
width: el.offsetWidth |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns position data of the canvas element |
||||
|
* @return {Object} obj Position object |
||||
|
*/ |
||||
|
getPosition: function() { |
||||
|
var bEl = this.frame.el.contentDocument.body; |
||||
|
var fo = this.frmOff; |
||||
|
var co = this.cvsOff; |
||||
|
return { |
||||
|
top: fo.top + bEl.scrollTop - co.top, |
||||
|
left: fo.left + bEl.scrollLeft - co.left |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
|
||||
render: function() { |
render: function() { |
||||
this.wrapper = this.model.get('wrapper'); |
this.wrapper = this.model.get('wrapper'); |
||||
|
|
||||
if(this.wrapper && typeof this.wrapper.render == 'function'){ |
if(this.wrapper && typeof this.wrapper.render == 'function'){ |
||||
this.$el.append( this.wrapper.render() ); |
this.model.get('frame').set('wrapper', this.wrapper); |
||||
|
this.$el.append(this.frame.render().el); |
||||
|
var frame = this.frame; |
||||
|
frame.el.onload = this.renderBody; |
||||
} |
} |
||||
this.$el.attr({class: this.className, id: this.config.canvasId}); |
this.toolsEl = $('<div>', { id: this.ppfx + 'tools' }).get(0); |
||||
|
this.hlEl = $('<div>', { class: this.ppfx + 'highlighter' }).get(0); |
||||
|
this.badgeEl = $('<div>', {class: this.ppfx + 'badge'}).get(0); |
||||
|
this.placerEl = $('<div>', {class: this.ppfx + 'placeholder'}).get(0); |
||||
|
this.placerIntEl = $('<div>', {class: this.ppfx + 'placeholder-int'}).get(0); |
||||
|
this.ghostEl = $('<div>', {class: this.ppfx + 'ghost'}).get(0); |
||||
|
this.placerEl.appendChild(this.placerIntEl); |
||||
|
this.toolsEl.appendChild(this.hlEl); |
||||
|
this.toolsEl.appendChild(this.badgeEl); |
||||
|
this.toolsEl.appendChild(this.placerEl); |
||||
|
this.toolsEl.appendChild(this.ghostEl); |
||||
|
this.$el.append(this.toolsEl); |
||||
|
var rte = this.em.get('rte'); |
||||
|
|
||||
|
if(rte) |
||||
|
this.toolsEl.appendChild(rte.render()); |
||||
|
|
||||
|
this.$el.attr({class: this.className}); |
||||
return this; |
return this; |
||||
}, |
}, |
||||
|
|
||||
}); |
}); |
||||
}); |
}); |
||||
|
|||||
@ -0,0 +1,54 @@ |
|||||
|
define(['backbone'], |
||||
|
function(Backbone) { |
||||
|
/** |
||||
|
* @class CanvasView |
||||
|
* */ |
||||
|
return Backbone.View.extend({ |
||||
|
|
||||
|
tagName: 'iframe', |
||||
|
|
||||
|
attributes: { |
||||
|
src: 'about:blank' |
||||
|
}, |
||||
|
|
||||
|
initialize: function(o) { |
||||
|
_.bindAll(this, 'udpateOffset'); |
||||
|
this.config = o.config || {}; |
||||
|
this.ppfx = this.config.pStylePrefix || ''; |
||||
|
this.em = this.config.em; |
||||
|
this.motionsEv = 'transitionend oTransitionEnd transitionend webkitTransitionEnd'; |
||||
|
this.listenTo(this.em, 'change:device', this.updateWidth); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Update width of the frame |
||||
|
* @private |
||||
|
*/ |
||||
|
updateWidth: function(model){ |
||||
|
var device = this.em.getDeviceModel(); |
||||
|
this.el.style.width = device ? device.get('width') : ''; |
||||
|
this.udpateOffset(); |
||||
|
this.$el.on(this.motionsEv, this.udpateOffset); |
||||
|
}, |
||||
|
|
||||
|
udpateOffset: function(){ |
||||
|
var offset = this.em.get('Canvas').getOffset(); |
||||
|
this.em.set('canvasOffset', offset); |
||||
|
this.$el.off(this.motionsEv, this.udpateOffset); |
||||
|
}, |
||||
|
|
||||
|
getBody: function(){ |
||||
|
this.$el.contents().find('body'); |
||||
|
}, |
||||
|
|
||||
|
getWrapper: function(){ |
||||
|
return this.$el.contents().find('body > div'); |
||||
|
}, |
||||
|
|
||||
|
render: function() { |
||||
|
this.$el.attr({class: this.ppfx + 'frame'}); |
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
|
}); |
||||
|
}); |
||||
@ -1,63 +0,0 @@ |
|||||
define(function(require) { |
|
||||
/** |
|
||||
* @class ClassManager |
|
||||
* @param {Object} config Configurations |
|
||||
* |
|
||||
* */ |
|
||||
var ClassManager = function(config) |
|
||||
{ |
|
||||
var c = config || {}, |
|
||||
def = require('./config/config'); |
|
||||
this.ClassTags = require('./model/ClassTags'); |
|
||||
this.ClassTagsView = require('./view/ClassTagsView'); |
|
||||
|
|
||||
for (var name in def) { |
|
||||
if (!(name in c)) |
|
||||
c[name] = def[name]; |
|
||||
} |
|
||||
|
|
||||
this.classes = new this.ClassTags(c.defaults); |
|
||||
this.config = c; |
|
||||
}; |
|
||||
|
|
||||
ClassManager.prototype = { |
|
||||
|
|
||||
/** |
|
||||
* Add new class to collection only if it's not already exists |
|
||||
* @param {String} name Class name |
|
||||
* |
|
||||
* @return {Object} Model class |
|
||||
* */ |
|
||||
addClass: function(name){ |
|
||||
var label = name; |
|
||||
var c = this.getClass(name); |
|
||||
if(!c) |
|
||||
return this.classes.add({name: name, label: label}); |
|
||||
return c; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Get class by its name |
|
||||
* @param {String} id Class name |
|
||||
* |
|
||||
* @return {Object|null} |
|
||||
* */ |
|
||||
getClass : function(id) { |
|
||||
var res = this.classes.where({name: id}); |
|
||||
return res.length ? res[0] : null; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Get collection of classes |
|
||||
* |
|
||||
* @return {Object} |
|
||||
* */ |
|
||||
getClasses : function() { |
|
||||
return this.classes; |
|
||||
}, |
|
||||
|
|
||||
}; |
|
||||
|
|
||||
return ClassManager; |
|
||||
|
|
||||
}); |
|
||||
@ -1,29 +0,0 @@ |
|||||
define(['backbone'], |
|
||||
function (Backbone) { |
|
||||
/** |
|
||||
* @class ClassTag |
|
||||
* */ |
|
||||
return Backbone.Model.extend({ |
|
||||
|
|
||||
defaults: { |
|
||||
label: '', |
|
||||
name: '', |
|
||||
active: true, |
|
||||
}, |
|
||||
|
|
||||
initialize: function(){ |
|
||||
this.set('name', this.escapeName(this.get('name'))); |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Escape string |
|
||||
* @param {String} name |
|
||||
* |
|
||||
* @return {String} |
|
||||
*/ |
|
||||
escapeName: function(name) { |
|
||||
return name.toLowerCase().replace(/([^a-z0-9\w]+)/gi, '-'); |
|
||||
}, |
|
||||
|
|
||||
}); |
|
||||
}); |
|
||||
@ -1,11 +0,0 @@ |
|||||
define(['backbone','./ClassTag'], |
|
||||
function (Backbone, ClassTag) { |
|
||||
/** |
|
||||
* @class ClassTags |
|
||||
* */ |
|
||||
return Backbone.Collection.extend({ |
|
||||
|
|
||||
model: ClassTag, |
|
||||
|
|
||||
}); |
|
||||
}); |
|
||||
@ -1,3 +0,0 @@ |
|||||
<span id="<%= pfx %>checkbox" class="fa"></span> |
|
||||
<span id="<%= pfx %>tag-label"><%= label %></span> |
|
||||
<span id="<%= pfx %>close">⨯</span> |
|
||||
@ -1,254 +1,225 @@ |
|||||
|
/** |
||||
|
* - [addGenerator](#addgenerator) |
||||
|
* - [getGenerator](#getgenerator) |
||||
|
* - [getGenerators](#getgenerators) |
||||
|
* - [addViewer](#addviewer) |
||||
|
* - [getViewer](#getviewer) |
||||
|
* - [getViewers](#getviewers) |
||||
|
* - [updateViewer](#updateviewer) |
||||
|
* - [getCode](#getcode) |
||||
|
* |
||||
|
* |
||||
|
* Before using methods you should get first the module from the editor instance, in this way: |
||||
|
* |
||||
|
* ```js
|
||||
|
* var codeManager = editor.CodeManager; |
||||
|
* ``` |
||||
|
* |
||||
|
* @module CodeManager |
||||
|
*/ |
||||
define(function(require) { |
define(function(require) { |
||||
/** |
|
||||
* @class CodeManager |
|
||||
* @param {Object} Configurations |
|
||||
* |
|
||||
* @return {Object} |
|
||||
* */ |
|
||||
function CodeManager(config) |
|
||||
{ |
|
||||
var c = config || {}, |
|
||||
defaults = require('./config/config'), |
|
||||
gInterface = require('./model/GeneratorInterface'), |
|
||||
gHtml = require('./model/HtmlGenerator'), |
|
||||
gCss = require('./model/CssGenerator'), |
|
||||
gJson = require('./model/JsonGenerator'), |
|
||||
eInterface = require('./model/EditorInterface'), |
|
||||
eCM = require('./model/CodeMirrorEditor'), |
|
||||
editorView = require('./view/EditorView'); |
|
||||
|
|
||||
for (var name in defaults) { |
|
||||
if (!(name in c)) |
|
||||
c[name] = defaults[name]; |
|
||||
} |
|
||||
|
|
||||
this.gi = new gInterface(); |
|
||||
this.generators = {}; |
|
||||
this.defaultGenerators = {}; |
|
||||
this.currentGenerator = null; |
|
||||
|
|
||||
this.ei = new eInterface(); |
|
||||
this.editors = {}; |
|
||||
this.defaultEditors = {}; |
|
||||
this.currentEditor = null; |
|
||||
|
|
||||
var geHtml = new gHtml(), |
|
||||
geCss = new gCss(), |
|
||||
geJson = new gJson(), |
|
||||
edCM = new eCM(); |
|
||||
|
|
||||
this.defaultGenerators[geHtml.getId()] = geHtml; |
|
||||
this.defaultGenerators[geCss.getId()] = geCss; |
|
||||
this.defaultGenerators[geJson.getId()] = geJson; |
|
||||
|
|
||||
this.defaultEditors[edCM.getId()] = edCM; |
|
||||
|
|
||||
this.EditorView = editorView; |
|
||||
this.config = c; |
|
||||
} |
|
||||
|
|
||||
CodeManager.prototype = { |
|
||||
|
|
||||
/** |
var CodeManager = function() { |
||||
* Add new code generator |
|
||||
* @param {GeneratorInterface} generator |
|
||||
* |
|
||||
* @return this |
|
||||
* */ |
|
||||
addGenerator : function(generator) |
|
||||
{ |
|
||||
// Check interface implementation
|
|
||||
for (var method in this.gi) |
|
||||
if(!generator[method]){ |
|
||||
console.warn("addGenerator: method '"+ method +"' was not found"); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
var id = generator.getId(); |
var c = {}, |
||||
this.generators[id] = generator; |
defaults = require('./config/config'), |
||||
|
gHtml = require('./model/HtmlGenerator'), |
||||
|
gCss = require('./model/CssGenerator'), |
||||
|
gJson = require('./model/JsonGenerator'), |
||||
|
eCM = require('./model/CodeMirrorEditor'), |
||||
|
editorView = require('./view/EditorView'); |
||||
|
|
||||
if(!this.currentGenerator) |
var generators = {}, |
||||
this.currentGenerator = id; |
defGenerators = {}, |
||||
|
viewers = {}, |
||||
|
defViewers = {}; |
||||
|
|
||||
return this; |
return { |
||||
|
|
||||
|
getConfig: function() { |
||||
|
return c; |
||||
}, |
}, |
||||
|
|
||||
/** |
config: c, |
||||
* Returns generator |
|
||||
* @param {String}|{Integer} id Generator ID |
|
||||
* |
|
||||
* @return {GeneratorInterface}|null |
|
||||
* */ |
|
||||
getGenerator : function(id) |
|
||||
{ |
|
||||
if(id && this.generators[id]) |
|
||||
generator = this.generators[id]; |
|
||||
|
|
||||
return generator ? generator : null; |
EditorView: editorView, |
||||
}, |
|
||||
|
|
||||
/** |
/** |
||||
* Returns generators |
* Name of the module |
||||
* |
* @type {String} |
||||
* @return {Array} |
* @private |
||||
* */ |
*/ |
||||
getGenerators : function() |
name: 'CodeManager', |
||||
{ |
|
||||
return this.generators; |
/** |
||||
}, |
* Initialize module. Automatically called with a new instance of the editor |
||||
|
* @param {Object} config Configurations |
||||
|
*/ |
||||
|
init: function(config) { |
||||
|
c = config || {}; |
||||
|
for (var name in defaults) { |
||||
|
if (!(name in c)) |
||||
|
c[name] = defaults[name]; |
||||
|
} |
||||
|
|
||||
|
var ppfx = c.pStylePrefix; |
||||
|
if(ppfx) |
||||
|
c.stylePrefix = ppfx + c.stylePrefix; |
||||
|
|
||||
|
defGenerators.html = new gHtml(); |
||||
|
defGenerators.css = new gCss(); |
||||
|
defGenerators.json = new gJson(); |
||||
|
|
||||
|
defViewers.CodeMirror = new eCM(); |
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Callback on load |
||||
|
*/ |
||||
|
onLoad: function(){ |
||||
|
this.loadDefaultGenerators().loadDefaultViewers(); |
||||
|
}, |
||||
|
|
||||
/** |
/** |
||||
* Get current generator |
* Add new code generator to the collection |
||||
* |
* @param {string} id Code generator ID |
||||
* @return {GeneratorInterface} |
* @param {Object} generator Code generator wrapper |
||||
|
* @param {Function} generator.build Function that builds the code |
||||
|
* @return {this} |
||||
|
* @example |
||||
|
* codeManager.addGenerator('html7',{ |
||||
|
* build: function(model){ |
||||
|
* return 'myCode'; |
||||
|
* } |
||||
|
* }); |
||||
* */ |
* */ |
||||
getCurrentGenerator : function() |
addGenerator: function(id, generator) { |
||||
{ |
generators[id] = generator; |
||||
if(!this.currentGenerator) |
return this; |
||||
this.loadDefaultGenerators(); |
|
||||
return this.getGenerator(this.currentGenerator); |
|
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Set current generator |
* Get code generator by id |
||||
* @param {Integer} id Generator ID |
* @param {string} id Code generator ID |
||||
* |
* @return {Object|null} |
||||
* @return this |
* @example |
||||
|
* var generator = codeManager.getGenerator('html7'); |
||||
|
* generator.build = function(model){ |
||||
|
* //extend
|
||||
|
* }; |
||||
* */ |
* */ |
||||
setCurrentGenerator : function(id) |
getGenerator: function(id) { |
||||
{ |
return generators[id] || null; |
||||
this.currentGenerator = id; |
|
||||
return this; |
|
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Load default generators |
* Returns all code generators |
||||
* |
* @return {Array<Object>} |
||||
* @return this |
|
||||
* */ |
* */ |
||||
loadDefaultGenerators : function() |
getGenerators: function() { |
||||
{ |
return generators; |
||||
for (var id in this.defaultGenerators) { |
|
||||
this.addGenerator(this.defaultGenerators[id]); |
|
||||
} |
|
||||
|
|
||||
return this; |
|
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Add new editor |
* Add new code viewer |
||||
* @param {EditorInterface} editor |
* @param {string} id Code viewer ID |
||||
* |
* @param {Object} viewer Code viewer wrapper |
||||
* @return this |
* @param {Function} viewer.init Set element on which viewer will be displayed |
||||
|
* @param {Function} viewer.setContent Set content to the viewer |
||||
|
* @return {this} |
||||
|
* @example |
||||
|
* codeManager.addViewer('ace',{ |
||||
|
* init: function(el){ |
||||
|
* var ace = require('ace-editor'); |
||||
|
* this.editor = ace.edit(el.id); |
||||
|
* }, |
||||
|
* setContent: function(code){ |
||||
|
* this.editor.setValue(code); |
||||
|
* } |
||||
|
* }); |
||||
* */ |
* */ |
||||
addEditor : function(editor) |
addViewer: function(id, viewer) { |
||||
{ |
viewers[id] = viewer; |
||||
// Check interface implementation
|
|
||||
for (var method in this.ei) |
|
||||
if(!editor[method]){ |
|
||||
console.warn("addEditor: method '"+ method +"' was not found"); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
var id = editor.getId(); |
|
||||
this.editors[id] = editor; |
|
||||
|
|
||||
if(!this.currentEditor) |
|
||||
this.currentEditor = id; |
|
||||
|
|
||||
return this; |
return this; |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Returns editor |
* Get code viewer by id |
||||
* @param {String}|{Integer} id Editor ID |
* @param {string} id Code viewer ID |
||||
* |
* @return {Object|null} |
||||
* @return {EditorInterface}|null |
* @example |
||||
|
* var viewer = codeManager.getViewer('ace'); |
||||
* */ |
* */ |
||||
getEditor : function(id) |
getViewer: function(id) { |
||||
{ |
return viewers[id] || null; |
||||
if(id && this.editors[id]) |
|
||||
editor = this.editors[id]; |
|
||||
|
|
||||
return editor ? editor : null; |
|
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Returns editors |
* Returns all code viewers |
||||
* |
* @return {Array<Object>} |
||||
* @return {Array} |
|
||||
* */ |
* */ |
||||
getEditors : function() |
getViewers: function() { |
||||
{ |
return viewers; |
||||
return this.editors; |
|
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Get current editor |
* Update code viewer content |
||||
* |
* @param {Object} viewer Viewer instance |
||||
* @return {EditorInterface} |
* @param {string} code Code string |
||||
|
* @example |
||||
|
* var AceViewer = codeManager.getViewer('ace'); |
||||
|
* // ...
|
||||
|
* var viewer = AceViewer.init(el); |
||||
|
* // ...
|
||||
|
* codeManager.updateViewer(AceViewer, 'code'); |
||||
* */ |
* */ |
||||
getCurrentEditor : function() |
updateViewer: function(viewer, code) { |
||||
{ |
viewer.setContent(code); |
||||
if(!this.currentEditor) |
|
||||
this.loadDefaultEditors(); |
|
||||
return this.getEditor(this.currentEditor); |
|
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Set current editor |
* Get code from model |
||||
* @param {Integer} id Editor ID |
* @param {Object} model Any kind of model that will be passed to the build method of generator |
||||
* |
* @param {string} genId Code generator id |
||||
* @return this |
* @param {Object} [opt] Options |
||||
|
* @return {string} |
||||
|
* @example |
||||
|
* var codeStr = codeManager.getCode(model, 'html'); |
||||
* */ |
* */ |
||||
setCurrentEditor : function(id) |
getCode: function(model, genId, opt) { |
||||
{ |
var generator = this.getGenerator(genId); |
||||
this.currentEditor = id; |
return generator ? generator.build(model, opt) : ''; |
||||
return this; |
|
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Load default editors |
* Load default code generators |
||||
* |
* @return {this} |
||||
* @return this |
* @private |
||||
* */ |
* */ |
||||
loadDefaultEditors : function() |
loadDefaultGenerators: function() { |
||||
{ |
for (var id in defGenerators) |
||||
for (var id in this.defaultEditors) { |
this.addGenerator(id, defGenerators[id]); |
||||
this.addEditor(this.defaultEditors[id]); |
|
||||
} |
|
||||
|
|
||||
return this; |
return this; |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Get code by name |
* Load default code viewers |
||||
* @param {Backbone.Model} model Model |
* @return {this} |
||||
* @param {String}|{Integer} v Id of code generator |
* @private |
||||
* |
|
||||
* @return {String}|null |
|
||||
* */ |
* */ |
||||
getCode : function(model, v, em) |
loadDefaultViewers: function() { |
||||
{ |
for (var id in defViewers) |
||||
var id = v || this.currentGenerator, |
this.addViewer(id, defViewers[id]); |
||||
generator = this.generators[id]; |
|
||||
return generator ? generator.build(model, em) : null; |
|
||||
}, |
|
||||
|
|
||||
/** |
return this; |
||||
* Update editor content |
|
||||
* @param {EditorInteface} editor Editor |
|
||||
* @param {String} code Code value |
|
||||
* |
|
||||
* @return void |
|
||||
* */ |
|
||||
updateEditor : function(editor, code) |
|
||||
{ |
|
||||
editor.setContent(code); |
|
||||
}, |
}, |
||||
|
|
||||
|
}; |
||||
|
|
||||
}; |
}; |
||||
|
|
||||
return CodeManager; |
return CodeManager; |
||||
|
|
||||
}); |
}); |
||||
@ -1,31 +0,0 @@ |
|||||
define(function() { |
|
||||
/** |
|
||||
* @class EditorInterface |
|
||||
* */ |
|
||||
function EditorInterface() {} |
|
||||
|
|
||||
EditorInterface.prototype = { |
|
||||
|
|
||||
/** |
|
||||
* Get id |
|
||||
* |
|
||||
* @return {String}|{Integer} |
|
||||
* */ |
|
||||
getId : function(){}, |
|
||||
|
|
||||
/** |
|
||||
* Set content |
|
||||
* @param {String} str |
|
||||
* |
|
||||
* */ |
|
||||
setContent : function(str){}, |
|
||||
|
|
||||
/** |
|
||||
* Init editor |
|
||||
* @param {Object} el DOM element |
|
||||
* */ |
|
||||
init : function(el){}, |
|
||||
}; |
|
||||
|
|
||||
return EditorInterface; |
|
||||
}); |
|
||||
@ -1,26 +0,0 @@ |
|||||
define(function() { |
|
||||
/** |
|
||||
* @class GeneratorInterface |
|
||||
* */ |
|
||||
function GeneratorInterface() {} |
|
||||
|
|
||||
GeneratorInterface.prototype = { |
|
||||
|
|
||||
/** |
|
||||
* Get id |
|
||||
* |
|
||||
* @return {String}|{Integer} |
|
||||
* */ |
|
||||
getId : function(){}, |
|
||||
|
|
||||
/** |
|
||||
* Generate code from model |
|
||||
* @param {Backbone.Model} model |
|
||||
* |
|
||||
* @return {String} |
|
||||
* */ |
|
||||
build : function(model){}, |
|
||||
}; |
|
||||
|
|
||||
return GeneratorInterface; |
|
||||
}); |
|
||||
@ -0,0 +1,86 @@ |
|||||
|
define(function() { |
||||
|
|
||||
|
return { |
||||
|
/** |
||||
|
* Check if fullscreen mode is enabled |
||||
|
* @return {Boolean} |
||||
|
*/ |
||||
|
isEnabled: function(){ |
||||
|
var d = document; |
||||
|
if(d.fullscreenElement || d.webkitFullscreenElement || d.mozFullScreenElement) |
||||
|
return 1; |
||||
|
else |
||||
|
return 0; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Enable fullscreen mode and return browser prefix |
||||
|
* @param {HTMLElement} el |
||||
|
* @return {string} |
||||
|
*/ |
||||
|
enable: function(el){ |
||||
|
var pfx = ''; |
||||
|
if (el.requestFullscreen) |
||||
|
el.requestFullscreen(); |
||||
|
else if (el.webkitRequestFullscreen) { |
||||
|
pfx = 'webkit'; |
||||
|
el.webkitRequestFullscreen(); |
||||
|
}else if (el.mozRequestFullScreen) { |
||||
|
pfx = 'moz'; |
||||
|
el.mozRequestFullScreen(); |
||||
|
}else if (el.msRequestFullscreen) |
||||
|
el.msRequestFullscreen(); |
||||
|
else |
||||
|
console.warn('Fullscreen not supported'); |
||||
|
return pfx; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Disable fullscreen mode |
||||
|
*/ |
||||
|
disable: function(){ |
||||
|
var d = document; |
||||
|
if (d.exitFullscreen) |
||||
|
d.exitFullscreen(); |
||||
|
else if (d.webkitExitFullscreen) |
||||
|
d.webkitExitFullscreen(); |
||||
|
else if (d.mozCancelFullScreen) |
||||
|
d.mozCancelFullScreen(); |
||||
|
else if (d.msExitFullscreen) |
||||
|
d.msExitFullscreen(); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Triggered when the state of the fullscreen is changed. Inside detects if |
||||
|
* it's enabled |
||||
|
* @param {strinf} pfx Browser prefix |
||||
|
* @param {Event} e |
||||
|
*/ |
||||
|
fsChanged: function(pfx, e){ |
||||
|
var d = document; |
||||
|
var ev = (pfx || '') + 'fullscreenchange'; |
||||
|
if(!this.isEnabled()){ |
||||
|
this.stop(null, this.sender); |
||||
|
document.removeEventListener(ev, this.fsChanged); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
run: function(editor, sender){ |
||||
|
this.sender = sender; |
||||
|
var pfx = this.enable(editor.getContainer()); |
||||
|
this.fsChanged = this.fsChanged.bind(this, pfx); |
||||
|
document.addEventListener(pfx + 'fullscreenchange', this.fsChanged); |
||||
|
if(editor) |
||||
|
editor.trigger('change:canvasOffset'); |
||||
|
}, |
||||
|
|
||||
|
stop: function(editor, sender){ |
||||
|
if(sender && sender.set) |
||||
|
sender.set('active', false); |
||||
|
this.disable(); |
||||
|
if(editor) |
||||
|
editor.trigger('change:canvasOffset'); |
||||
|
} |
||||
|
|
||||
|
}; |
||||
|
}); |
||||
@ -1,185 +1,113 @@ |
|||||
define(['backbone', './SelectComponent','./SelectPosition'], |
define(['backbone', './SelectComponent','./SelectPosition'], |
||||
function(Backbone, SelectComponent, SelectPosition) { |
function(Backbone, SelectComponent, SelectPosition) { |
||||
/** |
|
||||
* @class MoveComponent |
return _.extend({}, SelectPosition, SelectComponent, { |
||||
* */ |
|
||||
return _.extend({},SelectComponent, SelectPosition,{ |
|
||||
|
|
||||
init: function(o){ |
init: function(o){ |
||||
SelectComponent.init.apply(this, arguments); |
SelectComponent.init.apply(this, arguments); |
||||
_.bindAll(this,'startMove','onMove','endMove','rollback','selectingPosition','itemLeft'); |
_.bindAll(this, 'initSorter','rollback', 'onEndMove'); |
||||
this.opt = o; |
this.opt = o; |
||||
this.hoverClass = this.pfx + 'hover-move'; |
this.hoverClass = this.ppfx + 'highlighter-warning'; |
||||
this.badgeClass = this.pfx + 'badge-yellow'; |
this.badgeClass = this.ppfx + 'badge-warning'; |
||||
this.noSelClass = this.pfx + 'no-select'; |
this.noSelClass = this.ppfx + 'no-select'; |
||||
}, |
}, |
||||
|
|
||||
enable: function(){ |
enable: function(){ |
||||
this.canvasTop = this.$canvas.offset().top; |
SelectComponent.enable.apply(this, arguments); |
||||
this.canvasLeft = this.$canvas.offset().left; |
this.getBadgeEl().addClass(this.badgeClass); |
||||
this.$el.css('cursor','move'); |
this.getHighlighterEl().addClass(this.hoverClass); |
||||
this.$el.on('mousedown', this.startMove); |
var wp = this.$wrapper; |
||||
this.startSelectComponent(); |
wp.css('cursor','move'); |
||||
|
wp.on('mousedown', this.initSorter); |
||||
//Avoid strange moving behavior
|
|
||||
this.$el.addClass(this.noSelClass); |
// Avoid strange moving behavior
|
||||
|
wp.addClass(this.noSelClass); |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Hover command |
* Overwrite for doing nothing |
||||
* @param {Object} e |
* @private |
||||
*/ |
*/ |
||||
onHover: function(e) |
toggleClipboard: function(){}, |
||||
{ |
|
||||
e.stopPropagation(); |
|
||||
|
|
||||
var $this = $(e.target); |
/** |
||||
if($this.data('model').get('movable')){ //Show badge if possible
|
* Delegate sorting |
||||
$this.addClass(this.hoverClass); |
* @param {Event} e |
||||
this.attachBadge(e.target); |
* @private |
||||
} |
|
||||
}, |
|
||||
|
|
||||
/** Say what to do after the component was selected |
|
||||
* - Method from selectComponent |
|
||||
* @param Event |
|
||||
* @param Object Selected element |
|
||||
* */ |
|
||||
onSelect: function(e,el){}, |
|
||||
|
|
||||
/** Picking component to move |
|
||||
* @param event |
|
||||
* */ |
* */ |
||||
startMove: function(e, el){ |
initSorter: function(e){ |
||||
this.moved = false; |
var el = $(e.target).data('model'); |
||||
if( !$(e.target).data('model').get('movable') ){ return; } //In case the component selected is not movable
|
if(!el.get('draggable')) |
||||
this.$el.off('mousedown', this.startMove); |
return; |
||||
this.stopSelectComponent(e); |
// Avoid badge showing on move
|
||||
this.$selectedEl = $(e.target); |
this.cacheEl = null; |
||||
this.freezeComponent(this.$selectedEl); |
this.startSelectPosition(e.target, this.frameEl.contentDocument); |
||||
this.helperObj = $('<div>', {class: "tempComp"}).css({ //HELPER gray box
|
this.sorter.onEndMove = this.onEndMove.bind(this); |
||||
top : (e.pageY - this.canvasTop) + this.$canvas.scrollTop(), |
this.stopSelectComponent(); |
||||
left: (e.pageX - this.canvasLeft) + this.$canvas.scrollLeft(), |
this.$wrapper.off('mousedown', this.initSorter); |
||||
width: $(e.target).width(), |
this.getContentWindow().on('keydown', this.rollback); |
||||
height: $(e.target).height(), |
|
||||
position : 'absolute', |
|
||||
'pointer-events': 'none', //disable events for the element
|
|
||||
}).data('helper',1).appendTo(this.$el); |
|
||||
|
|
||||
this.startSelectPosition(); |
|
||||
|
|
||||
this.$el.on('mousemove',this.onMove); |
|
||||
$(document).on('mouseup',this.endMove); |
|
||||
$(document).on('keypress',this.rollback); |
|
||||
}, |
|
||||
|
|
||||
/** During move |
|
||||
* @param event */ |
|
||||
onMove: function(e){ |
|
||||
this.moved = true; |
|
||||
var relativeY = (e.pageY - this.canvasTop) + this.$canvas.scrollTop(); |
|
||||
var relativeX = (e.pageX - this.canvasLeft) + this.$canvas.scrollLeft(); |
|
||||
this.helperObj[0].style.top = (relativeY)+'px'; |
|
||||
this.helperObj[0].style.left = (relativeX)+'px'; |
|
||||
}, |
}, |
||||
|
|
||||
/** Leave component |
/** |
||||
* @param event */ |
* Callback after sorting |
||||
endMove: function(e){ |
* @private |
||||
this.$el.off('mousemove',this.onMove); |
*/ |
||||
$(document).off('mouseup', this.endMove); |
onEndMove: function(){ |
||||
$(document).off('keypress', this.rollback); |
|
||||
this.helperObj.remove(); |
|
||||
|
|
||||
this.removePositionPlaceholder(); |
|
||||
this.stopSelectPosition(); |
|
||||
|
|
||||
//this.highlightComponent(e,el) after end of move
|
|
||||
if(this.moved) |
|
||||
this.move(null, this.$selectedEl, this.posIndex, this.posMethod); |
|
||||
this.unfreezeComponent(this.$selectedEl); |
|
||||
this.enable(); |
this.enable(); |
||||
|
this.getContentWindow().off('keydown', this.rollback); |
||||
}, |
}, |
||||
|
|
||||
/** Move component to new position |
/** |
||||
* @param object Component to move |
* Say what to do after the component was selected (selectComponent) |
||||
* @param object Target component |
* @param {Event} e |
||||
* @param int Indicates the position inside the collection |
* @param {Object} Selected element |
||||
* @param string Before of after component |
* @private |
||||
* |
|
||||
* @return void |
|
||||
* */ |
|
||||
move: function(target, el, posIndex, posMethod){ |
|
||||
var index = posIndex || 0; |
|
||||
var model = el.data("model"); |
|
||||
var collection = model.collection; |
|
||||
|
|
||||
var targetCollection = this.posTargetCollection; |
|
||||
var targetModel = this.posTargetModel; |
|
||||
|
|
||||
if(targetCollection && targetModel.get('droppable')){ |
|
||||
var modelTemp = targetCollection.add({css:{}}, { at: index }); |
|
||||
var modelRemoved = collection.remove(model); |
|
||||
targetCollection.add(modelRemoved, { at: index }); |
|
||||
targetCollection.remove(modelTemp);//{ avoidStore: 1 }
|
|
||||
}else |
|
||||
console.warn("Invalid target position"); |
|
||||
}, |
|
||||
|
|
||||
/** Make component untouchable |
|
||||
* @param object Component |
|
||||
* @return void |
|
||||
* */ |
|
||||
freezeComponent: function($component){ |
|
||||
$component.css({'pointer-events':'none'}); |
|
||||
$component.addClass('freezed'); |
|
||||
}, |
|
||||
|
|
||||
/** Make component touchable |
|
||||
* @param object Component |
|
||||
* @return void |
|
||||
* */ |
* */ |
||||
unfreezeComponent: function($component){ |
onSelect: function(e,el){}, |
||||
$component.css({'pointer-events':'auto'}); |
|
||||
$component.removeClass('freezed'); |
|
||||
}, |
|
||||
|
|
||||
/** Used to bring the previous situation before start moving the component |
/** |
||||
* @param Event |
* Used to bring the previous situation before start moving the component |
||||
* @param Bool Indicates if rollback in anycase |
* @param {Event} e |
||||
* @return void |
* @param {Boolean} Indicates if rollback in anycase |
||||
|
* @private |
||||
* */ |
* */ |
||||
rollback: function(e, force){ |
rollback: function(e, force){ |
||||
var key = e.which || e.keyCode; |
var key = e.which || e.keyCode; |
||||
if(key == this.opt.ESCAPE_KEY || force){ |
if(key == this.opt.ESCAPE_KEY || force){ |
||||
this.moved = false; |
this.sorter.moved = false; |
||||
this.endMove(); |
this.sorter.endMove(); |
||||
} |
} |
||||
return; |
return; |
||||
}, |
}, |
||||
|
|
||||
/** Closing method |
/** |
||||
* */ |
* Returns badge element |
||||
last: function(){ |
* @return {HTMLElement} |
||||
this.placeholder.remove(); |
* @private |
||||
this.placeholderStart.remove(); |
*/ |
||||
this.helperObj.remove(); |
getBadgeEl: function(){ |
||||
this.$el.off('mousemove',this.move); |
if(!this.$badge) |
||||
$(document).off('mouseup', this.endMove); |
this.$badge = $(this.getBadge()); |
||||
$(document).off('keypress', this.rollback); |
return this.$badge; |
||||
}, |
}, |
||||
|
|
||||
/* Run method */ |
/** |
||||
run: function(){ |
* Returns highlighter element |
||||
this.enable(); |
* @return {HTMLElement} |
||||
|
* @private |
||||
|
*/ |
||||
|
getHighlighterEl: function(){ |
||||
|
if(!this.$hl) |
||||
|
this.$hl = $(this.canvas.getHighlighter()); |
||||
|
return this.$hl; |
||||
}, |
}, |
||||
|
|
||||
/* Stop method */ |
|
||||
stop: function(){ |
stop: function(){ |
||||
this.stopSelectComponent(); |
SelectComponent.stop.apply(this, arguments); |
||||
this.$el.css('cursor','');//changes back aspect of the cursor
|
this.getBadgeEl().removeClass(this.badgeClass); |
||||
this.$el.unbind();//removes all attached events
|
this.getHighlighterEl().removeClass(this.hoverClass); |
||||
this.$el.removeClass(this.noSelClass); |
var wp = this.$wrapper; |
||||
|
wp.css('cursor', '').unbind().removeClass(this.noSelClass); |
||||
} |
} |
||||
}); |
}); |
||||
}); |
}); |
||||
@ -0,0 +1,28 @@ |
|||||
|
define(function() { |
||||
|
|
||||
|
return { |
||||
|
|
||||
|
run: function(editor, sender) { |
||||
|
var config = editor.Config; |
||||
|
var pfx = config.stylePrefix; |
||||
|
var bm = editor.BlockManager; |
||||
|
if(!this.blocks){ |
||||
|
this.blocks = $('<div/>').get(0); |
||||
|
this.blocks.appendChild(bm.render()); |
||||
|
var panels = editor.Panels; |
||||
|
if(!panels.getPanel('views-container')) |
||||
|
panelC = panels.addPanel({id: 'views-container'}); |
||||
|
else |
||||
|
panelC = panels.getPanel('views-container'); |
||||
|
panelC.set('appendContent', this.blocks).trigger('change:appendContent'); |
||||
|
} |
||||
|
|
||||
|
this.blocks.style.display = 'block'; |
||||
|
}, |
||||
|
|
||||
|
stop: function() { |
||||
|
if(this.blocks) |
||||
|
this.blocks.style.display = 'none'; |
||||
|
} |
||||
|
}; |
||||
|
}); |
||||
@ -0,0 +1,32 @@ |
|||||
|
define(function() { |
||||
|
|
||||
|
return { |
||||
|
|
||||
|
run: function(editor, sender) { |
||||
|
var config = editor.Config; |
||||
|
var pfx = config.stylePrefix; |
||||
|
var tm = editor.TraitManager; |
||||
|
if(!this.obj){ |
||||
|
var tmView = tm.getTraitsViewer(); |
||||
|
var confTm = tm.getConfig(); |
||||
|
this.obj = $('<div/>') |
||||
|
.append('<div class="'+pfx+'traits-label">' + confTm.labelContainer + '</div>') |
||||
|
.get(0); |
||||
|
this.obj.appendChild(tmView.render().el); |
||||
|
var panels = editor.Panels; |
||||
|
if(!panels.getPanel('views-container')) |
||||
|
panelC = panels.addPanel({id: 'views-container'}); |
||||
|
else |
||||
|
panelC = panels.getPanel('views-container'); |
||||
|
panelC.set('appendContent', this.obj).trigger('change:appendContent'); |
||||
|
} |
||||
|
|
||||
|
this.obj.style.display = 'block'; |
||||
|
}, |
||||
|
|
||||
|
stop: function() { |
||||
|
if(this.obj) |
||||
|
this.obj.style.display = 'none'; |
||||
|
} |
||||
|
}; |
||||
|
}); |
||||
@ -0,0 +1,54 @@ |
|||||
|
define(function() { |
||||
|
|
||||
|
return { |
||||
|
|
||||
|
getPanels: function(editor){ |
||||
|
if(!this.panels) |
||||
|
this.panels = editor.Panels.getPanelsEl(); |
||||
|
return this.panels; |
||||
|
}, |
||||
|
|
||||
|
run: function(editor, sender) { |
||||
|
if(sender) |
||||
|
sender.set('active',false); |
||||
|
editor.stopCommand('sw-visibility'); |
||||
|
var that = this; |
||||
|
var panels = this.getPanels(editor); |
||||
|
var canvas = editor.Canvas.getElement(); |
||||
|
var editorEl = editor.getEl(); |
||||
|
var pfx = editor.Config.stylePrefix; |
||||
|
if(!this.helper) { |
||||
|
this.helper = document.createElement('span'); |
||||
|
this.helper.className = pfx + 'off-prv fa fa-eye-slash'; |
||||
|
editorEl.appendChild(this.helper); |
||||
|
this.helper.onclick = function(){ |
||||
|
that.stop(editor); |
||||
|
}; |
||||
|
} |
||||
|
this.helper.style.display = 'inline-block'; |
||||
|
|
||||
|
panels.style.display = 'none'; |
||||
|
var canvasS = canvas.style; |
||||
|
canvasS.width = '100%'; |
||||
|
canvasS.height = '100%'; |
||||
|
canvasS.top = '0'; |
||||
|
canvasS.left = '0'; |
||||
|
canvasS.padding = '0'; |
||||
|
canvasS.margin = '0'; |
||||
|
editor.trigger('change:canvasOffset'); |
||||
|
}, |
||||
|
|
||||
|
stop: function(editor, sender) { |
||||
|
var panels = this.getPanels(editor); |
||||
|
editor.runCommand('sw-visibility'); |
||||
|
editor.getModel().runDefault(); |
||||
|
panels.style.display = 'block'; |
||||
|
var canvas = editor.Canvas.getElement(); |
||||
|
canvas.setAttribute('style', ''); |
||||
|
if(this.helper) { |
||||
|
this.helper.style.display = 'none'; |
||||
|
} |
||||
|
editor.trigger('change:canvasOffset'); |
||||
|
} |
||||
|
}; |
||||
|
}); |
||||
@ -1,59 +0,0 @@ |
|||||
define(['backbone', 'jqueryUi', './MoveComponent'], |
|
||||
function(Backbone, jqueryUi, MoveComponent) { |
|
||||
/** |
|
||||
* @class ResizeComponent |
|
||||
* */ |
|
||||
return _.extend({}, MoveComponent,{ |
|
||||
|
|
||||
enable: function(){ |
|
||||
var $this = this; |
|
||||
this.startSelectComponent(); |
|
||||
this.$el.find('*').resizable({ |
|
||||
containment: 'parent', |
|
||||
start: function(event,ui){ |
|
||||
ui.element[0].style.height = ui.element.height()+'px'; |
|
||||
ui.element.css({'min-height':'', 'min-width':'' }); |
|
||||
}, |
|
||||
stop: function(event,ui){ |
|
||||
ui.element.css('overflow','auto'); |
|
||||
$this.updateModel(ui); |
|
||||
} |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
|
|
||||
/** |
|
||||
* Update model of resized element |
|
||||
* @param object Component model |
|
||||
* */ |
|
||||
updateModel: function(el){ |
|
||||
var um = 'px', |
|
||||
model = el.element.data("model"), |
|
||||
style = _.clone(model.get('style')); |
|
||||
delete style['min-height']; |
|
||||
delete style['min-width']; |
|
||||
style.height = el.size.height + um; |
|
||||
style.width = el.size.width + um; |
|
||||
style.overflow = 'auto'; |
|
||||
model.set('style', style); |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Run method |
|
||||
* */ |
|
||||
run: function(){ |
|
||||
this.enable(); |
|
||||
this.active = true; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Stop method |
|
||||
* */ |
|
||||
stop: function(){ |
|
||||
this.stopSelectComponent(); |
|
||||
this.$el.find('.ui-resizable').resizable("destroy"); |
|
||||
this.$el.unbind();//removes all attached events
|
|
||||
this.active = false; |
|
||||
} |
|
||||
}); |
|
||||
}); |
|
||||
@ -1,205 +1,302 @@ |
|||||
define(function() { |
define(function() { |
||||
/** |
/** |
||||
* @class SelectComponent |
* @class SelectComponent |
||||
|
* @private |
||||
* */ |
* */ |
||||
return { |
return { |
||||
|
|
||||
init: function(o){ |
init: function(o){ |
||||
_.bindAll(this, 'onHover', 'onOut', 'onClick'); |
_.bindAll(this, 'onHover', 'onOut', 'onClick', 'onKeyPress'); |
||||
}, |
}, |
||||
|
|
||||
enable: function() |
|
||||
{ |
enable: function() { |
||||
_.bindAll(this,'copyComp','pasteComp'); |
_.bindAll(this, 'copyComp', 'pasteComp', 'onFrameScroll'); |
||||
var confMain = this.config.em.get('Config'); |
this.frameOff = this.canvasOff = this.adjScroll = null; |
||||
|
var config = this.config.em.get('Config'); |
||||
this.startSelectComponent(); |
this.startSelectComponent(); |
||||
if(confMain.copyPaste){ |
this.toggleClipboard(config.copyPaste); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Toggle clipboard function |
||||
|
* @param {Boolean} active |
||||
|
* @return {this} |
||||
|
* @private |
||||
|
*/ |
||||
|
toggleClipboard: function(active){ |
||||
|
var en = active || 0; |
||||
|
if(en){ |
||||
key('⌘+c, ctrl+c', this.copyComp); |
key('⌘+c, ctrl+c', this.copyComp); |
||||
key('⌘+v, ctrl+v', this.pasteComp); |
key('⌘+v, ctrl+v', this.pasteComp); |
||||
|
}else{ |
||||
|
key.unbind('⌘+c, ctrl+c'); |
||||
|
key.unbind('⌘+v, ctrl+v'); |
||||
} |
} |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Copy component to clipboard |
* Copy component to the clipboard |
||||
|
* @private |
||||
*/ |
*/ |
||||
copyComp: function() |
copyComp: function() { |
||||
{ |
var el = this.editorModel.get('selectedComponent'); |
||||
var sel = this.editorModel.get('selectedComponent'); |
if(el && el.get('copyable')) |
||||
|
this.editorModel.set('clipboard', el); |
||||
if(sel && sel.get('copyable')) |
|
||||
this.editorModel.set('clipboard', sel); |
|
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Paste component from clipboard |
* Paste component from clipboard |
||||
|
* @private |
||||
*/ |
*/ |
||||
pasteComp: function() |
pasteComp: function() { |
||||
{ |
var clp = this.editorModel.get('clipboard'), |
||||
var clp = this.editorModel.get('clipboard'), |
sel = this.editorModel.get('selectedComponent'); |
||||
sel = this.editorModel.get('selectedComponent'); |
if(clp && sel && sel.collection){ |
||||
if(clp && sel && sel.collection){ |
var index = sel.collection.indexOf(sel), |
||||
var index = sel.collection.indexOf(sel), |
clone = clp.clone(); |
||||
clone = clp.clone(); |
sel.collection.add(clone, { at: index + 1 }); |
||||
sel.collection.add(clone, { at: index + 1 }); |
} |
||||
} |
}, |
||||
|
|
||||
|
/** |
||||
|
* Start select component event |
||||
|
* @private |
||||
|
* */ |
||||
|
startSelectComponent: function() { |
||||
|
this.selEl = $(this.getCanvasBody()).find('*'); |
||||
|
this.selEl.on('mouseover', this.onHover) |
||||
|
.on('mouseout', this.onOut) |
||||
|
.on('click', this.onClick); |
||||
|
var cw = this.getContentWindow(); |
||||
|
cw.on('scroll', this.onFrameScroll); |
||||
|
cw.on('keydown', this.onKeyPress); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Stop select component event |
||||
|
* @private |
||||
|
* */ |
||||
|
stopSelectComponent: function() { |
||||
|
if(this.selEl) |
||||
|
this.selEl.trigger('mouseout').off('mouseover', this.onHover) |
||||
|
.off('mouseout', this.onOut) |
||||
|
.off('click', this.onClick); |
||||
|
this.selEl = null; |
||||
|
var cw = this.getContentWindow(); |
||||
|
cw.off('scroll', this.onFrameScroll); |
||||
|
cw.off('keydown', this.onKeyPress); |
||||
}, |
}, |
||||
|
|
||||
/** Start select component event |
/** |
||||
* @return void |
* On key press event |
||||
|
* @private |
||||
* */ |
* */ |
||||
startSelectComponent: function(){ |
onKeyPress: function(e) { |
||||
var that = this; |
var key = e.which || e.keyCode; |
||||
this.$el.find('*') |
var comp = this.editorModel.get('selectedComponent'); |
||||
.on('mouseover',this.onHover) |
var focused = this.frameEl.contentDocument.activeElement.tagName !== 'BODY'; |
||||
.on('mouseout', this.onOut) |
if(key == 8 || key == 46) { |
||||
.on('click', this.onClick); |
if(!focused) |
||||
this.selEl = this.$el.find('*'); |
e.preventDefault(); |
||||
|
if(comp && !focused){ |
||||
|
if(!comp.get('removable')) |
||||
|
return; |
||||
|
comp.set('status',''); |
||||
|
comp.destroy(); |
||||
|
this.hideBadge(); |
||||
|
this.clean(); |
||||
|
this.editorModel.set('selectedComponent',null); |
||||
|
} |
||||
|
} |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Hover command |
* Hover command |
||||
* @param {Object} e |
* @param {Object} e |
||||
|
* @private |
||||
*/ |
*/ |
||||
onHover: function(e) |
onHover: function(e) { |
||||
{ |
|
||||
e.stopPropagation(); |
e.stopPropagation(); |
||||
$(e.target).addClass(this.hoverClass); |
var trg = e.target; |
||||
this.attachBadge(e.target); |
// Adjust tools scroll top
|
||||
|
if(!this.adjScroll){ |
||||
|
this.adjScroll = 1; |
||||
|
this.onFrameScroll(e); |
||||
|
} |
||||
|
var pos = this.getElementPos(trg); |
||||
|
this.updateBadge(trg, pos); |
||||
|
this.updateHighlighter(trg, pos); |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Out command |
* Out command |
||||
* @param {Object} e |
* @param {Object} e |
||||
|
* @private |
||||
*/ |
*/ |
||||
onOut: function(e) |
onOut: function(e) { |
||||
{ |
|
||||
e.stopPropagation(); |
e.stopPropagation(); |
||||
$(e.target).removeClass(this.hoverClass); |
this.hideBadge(); |
||||
if(this.badge) |
if(this.hl) |
||||
this.badge.css({ left: -10000, top:-10000 }); |
this.hl.css({ left: -10000, top:-10000 }); |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Hover command |
* Hover command |
||||
* @param {Object} e |
* @param {Object} e |
||||
|
* @private |
||||
*/ |
*/ |
||||
onClick: function(e) |
onClick: function(e) { |
||||
{ |
var m = $(e.target).data('model'); |
||||
var s = $(e.target).data('model').get('stylable'); |
if(!m) |
||||
|
return; |
||||
|
var s = m.get('stylable'); |
||||
if(!(s instanceof Array) && !s) |
if(!(s instanceof Array) && !s) |
||||
return; |
return; |
||||
this.onSelect(e, e.target); |
this.onSelect(e, e.target); |
||||
}, |
}, |
||||
|
|
||||
/** Stop select component event |
/** |
||||
* @param Event |
* Update badge for the component |
||||
* @return void |
* @param {Object} Component |
||||
|
* @param {Object} pos Position object |
||||
|
* @private |
||||
* */ |
* */ |
||||
stopSelectComponent: function(e){ |
updateBadge: function(el, pos) { |
||||
if(this.selEl) |
var $el = $(el); |
||||
this.selEl.trigger('mouseout').off('mouseover mouseout click'); |
this.cacheEl = el; |
||||
this.selEl = null; |
var model = $el.data("model"); |
||||
|
if(!model || !model.get('badgable')) |
||||
|
return; |
||||
|
var badge = this.getBadge(); |
||||
|
badge.innerHTML = model.getName(); |
||||
|
var bStyle = badge.style; |
||||
|
var u = 'px'; |
||||
|
bStyle.display = 'block'; |
||||
|
var canvasPos = this.canvas.getCanvasView().getPosition(); |
||||
|
var badgeH = badge ? badge.offsetHeight : 0; |
||||
|
var badgeW = badge ? badge.offsetWidth : 0; |
||||
|
var top = pos.top - badgeH < canvasPos.top ? canvasPos.top : pos.top - badgeH; |
||||
|
var left = pos.left + badgeW < canvasPos.left ? canvasPos.left : pos.left; |
||||
|
bStyle.top = top + u; |
||||
|
bStyle.left = left + u; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Update highlighter element |
||||
|
* @param {HTMLElement} el |
||||
|
* @param {Object} pos Position object |
||||
|
* @private |
||||
|
*/ |
||||
|
updateHighlighter: function(el, pos){ |
||||
|
if(!this.hl) |
||||
|
this.hl = $(this.canvas.getHighlighter()); |
||||
|
this.hl.css({ |
||||
|
left: pos.left, |
||||
|
top: pos.top, |
||||
|
height: pos.height, |
||||
|
width: pos.width |
||||
|
}); |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Say what to do after the component was selected |
* Say what to do after the component was selected |
||||
* @param {Object} e |
* @param {Object} e |
||||
* @param {Object} el |
* @param {Object} el |
||||
|
* @private |
||||
* */ |
* */ |
||||
onSelect: function(e, el) |
onSelect: function(e, el) { |
||||
{ |
|
||||
e.stopPropagation(); |
e.stopPropagation(); |
||||
var md = this.editorModel.get('selectedComponent'); |
var md = this.editorModel.get('selectedComponent'); |
||||
if(md) |
this.cleanPrevious(md); |
||||
md.set('status',''); |
var $el = $(el); |
||||
var nMd = $(el).data('model'); |
var nMd = $el.data('model'); |
||||
if(nMd){ |
if(nMd){ |
||||
this.editorModel.set('selectedComponent', nMd); |
this.editorModel.set('selectedComponent', nMd); |
||||
nMd.set('status','selected'); |
nMd.set('status','selected'); |
||||
} |
} |
||||
}, |
}, |
||||
|
|
||||
/** Removes all highlighting effects on components |
/** |
||||
* @return void |
* Removes all highlighting effects on components |
||||
|
* @private |
||||
* */ |
* */ |
||||
clean: function(){ |
clean: function() { |
||||
this.$el.find('*').removeClass(this.hoverClass); |
if(this.selEl) |
||||
|
this.selEl.removeClass(this.hoverClass); |
||||
}, |
}, |
||||
|
|
||||
/** Attach badge to component |
/** |
||||
* @param Object Component |
* Returns badge element |
||||
* @return void |
* @return {HTMLElement} |
||||
* */ |
* @private |
||||
attachBadge: function(el){ |
*/ |
||||
var model = $(el).data("model"); |
getBadge: function(){ |
||||
if(!model || !model.get('badgable')) |
return this.canvas.getBadgeEl(); |
||||
return; |
|
||||
if(!this.badge) |
|
||||
this.createBadge(); |
|
||||
var badgeH = this.badge.outerHeight(); |
|
||||
this.updateBadgeLabel(model); |
|
||||
var $el = $(el); |
|
||||
if(!this.wrapper) |
|
||||
this.wrapper = this.$wrapper; |
|
||||
if(!this.wrapperTop) |
|
||||
this.wrapperTop = this.wrapper.offset() ? this.wrapper.offset().top : 0; |
|
||||
if(!this.wrapperLeft) |
|
||||
this.wrapperLeft= this.wrapper.offset() ? this.wrapper.offset().left : 0; |
|
||||
var relativeT = ($el.offset().top - this.wrapperTop) + this.wrapper.scrollTop(); |
|
||||
var relativeL = ($el.offset().left- this.wrapperLeft) + this.wrapper.scrollLeft(); |
|
||||
if( (relativeT-badgeH) > this.wrapperTop) //If it's possible set badge to top
|
|
||||
relativeT -= badgeH; |
|
||||
this.badge.css({ left: relativeL, top:relativeT }); |
|
||||
}, |
|
||||
|
|
||||
/** Create badge for the component |
|
||||
* @return void |
|
||||
* */ |
|
||||
createBadge: function (){ |
|
||||
this.badge = $('<div>', {class: this.badgeClass + " no-dots"}).appendTo(this.$wrapper); |
|
||||
}, |
}, |
||||
|
|
||||
/** Remove badge |
/** |
||||
* @return void |
* On frame scroll callback |
||||
* */ |
* @private |
||||
removeBadge: function (){ |
*/ |
||||
if(this.badge){ |
onFrameScroll: function(e){ |
||||
this.badge.remove(); |
var el = this.cacheEl; |
||||
delete this.badge; |
if(el) |
||||
} |
this.updateBadge(el, this.getElementPos(el)); |
||||
}, |
}, |
||||
|
|
||||
/** Updates badge label |
/** |
||||
* @param Object Model |
* Returns element's data info |
||||
* @return void |
* @param {HTMLElement} el |
||||
|
* @return {Object} |
||||
|
* @private |
||||
|
*/ |
||||
|
getElementPos: function(el, badge){ |
||||
|
return this.canvas.getCanvasView().getElementPos(el); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Hide badge |
||||
|
* @private |
||||
* */ |
* */ |
||||
updateBadgeLabel: function (model){ |
hideBadge: function () { |
||||
|
this.getBadge().style.display = 'none'; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Clean previous model from different states |
||||
|
* @param {Component} model |
||||
|
* @private |
||||
|
*/ |
||||
|
cleanPrevious: function(model) { |
||||
if(model) |
if(model) |
||||
this.badge.html( model.getName() ); |
model.set({ |
||||
|
status: '', |
||||
|
state: '', |
||||
|
}); |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Run method |
* Returns content window |
||||
* */ |
* @private |
||||
run: function(em, sender){ |
*/ |
||||
|
getContentWindow: function(){ |
||||
|
if(!this.contWindow) |
||||
|
this.contWindow = $(this.frameEl.contentWindow); |
||||
|
return this.contWindow; |
||||
|
}, |
||||
|
|
||||
|
run: function(em, sender) { |
||||
this.enable(); |
this.enable(); |
||||
this.render(); |
|
||||
}, |
}, |
||||
|
|
||||
/** |
stop: function() { |
||||
* Stop method |
this.stopSelectComponent(); |
||||
* */ |
this.cleanPrevious(this.em.get('selectedComponent')); |
||||
stop: function(){ |
|
||||
var sel = this.editorModel.get('selectedComponent'); |
|
||||
if(sel) |
|
||||
sel.set('status',''); |
|
||||
this.$el.unbind(); //removes all attached events
|
|
||||
this.removeBadge(); |
|
||||
this.clean(); |
this.clean(); |
||||
this.$el.find('*').unbind('mouseover').unbind('mouseout').unbind('click'); |
this.em.set('selectedComponent', null); |
||||
this.editorModel.set('selectedComponent',null); |
this.toggleClipboard(); |
||||
key.unbind('⌘+c, ctrl+c'); |
this.hideBadge(); |
||||
key.unbind('⌘+v, ctrl+v'); |
|
||||
} |
} |
||||
}; |
}; |
||||
}); |
}); |
||||
|
|||||
@ -1,368 +1,102 @@ |
|||||
define(function() { |
define(function() { |
||||
/** |
|
||||
* @class SelectPosition |
|
||||
* */ |
|
||||
return { |
return { |
||||
|
|
||||
init: function(opt) { |
/** |
||||
_.bindAll(this,'selectingPosition','itemLeft'); |
|
||||
this.config = opt; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Returns position placeholder |
|
||||
* |
|
||||
* @return {Object} Placeholder |
|
||||
* */ |
|
||||
getPositionPlaceholder: function() |
|
||||
{ |
|
||||
return this.$plh; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Creates position placeholder |
|
||||
* |
|
||||
* @return {Object} Placeholder |
|
||||
* */ |
|
||||
createPositionPlaceholder: function() |
|
||||
{ |
|
||||
this.$plh = $('<div>', { class: this.plhClass + " no-dots" }) |
|
||||
.css({'pointer-events':'none'}).data('helper',1); |
|
||||
this.$plh.append( $('<div>', { class: this.plhClass + "-int no-dots" } ) ); |
|
||||
this.$plh.appendTo( this.$wrapper ); |
|
||||
return this.$plh; |
|
||||
}, |
|
||||
|
|
||||
enable: function() |
|
||||
{ |
|
||||
this.startSelectPosition(); |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Start select position event |
* Start select position event |
||||
* |
* @param {HTMLElement} trg |
||||
* @return void |
* @private |
||||
* */ |
* */ |
||||
startSelectPosition: function() |
startSelectPosition: function(trg, doc) { |
||||
{ |
|
||||
this.isPointed = false; |
this.isPointed = false; |
||||
this.$wrapper.on('mousemove', this.selectingPosition); |
var utils = this.editorModel.get('Utils'); |
||||
}, |
if(utils && !this.sorter) |
||||
|
this.sorter = new utils.Sorter({ |
||||
/** |
container: this.getCanvasBody(), |
||||
|
placer: this.canvas.getPlacerEl(), |
||||
|
containerSel: '*', |
||||
|
itemSel: '*', |
||||
|
pfx: this.ppfx, |
||||
|
direction: 'a', |
||||
|
document: doc, |
||||
|
wmargin: 1, |
||||
|
nested: 1, |
||||
|
em: this.editorModel, |
||||
|
}); |
||||
|
this.sorter.startSort(trg); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Get frame position |
||||
|
* @return {Object} |
||||
|
* @private |
||||
|
*/ |
||||
|
getOffsetDim: function() { |
||||
|
var frameOff = this.offset(this.canvas.getFrameEl()); |
||||
|
var canvasOff = this.offset(this.canvas.getElement()); |
||||
|
var top = frameOff.top - canvasOff.top; |
||||
|
var left = frameOff.left - canvasOff.left; |
||||
|
return { top: top, left: left }; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
* Stop select position event |
* Stop select position event |
||||
* @return void |
* @private |
||||
* */ |
* */ |
||||
stopSelectPosition: function() |
stopSelectPosition: function() { |
||||
{ |
|
||||
this.$wrapper.off('mousemove',this.selectingPosition); |
|
||||
this.posTargetCollection = null; |
this.posTargetCollection = null; |
||||
this.posIndex = this.posMethod=='after' && this.cDim.length!==0 ? this.posIndex + 1 : this.posIndex; //Normalize
|
this.posIndex = this.posMethod=='after' && this.cDim.length!==0 ? this.posIndex + 1 : this.posIndex; //Normalize
|
||||
|
if(this.sorter){ |
||||
|
this.sorter.moved = 0; |
||||
|
this.sorter.endMove(); |
||||
|
} |
||||
if(this.cDim){ |
if(this.cDim){ |
||||
this.posIsLastEl = this.cDim.length!==0 && this.posMethod=='after' && this.posIndex==this.cDim.length; |
this.posIsLastEl = this.cDim.length!==0 && this.posMethod=='after' && this.posIndex==this.cDim.length; |
||||
this.posTargetEl = (this.cDim.length===0 ? $(this.outsideElem) : |
this.posTargetEl = (this.cDim.length===0 ? $(this.outsideElem) : |
||||
(!this.posIsLastEl && this.cDim[this.posIndex] ? $(this.cDim[this.posIndex][5]).parent() : $(this.outsideElem) )); |
(!this.posIsLastEl && this.cDim[this.posIndex] ? $(this.cDim[this.posIndex][5]).parent() : $(this.outsideElem) )); |
||||
this.posTargetModel = this.posTargetEl.data("model"); |
this.posTargetModel = this.posTargetEl.data("model"); |
||||
this.posTargetCollection = this.posTargetEl.data("model-comp"); |
this.posTargetCollection = this.posTargetEl.data("model-comp"); |
||||
} |
} |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* During event |
* Enabel select position |
||||
* @param {Object} e Event |
* @private |
||||
* */ |
*/ |
||||
selectingPosition: function(e) |
enable: function() { |
||||
{ |
this.startSelectPosition(); |
||||
this.isPointed = true; |
|
||||
|
|
||||
if(!this.wp){ |
|
||||
this.$wp = this.$wrapper; |
|
||||
this.wp = this.$wp[0]; |
|
||||
} |
|
||||
var wpO = this.$wp.offset(); |
|
||||
this.wpT = wpO.top; |
|
||||
this.wpL = wpO.left; |
|
||||
this.wpScT = this.$wp.scrollTop(); |
|
||||
this.wpScL = this.$wp.scrollLeft(); |
|
||||
|
|
||||
if(!this.$plh) |
|
||||
this.createPositionPlaceholder(); |
|
||||
|
|
||||
this.rY = (e.pageY - this.wpT) + this.wpScT; |
|
||||
this.rX = (e.pageX - this.wpL) + this.wpScL; |
|
||||
|
|
||||
this.entered(e); |
|
||||
this.updatePosition(this.rX, this.rY); |
|
||||
var actualPos = this.posIndex + ':' + this.posMethod; //save globally the new index
|
|
||||
|
|
||||
if(!this.lastPos || (this.lastPos != actualPos)){ //If there is a significant changes with mouse
|
|
||||
this.updatePositionPlaceholder(this.posIndex, this.posMethod); |
|
||||
this.lastPos = actualPos; |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Search where to put placeholder |
|
||||
* @param {Integer} posX X position of the mouse |
|
||||
* @param {Integer} posY Y position of the mouse |
|
||||
* |
|
||||
* @retun void |
|
||||
* */ |
|
||||
updatePosition: function( posX, posY ){ |
|
||||
this.posMethod = "before"; |
|
||||
this.posIndex = 0; |
|
||||
var leftLimit = 0, xLimit = 0, dimRight = 0, yLimit = 0, xCenter = 0, yCenter = 0, dimDown = 0, dim = 0; |
|
||||
for(var i = 0; i < this.cDim.length; i++){ |
|
||||
dim = this.cDim[i]; |
|
||||
dimDown = dim[0] + dim[2]; |
|
||||
yCenter = dim[0] + (dim[2] / 2); //Horizontal center
|
|
||||
xCenter = dim[1] + (dim[3] / 2); //Vertical center
|
|
||||
dimRight = dim[1] + dim[3]; |
|
||||
if( (xLimit && dim[1] > xLimit) || (yLimit && yCenter > yLimit) || |
|
||||
(leftLimit && dimRight < leftLimit)) //No need with this one if over the limit
|
|
||||
continue; |
|
||||
if(!dim[4]){ //If it's not inFlow (like float element)
|
|
||||
if( posY < dimDown) |
|
||||
yLimit = dimDown; |
|
||||
if( posX < xCenter){ //If mouse lefter than center
|
|
||||
xLimit = xCenter; |
|
||||
this.posMethod = "before"; |
|
||||
}else{ |
|
||||
leftLimit = xCenter; |
|
||||
this.posMethod = "after"; |
|
||||
} |
|
||||
this.posIndex = i; |
|
||||
}else{ |
|
||||
this.posIndex = i; |
|
||||
if( posY < yCenter ){ //If mouse upper than center
|
|
||||
this.posMethod = "before"; //Should place helper before
|
|
||||
break; //No need to continue under inFlow element
|
|
||||
}else |
|
||||
this.posMethod = "after"; |
|
||||
} |
|
||||
} |
|
||||
if(this.posIndex == (this.cDim.length) && this.posMethod == 'after' ){ |
|
||||
this.posIndex--; |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Updates the position of the placeholder |
|
||||
* @param {Integer} index Index of the nearest child |
|
||||
* @param {String} method Before or after position |
|
||||
* |
|
||||
* @return void |
|
||||
* */ |
|
||||
updatePositionPlaceholder: function(index, method){ |
|
||||
var t = 0, l = 0, w = 0, h = 0, |
|
||||
marg = 2, |
|
||||
un = 'px', |
|
||||
margI = 5, |
|
||||
plh = this.$plh[0]; |
|
||||
if( this.cDim[index] ){ |
|
||||
var elDim = this.cDim[index]; |
|
||||
if(!elDim[4]){ |
|
||||
w = 'auto'; |
|
||||
t = elDim[0] + marg; |
|
||||
h = elDim[2] - (marg * 2) + un; |
|
||||
l = (method == 'before') ? (elDim[1] - marg) : (elDim[1] + elDim[3] - marg); |
|
||||
}else{ |
|
||||
w = elDim[3] + un; |
|
||||
t = (method == 'before') ? (elDim[0] - marg) : (elDim[0] + elDim[2] - marg); |
|
||||
l = elDim[1]; |
|
||||
h = 'auto'; |
|
||||
} |
|
||||
//t -= this.wpScT;
|
|
||||
//l -= this.wpScL;
|
|
||||
}else{ |
|
||||
if(this.$targetEl){ |
|
||||
var trg = this.$targetEl[0], |
|
||||
$eO = this.$targetEl.offset(); |
|
||||
t = $eO.top - this.wpT + this.wpScT + margI; |
|
||||
l = $eO.left - this.wpL + this.wpScL + margI; |
|
||||
w = (parseInt(trg.offsetWidth) - margI * 2) + un; |
|
||||
h = 'auto'; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
plh.style.top = t + un; |
|
||||
plh.style.left = l + un; |
|
||||
if(w) |
|
||||
plh.style.width = w; |
|
||||
if(h) |
|
||||
plh.style.height = h; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Track inside which element pointer entered |
|
||||
* @param {Object} e Event |
|
||||
* |
|
||||
* @return void |
|
||||
* */ |
|
||||
entered: function(e){ |
|
||||
if( (!this.outsideElem || this.outsideElem != e.target) ){ //If I'm in the new element
|
|
||||
this.outsideElem = e.target; //Set the element in which it's actually inside
|
|
||||
this.$targetEl = $(e.target); |
|
||||
$(this.outsideElem).on('mouseleave',this.itemLeft); |
|
||||
this.cDim = this.getChildrenDim(); |
|
||||
this.dimT = this.getTargetDim(e); |
|
||||
if( this.nearToBorders(e) && (e.target.parentNode!=this.wp.parentNode) ) //Avoid flickering
|
|
||||
this.cDim = this.getChildrenDim(e.target.parentNode); |
|
||||
}else if( this.nearToBorders(e) && (e.target.parentNode!=this.wp.parentNode) ){ //Near to borders and parent is not the canvas
|
|
||||
this.cDim = this.getChildrenDim(e.target.parentNode); |
|
||||
}else if( !(this.nearToBorders(e)) ){ |
|
||||
this.cDim = this.getChildrenDim(); |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Check if pointer is near to the borders of the target |
|
||||
* @param {Object} e Event |
|
||||
* |
|
||||
* @return {Integer} |
|
||||
* */ |
|
||||
nearToBorders: function(e){ |
|
||||
var m = 7; //Limit in pixels for be near
|
|
||||
if(!this.dimT) |
|
||||
return; |
|
||||
var dimT = this.dimT; |
|
||||
if(dimT[2] < 40) |
|
||||
m = 5; |
|
||||
if( ((dimT[0] + m) > this.rY) || (this.rY > (dimT[0] + dimT[2] - m)) || |
|
||||
((dimT[1] + m) > this.rX) || (this.rX > (dimT[1] + dimT[3] - m)) ) //Check if the pointer is near
|
|
||||
return 1; |
|
||||
else |
|
||||
return 0; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Check if pointer is near to the float component |
|
||||
* |
|
||||
* @return {Integer} |
|
||||
* */ |
|
||||
nearToFloat: function() |
|
||||
{ |
|
||||
var index = this.posIndex; |
|
||||
var isLastEl = this.posIsLastEl; |
|
||||
if(this.cDim.length !== 0 && ( |
|
||||
(!isLastEl && !this.cDim[index][4]) || |
|
||||
(this.cDim[index-1] && !this.cDim[index-1][4]) || |
|
||||
(isLastEl && !this.cDim[index-1][4]) ) ) |
|
||||
return 1; |
|
||||
else |
|
||||
return 0; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Returns dimension of the taget |
|
||||
* @param {Object} e Event |
|
||||
* |
|
||||
* @return {Array} |
|
||||
* */ |
|
||||
getTargetDim: function(e) |
|
||||
{ |
|
||||
var elT = e.target, |
|
||||
$el = $(elT); |
|
||||
return [ elT.offsetTop, elT.offsetLeft, $el.outerHeight(), $el.outerWidth() ]; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Returns children and their dimensions of the target element, |
|
||||
* excluding text nodes and the move placeholder |
|
||||
* @param {Object} el Element |
|
||||
* |
|
||||
* @return {Array} |
|
||||
* */ |
|
||||
getChildrenDim: function(el) |
|
||||
{ |
|
||||
var dim = []; |
|
||||
var elToPars = el || this.outsideElem; |
|
||||
var isInFlow = this.isInFlow; //Assign method for make it work inside $.each
|
|
||||
var $this = this; //Store context
|
|
||||
$(elToPars.childNodes).each(function(){ |
|
||||
var $el = $(this); |
|
||||
if(this.nodeName != '#text' && !$el.data('helper') ){ //Ignore text nodes and helpers
|
|
||||
dim.push( [ this.offsetTop, this.offsetLeft, $el.outerHeight(), $el.outerWidth(), isInFlow($this, this), this ] ); |
|
||||
} |
|
||||
}); |
|
||||
return dim; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Track when I go ouside of the element (basically when the target changes) |
|
||||
* @param {Object} e Event |
|
||||
* |
|
||||
* @return void |
|
||||
* */ |
|
||||
itemLeft: function(e) |
|
||||
{ |
|
||||
$(this.outsideElem).off('mouseleave',this.itemLeft); |
|
||||
this.outsideElem = null; |
|
||||
this.$targetEl = null; |
|
||||
this.lastPos = null; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Returns true if the elements is in flow, or better is not in flow where |
|
||||
* for example the component is with float:left |
|
||||
* @param {Object} $this Context |
|
||||
* @param {Object} elm Element |
|
||||
* |
|
||||
* @return {Boolean} |
|
||||
* */ |
|
||||
isInFlow: function($this, elm) |
|
||||
{ |
|
||||
var $elm = $(elm), ch = -1; |
|
||||
if(!$elm.length) |
|
||||
return false; |
|
||||
if( ($elm.height() < ch) || !$this.okProps($elm) ) |
|
||||
return false; |
|
||||
return true; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Returns true only if the element follow the standard flow |
|
||||
* @param {Object} $elm Element |
|
||||
* |
|
||||
* @return {Boolean} |
|
||||
* */ |
|
||||
okProps: function($elm) |
|
||||
{ |
|
||||
if ($elm.css('float')!=='none') |
|
||||
return false; |
|
||||
switch($elm.css('position')) { |
|
||||
case 'static': case 'relative': break; |
|
||||
default: return false; |
|
||||
} |
|
||||
switch ($elm.css('display')) { |
|
||||
case 'block': case 'list-item': case 'table': return true; |
|
||||
} |
|
||||
return false; |
|
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Removes position placeholder |
* Check if the pointer is near to the float component |
||||
* |
* @param {number} index |
||||
* @param void |
* @param {string} method |
||||
* */ |
* @param {Array<Array>} dims |
||||
removePositionPlaceholder: function() |
* @return {Boolean} |
||||
{ |
* @private |
||||
if(this.$plh && this.$plh.length) |
* */ |
||||
this.$plh.remove(); |
nearFloat: function(index, method, dims) { |
||||
this.$plh = null; |
var i = index || 0; |
||||
|
var m = method || 'before'; |
||||
|
var len = dims.length; |
||||
|
var isLast = len !== 0 && m == 'after' && i == len; |
||||
|
if(len !== 0 && ( |
||||
|
(!isLast && !dims[i][4]) || |
||||
|
(dims[i-1] && !dims[i-1][4]) || |
||||
|
(isLast && !dims[i-1][4]) ) ) |
||||
|
return 1; |
||||
|
return 0; |
||||
}, |
}, |
||||
|
|
||||
/* Run method */ |
|
||||
run: function(){ |
run: function() { |
||||
this.enable(); |
this.enable(); |
||||
}, |
}, |
||||
|
|
||||
/* Stop method */ |
stop: function() { |
||||
stop: function(){ |
|
||||
this.removePositionPlaceholder(); |
|
||||
this.stopSelectPosition(); |
this.stopSelectPosition(); |
||||
this.$wrapper.css('cursor','');//changes back aspect of the cursor
|
this.$wrapper.css('cursor',''); |
||||
this.$wrapper.unbind();//removes all attached events
|
this.$wrapper.unbind(); |
||||
} |
} |
||||
}; |
}; |
||||
}); |
}); |
||||
@ -1,17 +1,13 @@ |
|||||
define(function() { |
define(function() { |
||||
/** |
|
||||
* @class SwitchVisibility |
|
||||
* */ |
|
||||
return { |
return { |
||||
|
|
||||
run: function() |
run: function(ed) { |
||||
{ |
ed.Canvas.getBody().className = this.ppfx + 'dashed'; |
||||
this.$canvas.addClass(this.pfx + 'dashed'); |
|
||||
}, |
}, |
||||
|
|
||||
stop: function() |
stop: function(ed) { |
||||
{ |
ed.Canvas.getBody().className = ""; |
||||
this.$canvas.removeClass(this.pfx + 'dashed'); |
|
||||
} |
} |
||||
|
|
||||
}; |
}; |
||||
}); |
}); |
||||
@ -1,106 +1,258 @@ |
|||||
|
/** |
||||
|
* * [add](#add) |
||||
|
* * [get](#get) |
||||
|
* * [getAll](#getall) |
||||
|
* * [load](#load) |
||||
|
* * [store](#store) |
||||
|
* |
||||
|
* This module contains and manage CSS rules for the template inside the canvas |
||||
|
* Before using the methods you should get first the module from the editor instance, in this way: |
||||
|
* |
||||
|
* ```js
|
||||
|
* var cssComposer = editor.CssComposer; |
||||
|
* ``` |
||||
|
* |
||||
|
* @module CssComposer |
||||
|
* @param {Object} config Configurations |
||||
|
* @param {string|Array<Object>} [config.rules=[]] CSS string or an array of rule objects |
||||
|
* @example |
||||
|
* ... |
||||
|
* CssComposer: { |
||||
|
* rules: '.myClass{ color: red}', |
||||
|
* } |
||||
|
*/ |
||||
define(function(require) { |
define(function(require) { |
||||
/** |
return function() { |
||||
* @class CssComposer |
var c = {}, |
||||
* @param {Object} config Configurations |
defaults = require('./config/config'), |
||||
* |
CssRule = require('./model/CssRule'), |
||||
* */ |
CssRules = require('./model/CssRules'), |
||||
var CssComposer = function(config) |
Selectors = require('./model/Selectors'), |
||||
{ |
CssRulesView = require('./view/CssRulesView'); |
||||
var c = config || {}, |
|
||||
def = require('./config/config'), |
var rules, rulesView; |
||||
CssRule = require('./model/CssRule'), |
|
||||
CssRules = require('./model/CssRules'), |
|
||||
Selectors = require('./model/Selectors'), |
|
||||
CssRulesView = require('./view/CssRulesView'); |
|
||||
|
|
||||
for (var name in def) { |
|
||||
if (!(name in c)) |
|
||||
c[name] = def[name]; |
|
||||
} |
|
||||
|
|
||||
var rules = new CssRules(c.defaults, c), |
|
||||
rulesView = new CssRulesView({ |
|
||||
collection: rules, |
|
||||
config: c, |
|
||||
}); |
|
||||
|
|
||||
return { |
return { |
||||
|
|
||||
Selectors: Selectors, |
Selectors: Selectors, |
||||
|
|
||||
/** |
/** |
||||
* Create new rule and return it. Don't add it to the collection |
* Name of the module |
||||
* @param {Array} selectors Array of selectors |
* @type {String} |
||||
* @param {String} state Css rule state |
* @private |
||||
* @param {String} width For which device this style is oriented |
*/ |
||||
* |
name: 'CssComposer', |
||||
* @return {Object} |
|
||||
|
/** |
||||
|
* Mandatory for the storage manager |
||||
|
* @type {String} |
||||
|
* @private |
||||
|
*/ |
||||
|
storageKey: function(){ |
||||
|
var keys = []; |
||||
|
var smc = (c.stm && c.stm.getConfig()) || {}; |
||||
|
if(smc.storeCss) |
||||
|
keys.push('css'); |
||||
|
if(smc.storeStyles) |
||||
|
keys.push('styles'); |
||||
|
return keys; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Initializes module. Automatically called with a new instance of the editor |
||||
|
* @param {Object} config Configurations |
||||
|
* @private |
||||
|
*/ |
||||
|
init: function(config) { |
||||
|
c = config || {}; |
||||
|
for (var name in defaults) { |
||||
|
if (!(name in c)) |
||||
|
c[name] = defaults[name]; |
||||
|
} |
||||
|
|
||||
|
var ppfx = c.pStylePrefix; |
||||
|
if(ppfx) |
||||
|
c.stylePrefix = ppfx + c.stylePrefix; |
||||
|
|
||||
|
var elStyle = (c.em && c.em.config.style) || ''; |
||||
|
c.rules = elStyle || c.rules; |
||||
|
|
||||
|
c.sm = c.em; // TODO Refactor
|
||||
|
rules = new CssRules([], c); |
||||
|
rules.add(c.rules); |
||||
|
|
||||
|
rulesView = new CssRulesView({ |
||||
|
collection: rules, |
||||
|
config: c, |
||||
|
}); |
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* On load callback |
||||
|
* @private |
||||
|
*/ |
||||
|
onLoad: function(){ |
||||
|
if(c.stm && c.stm.getConfig().autoload) |
||||
|
this.load(); |
||||
|
|
||||
|
if(c.stm && c.stm.isAutosave()) |
||||
|
c.em.listenRules(this.getAll()); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Load data from the passed object, if the object is empty will try to fetch them |
||||
|
* autonomously from the storage manager. |
||||
|
* The fetched data will be added to the collection |
||||
|
* @param {Object} data Object of data to load |
||||
|
* @return {Object} Loaded rules |
||||
|
*/ |
||||
|
load: function(data){ |
||||
|
var d = data || ''; |
||||
|
if(!d && c.stm) |
||||
|
d = c.em.getCacheLoad(); |
||||
|
var obj = ''; |
||||
|
if(d.style){ |
||||
|
try{ |
||||
|
obj = JSON.parse(d.style); |
||||
|
}catch(err){} |
||||
|
}else if(d.css) |
||||
|
obj = c.em.get('Parser').parseCss(d.css); |
||||
|
|
||||
|
if(obj) |
||||
|
rules.reset(obj); |
||||
|
return obj; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Store data to the selected storage |
||||
|
* @param {Boolean} noStore If true, won't store |
||||
|
* @return {Object} Data to store |
||||
|
*/ |
||||
|
store: function(noStore){ |
||||
|
if(!c.stm) |
||||
|
return; |
||||
|
var obj = {}; |
||||
|
var keys = this.storageKey(); |
||||
|
if(keys.indexOf('css') >= 0) |
||||
|
obj.css = c.em.getCss(); |
||||
|
if(keys.indexOf('styles') >= 0) |
||||
|
obj.styles = JSON.stringify(rules); |
||||
|
if(!noStore) |
||||
|
c.stm.store(obj); |
||||
|
return obj; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Add new rule to the collection, if not yet exists with the same selectors |
||||
|
* @param {Array<Selector>} selectors Array of selectors |
||||
|
* @param {String} state Css rule state |
||||
|
* @param {String} width For which device this style is oriented |
||||
|
* @return {Model} |
||||
|
* @example |
||||
|
* var sm = editor.SelectorManager; |
||||
|
* var sel1 = sm.add('myClass1'); |
||||
|
* var sel2 = sm.add('myClass2'); |
||||
|
* var rule = cssComposer.add([sel1, sel2], 'hover'); |
||||
|
* rule.set('style', { |
||||
|
* width: '100px', |
||||
|
* color: '#fff', |
||||
|
* }); |
||||
* */ |
* */ |
||||
newRule: function(selectors, state, width) { |
add: function(selectors, state, width) { |
||||
var s = state || ''; |
var s = state || ''; |
||||
var w = width || ''; |
var w = width || ''; |
||||
var rule = new CssRule({ |
var rule = this.get(selectors, s, w); |
||||
state: s, |
if(rule) |
||||
maxWidth: w, |
return rule; |
||||
}); |
else{ |
||||
rule.get('selectors').add(selectors); |
rule = new CssRule({ |
||||
return rule; |
state: s, |
||||
|
maxWidth: w, |
||||
|
}); |
||||
|
rule.get('selectors').add(selectors); |
||||
|
rules.add(rule); |
||||
|
return rule; |
||||
|
} |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Add new rule to the collection if not yet exists |
* Get the rule |
||||
* @param {Object} rule |
* @param {Array<Selector>} selectors Array of selectors |
||||
* |
* @param {String} state Css rule state |
||||
* @return {Object} |
* @param {String} width For which device this style is oriented |
||||
|
* @return {Model|null} |
||||
|
* @example |
||||
|
* var sm = editor.SelectorManager; |
||||
|
* var sel1 = sm.add('myClass1'); |
||||
|
* var sel2 = sm.add('myClass2'); |
||||
|
* var rule = cssComposer.get([sel1, sel2], 'hover'); |
||||
|
* // Update the style
|
||||
|
* rule.set('style', { |
||||
|
* width: '300px', |
||||
|
* color: '#000', |
||||
|
* }); |
||||
* */ |
* */ |
||||
addRule: function(rule){ |
get: function(selectors, state, width) { |
||||
var models = rule.get('selectors').models; |
var rule = null; |
||||
var r = this.getRule(models, rule.get('state'), rule.get('maxWidth')); |
rules.each(function(m){ |
||||
if(!r) |
if(rule) |
||||
r = rules.add(rule); |
return; |
||||
return r; |
if(m.compare(selectors, state, width)) |
||||
|
rule = m; |
||||
|
}); |
||||
|
return rule; |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Get class by its name |
* Get the collection of rules |
||||
* @param {Array} selectors Array of selectors |
* @return {Collection} |
||||
* @param {String} state Css rule state |
|
||||
* @param {String} width For which device this style is oriented |
|
||||
* |
|
||||
* @return {Object|null} |
|
||||
* */ |
* */ |
||||
getRule : function(selectors, state, width) { |
getAll: function() { |
||||
fRule = null; |
return rules; |
||||
rules.each(function(rule){ |
|
||||
if(fRule) |
|
||||
return; |
|
||||
if(rule.compare(selectors, state, width)) |
|
||||
fRule = rule; |
|
||||
}, this); |
|
||||
return fRule; |
|
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Get collection of css rules |
* Add a raw collection of rule objects |
||||
* |
* This method overrides styles, in case, of already defined rule |
||||
* @return {Object} |
* @param {Array<Object>} data Array of rule objects |
||||
* */ |
* @return {Array<Model>} |
||||
getRules : function() { |
* @private |
||||
return rules; |
*/ |
||||
|
addCollection: function(data){ |
||||
|
var result = []; |
||||
|
var d = data instanceof Array ? data : [data]; |
||||
|
for(var i = 0, l = d.length; i < l; i++){ |
||||
|
var rule = d[i] || {}; |
||||
|
if(!rule.selectors) |
||||
|
continue; |
||||
|
var sm = c.em && c.em.get('SelectorManager'); |
||||
|
if(!sm) |
||||
|
console.warn('Selector Manager not found'); |
||||
|
var sl = rule.selectors; |
||||
|
var sels = sl instanceof Array ? sl : [sl]; |
||||
|
var newSels = []; |
||||
|
for(var j = 0, le = sels.length; j < le; j++){ |
||||
|
var selec = sm.add(sels[j]); |
||||
|
newSels.push(selec); |
||||
|
} |
||||
|
var model = this.add(newSels, rule.state, rule.width); |
||||
|
model.set('style', rule.style || {}); |
||||
|
result.push(model); |
||||
|
} |
||||
|
return result; |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Render block of CSS rules |
* Render the block of CSS rules |
||||
* |
* @return {HTMLElement} |
||||
* @return {Object} |
* @private |
||||
*/ |
*/ |
||||
render: function(){ |
render: function() { |
||||
return rulesView.render().el; |
return rulesView.render().el; |
||||
} |
} |
||||
|
|
||||
}; |
}; |
||||
}; |
}; |
||||
|
|
||||
return CssComposer; |
}); |
||||
|
|
||||
}); |
|
||||
|
|||||
File diff suppressed because it is too large
@ -0,0 +1,9 @@ |
|||||
|
define(function () { |
||||
|
return { |
||||
|
|
||||
|
devices: [], |
||||
|
|
||||
|
deviceLabel: 'Device', |
||||
|
|
||||
|
}; |
||||
|
}); |
||||
@ -0,0 +1,115 @@ |
|||||
|
/** |
||||
|
* * [add](#add) |
||||
|
* * [get](#get) |
||||
|
* * [getAll](#getall) |
||||
|
* |
||||
|
* Before using methods you should get first the module from the editor instance, in this way: |
||||
|
* |
||||
|
* ```js
|
||||
|
* var deviceManager = editor.DeviceManager; |
||||
|
* ``` |
||||
|
* |
||||
|
* @module DeviceManager |
||||
|
*/ |
||||
|
define(function(require) { |
||||
|
|
||||
|
return function() { |
||||
|
var c = {}, |
||||
|
defaults = require('./config/config'), |
||||
|
Devices = require('./model/Devices'), |
||||
|
DevicesView = require('./view/DevicesView'); |
||||
|
var devices, view; |
||||
|
|
||||
|
return { |
||||
|
|
||||
|
/** |
||||
|
* Name of the module |
||||
|
* @type {String} |
||||
|
* @private |
||||
|
*/ |
||||
|
name: 'DeviceManager', |
||||
|
|
||||
|
/** |
||||
|
* Initialize module. Automatically called with a new instance of the editor |
||||
|
* @param {Object} config Configurations |
||||
|
* @param {Array<Object>} [config.devices=[]] Default devices |
||||
|
* @example |
||||
|
* ... |
||||
|
* { |
||||
|
* devices: [ |
||||
|
* {name: 'Desktop', width: ''} |
||||
|
* {name: 'Tablet', width: '991px'} |
||||
|
* ], |
||||
|
* } |
||||
|
* ... |
||||
|
* @return {this} |
||||
|
*/ |
||||
|
init: function(config) { |
||||
|
c = config || {}; |
||||
|
for (var name in defaults) { |
||||
|
if (!(name in c)) |
||||
|
c[name] = defaults[name]; |
||||
|
} |
||||
|
|
||||
|
devices = new Devices(c.devices); |
||||
|
view = new DevicesView({ |
||||
|
collection: devices, |
||||
|
config: c |
||||
|
}); |
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Add new device to the collection. URLs are supposed to be unique |
||||
|
* @param {string} name Device name |
||||
|
* @param {string} width Width of the device |
||||
|
* @param {Object} opts Custom options |
||||
|
* @return {Device} Added device |
||||
|
* @example |
||||
|
* deviceManager.add('Tablet', '900px'); |
||||
|
*/ |
||||
|
add: function(name, width, opts){ |
||||
|
var obj = opts || {}; |
||||
|
obj.name = name; |
||||
|
obj.width = width; |
||||
|
return devices.add(obj); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Return device by name |
||||
|
* @param {string} name Name of the device |
||||
|
* @example |
||||
|
* var device = deviceManager.get('Tablet'); |
||||
|
* console.log(JSON.stringify(device)); |
||||
|
* // {name: 'Tablet', width: '900px'}
|
||||
|
*/ |
||||
|
get: function(name){ |
||||
|
return devices.get(name); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Return all devices |
||||
|
* @return {Collection} |
||||
|
* @example |
||||
|
* var devices = deviceManager.getAll(); |
||||
|
* console.log(JSON.stringify(devices)); |
||||
|
* // [{name: 'Desktop', width: ''}, ...]
|
||||
|
*/ |
||||
|
getAll: function(){ |
||||
|
return devices; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Render devices |
||||
|
* @return {string} HTML string |
||||
|
* @private |
||||
|
*/ |
||||
|
render: function(){ |
||||
|
return view.render().el; |
||||
|
}, |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
}); |
||||
@ -0,0 +1,14 @@ |
|||||
|
define(['backbone'], |
||||
|
function(Backbone){ |
||||
|
|
||||
|
return Backbone.Model.extend({ |
||||
|
|
||||
|
idAttribute: 'name', |
||||
|
|
||||
|
defaults :{ |
||||
|
name: '', |
||||
|
width: '', |
||||
|
}, |
||||
|
|
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,9 @@ |
|||||
|
define(['backbone','./Device'], |
||||
|
function (Backbone, Device) { |
||||
|
|
||||
|
return Backbone.Collection.extend({ |
||||
|
|
||||
|
model: Device, |
||||
|
|
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,10 @@ |
|||||
|
<div class="<%= ppfx %>device-label"><%= deviceLabel %></div> |
||||
|
<div class="<%= ppfx %>field <%= ppfx %>select"> |
||||
|
<span id="<%= ppfx %>input-holder"> |
||||
|
<select class="<%= ppfx %>devices"></select> |
||||
|
</span> |
||||
|
<div class="<%= ppfx %>sel-arrow"> |
||||
|
<div class="<%= ppfx %>d-s-arrow"></div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<button style="display:none" class="<%= ppfx %>add-trasp">+</button> |
||||
@ -0,0 +1,82 @@ |
|||||
|
define(['backbone', 'text!./../template/devices.html'], |
||||
|
function(Backbone, devicesTemplate) { |
||||
|
|
||||
|
return Backbone.View.extend({ |
||||
|
|
||||
|
template: _.template(devicesTemplate), |
||||
|
|
||||
|
events: { |
||||
|
'change': 'updateDevice' |
||||
|
}, |
||||
|
|
||||
|
initialize: function(o) { |
||||
|
this.config = o.config || {}; |
||||
|
this.em = this.config.em; |
||||
|
this.ppfx = this.config.pStylePrefix || ''; |
||||
|
this.events['click .' + this.ppfx + 'add-trasp'] = this.startAdd; |
||||
|
this.listenTo(this.em, 'change:device', this.updateSelect); |
||||
|
this.delegateEvents(); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Start adding new device |
||||
|
* @return {[type]} [description] |
||||
|
* @private |
||||
|
*/ |
||||
|
startAdd: function(){}, |
||||
|
|
||||
|
/** |
||||
|
* Update device of the editor |
||||
|
* @private |
||||
|
*/ |
||||
|
updateDevice: function(){ |
||||
|
var em = this.em; |
||||
|
if(em){ |
||||
|
var devEl = this.devicesEl; |
||||
|
var val = devEl ? devEl.val() : ''; |
||||
|
em.set('device', val); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Update select value on device update |
||||
|
* @private |
||||
|
*/ |
||||
|
updateSelect: function(){ |
||||
|
var em = this.em; |
||||
|
var devEl = this.devicesEl; |
||||
|
if(em && em.getDeviceModel && devEl){ |
||||
|
var device = em.getDeviceModel(); |
||||
|
var name = device ? device.get('name') : ''; |
||||
|
devEl.val(name); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Return devices options |
||||
|
* @return {string} String of options |
||||
|
* @private |
||||
|
*/ |
||||
|
getOptions: function(){ |
||||
|
var result = ''; |
||||
|
this.collection.each(function(device){ |
||||
|
var name = device.get('name'); |
||||
|
result += '<option value="' + name+ '">' + name + '</option>'; |
||||
|
}); |
||||
|
return result; |
||||
|
}, |
||||
|
|
||||
|
render: function() { |
||||
|
var pfx = this.ppfx; |
||||
|
this.$el.html(this.template({ |
||||
|
ppfx: pfx, |
||||
|
deviceLabel: this.config.deviceLabel |
||||
|
})); |
||||
|
this.devicesEl = this.$el.find('.' + pfx + 'devices'); |
||||
|
this.devicesEl.append(this.getOptions()); |
||||
|
this.el.className = pfx + 'devices-c'; |
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
|
}); |
||||
|
}); |
||||
@ -1,89 +1,288 @@ |
|||||
|
/** |
||||
|
* |
||||
|
* * [getWrapper](#getwrapper) |
||||
|
* * [getComponents](#getcomponents) |
||||
|
* * [addComponent](#addcomponent) |
||||
|
* * [clear](#clear) |
||||
|
* * [load](#load) |
||||
|
* * [store](#store) |
||||
|
* * [render](#render) |
||||
|
* |
||||
|
* With this module is possible to manage components inside the canvas. |
||||
|
* Before using methods you should get first the module from the editor instance, in this way: |
||||
|
* |
||||
|
* ```js
|
||||
|
* var domComponents = editor.DomComponents; |
||||
|
* ``` |
||||
|
* |
||||
|
* @module DomComponents |
||||
|
* @param {Object} config Configurations |
||||
|
* @param {string|Array<Object>} [config.components=[]] HTML string or an array of possible components |
||||
|
* @example |
||||
|
* ... |
||||
|
* domComponents: { |
||||
|
* components: '<div>Hello world!</div>', |
||||
|
* } |
||||
|
* // Or
|
||||
|
* domComponents: { |
||||
|
* components: [ |
||||
|
* { tagName: 'span', style: {color: 'red'}, content: 'Hello'}, |
||||
|
* { style: {width: '100px', content: 'world!'}} |
||||
|
* ], |
||||
|
* } |
||||
|
* ... |
||||
|
*/ |
||||
define(function(require) { |
define(function(require) { |
||||
/** |
|
||||
* @class Components |
return function (){ |
||||
* @param {Object} Configurations |
var c = {}, |
||||
* |
defaults = require('./config/config'), |
||||
* @return {Object} |
Component = require('./model/Component'), |
||||
* */ |
ComponentText = require('./model/ComponentText'), |
||||
function Components(config) |
ComponentImage = require('./model/ComponentImage'), |
||||
{ |
ComponentLink = require('./model/ComponentLink'), |
||||
var c = config || {}, |
ComponentView = require('./view/ComponentView'), |
||||
defaults = require('./config/config'), |
ComponentImageView = require('./view/ComponentImageView'), |
||||
Component = require('./model/Component'), |
|
||||
ComponentText = require('./model/ComponentText'), |
|
||||
ComponentImage = require('./model/ComponentImage'), |
|
||||
ComponentView = require('./view/ComponentView'), |
|
||||
ComponentImageView = require('./view/ComponentImageView'), |
|
||||
ComponentTextView = require('./view/ComponentTextView'); |
ComponentTextView = require('./view/ComponentTextView'); |
||||
|
ComponentLinkView = require('./view/ComponentLinkView'); |
||||
|
var component, componentView; |
||||
|
|
||||
// Set default options
|
return { |
||||
for (var name in defaults) { |
|
||||
if (!(name in c)) |
|
||||
c[name] = defaults[name]; |
|
||||
} |
|
||||
|
|
||||
if(!c.wrapper.attributes) |
/** |
||||
c.wrapper.attributes = {}; |
* Name of the module |
||||
c.wrapper.attributes.id = 'wrapper'; |
* @type {String} |
||||
|
* @private |
||||
|
*/ |
||||
|
name: 'DomComponents', |
||||
|
|
||||
// If there is no components try to append defaults
|
/** |
||||
if(!c.wrapper.components.length && c.defaults.length){ |
* Mandatory for the storage manager |
||||
c.wrapper.components = c.defaults; |
* @type {String} |
||||
} |
* @private |
||||
|
*/ |
||||
|
storageKey: function(){ |
||||
|
var keys = []; |
||||
|
var smc = (c.stm && c.stm.getConfig()) || {}; |
||||
|
if(smc.storeHtml) |
||||
|
keys.push('html'); |
||||
|
if(smc.storeComponents) |
||||
|
keys.push('components'); |
||||
|
return keys; |
||||
|
}, |
||||
|
|
||||
if(!c.wrapper.style) |
/** |
||||
c.wrapper.style = {}; |
* Initialize module. Called on a new instance of the editor with configurations passed |
||||
|
* inside 'domComponents' field |
||||
|
* @param {Object} config Configurations |
||||
|
* @private |
||||
|
*/ |
||||
|
init: function(config) { |
||||
|
c = config || {}; |
||||
|
if(c.em) |
||||
|
c.components = c.em.config.components || c.components; |
||||
|
|
||||
c.wrapper.style.position = 'relative'; |
for (var name in defaults) { |
||||
this.component = new Component(c.wrapper, { sm: c.em }); |
if (!(name in c)) |
||||
|
c[name] = defaults[name]; |
||||
|
} |
||||
|
|
||||
var obj = { |
var ppfx = c.pStylePrefix; |
||||
model: this.component, |
if(ppfx) |
||||
config: c, |
c.stylePrefix = ppfx + c.stylePrefix; |
||||
}; |
|
||||
|
// Load dependencies
|
||||
|
if(c.em){ |
||||
|
c.rte = c.em.get('rte') || ''; |
||||
|
c.modal = c.em.get('Modal') || ''; |
||||
|
c.am = c.em.get('AssetManager') || ''; |
||||
|
} |
||||
|
|
||||
|
component = new Component(c.wrapper, { sm: c.em, config: c }); |
||||
|
component.set({ attributes: {id: 'wrapper'}}); |
||||
|
component.get('components').add(c.components); |
||||
|
componentView = new ComponentView({ |
||||
|
model: component, |
||||
|
config: c, |
||||
|
}); |
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* On load callback |
||||
|
* @private |
||||
|
*/ |
||||
|
onLoad: function(){ |
||||
|
if(c.stm && c.stm.getConfig().autoload) |
||||
|
this.load(); |
||||
|
|
||||
|
if(c.stm && c.stm.isAutosave()){ |
||||
|
c.em.initUndoManager(); |
||||
|
c.em.initChildrenComp(this.getWrapper()); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Load components from the passed object, if the object is empty will try to fetch them |
||||
|
* autonomously from the selected storage |
||||
|
* The fetched data will be added to the collection |
||||
|
* @param {Object} data Object of data to load |
||||
|
* @return {Object} Loaded data |
||||
|
*/ |
||||
|
load: function(data){ |
||||
|
var d = data || ''; |
||||
|
if(!d && c.stm) |
||||
|
d = c.em.getCacheLoad(); |
||||
|
var obj = ''; |
||||
|
if(d.components){ |
||||
|
try{ |
||||
|
obj = JSON.parse(d.components); |
||||
|
}catch(err){} |
||||
|
}else if(d.html) |
||||
|
obj = d.html; |
||||
|
|
||||
this.c = c; |
if(obj) |
||||
this.ComponentView = new ComponentView(obj); |
this.getComponents().reset(obj); |
||||
} |
return obj; |
||||
|
}, |
||||
|
|
||||
Components.prototype = { |
/** |
||||
|
* Store components on the selected storage |
||||
|
* @param {Boolean} noStore If true, won't store |
||||
|
* @return {Object} Data to store |
||||
|
*/ |
||||
|
store: function(noStore){ |
||||
|
if(!c.stm) |
||||
|
return; |
||||
|
var obj = {}; |
||||
|
var keys = this.storageKey(); |
||||
|
if(keys.indexOf('html') >= 0) |
||||
|
obj.html = c.em.getHtml(); |
||||
|
if(keys.indexOf('components') >= 0) |
||||
|
obj.components = JSON.stringify(c.em.getComponents()); |
||||
|
if(!noStore) |
||||
|
c.stm.store(obj); |
||||
|
return obj; |
||||
|
}, |
||||
|
|
||||
/** |
/** |
||||
* Returns main wrapper which will contain all new components |
* Returns privately the main wrapper |
||||
* |
|
||||
* @return {Object} |
* @return {Object} |
||||
|
* @private |
||||
*/ |
*/ |
||||
getComponent : function(){ |
getComponent : function(){ |
||||
return this.component; |
return component; |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Returns main wrapper which will contain all new components |
* Returns root component inside the canvas. Something like <body> inside HTML page |
||||
* |
* The wrapper doesn't differ from the original Component Model |
||||
* @return {Object} |
* @return {Component} Root Component |
||||
|
* @example |
||||
|
* // Change background of the wrapper and set some attribute
|
||||
|
* var wrapper = domComponents.getWrapper(); |
||||
|
* wrapper.set('style', {'background-color': 'red'}); |
||||
|
* wrapper.set('attributes', {'title': 'Hello!'}); |
||||
*/ |
*/ |
||||
getWrapper: function(){ |
getWrapper: function(){ |
||||
return this.getComponent(); |
return this.getComponent(); |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Returns children from the wrapper |
* Returns wrapper's children collection. Once you have the collection you can |
||||
* |
* add other Components(Models) inside. Each component can have several nested |
||||
* @return {Object} |
* components inside and you can nest them as more as you wish. |
||||
|
* @return {Components} Collection of components |
||||
|
* @example |
||||
|
* // Let's add some component
|
||||
|
* var wrapperChildren = domComponents.getComponents(); |
||||
|
* var comp1 = wrapperChildren.add({ |
||||
|
* style: { 'background-color': 'red'} |
||||
|
* }); |
||||
|
* var comp2 = wrapperChildren.add({ |
||||
|
* tagName: 'span', |
||||
|
* attributes: { title: 'Hello!'} |
||||
|
* }); |
||||
|
* // Now let's add an other one inside first component
|
||||
|
* // First we have to get the collection inside. Each
|
||||
|
* // component has 'components' property
|
||||
|
* var comp1Children = comp1.get('components'); |
||||
|
* // Procede as before. You could also add multiple objects
|
||||
|
* comp1Children.add([ |
||||
|
* { style: { 'background-color': 'blue'}}, |
||||
|
* { style: { height: '100px', width: '100px'}} |
||||
|
* ]); |
||||
|
* // Remove comp2
|
||||
|
* wrapperChildren.remove(comp2); |
||||
*/ |
*/ |
||||
getComponents: function(){ |
getComponents: function(){ |
||||
return this.getWrapper().get('components'); |
return this.getWrapper().get('components'); |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* Render and returns wrapper |
* Add new components to the wrapper's children. It's the same |
||||
* |
* as 'domComponents.getComponents().add(...)' |
||||
* @return {Object} |
* @param {Object|Component|Array<Object>} component Component/s to add |
||||
|
* @param {string} [component.tagName='div'] Tag name |
||||
|
* @param {string} [component.type=''] Type of the component. Available: ''(default), 'text', 'image' |
||||
|
* @param {boolean} [component.removable=true] If component is removable |
||||
|
* @param {boolean} [component.draggable=true] If is possible to move the component around the structure |
||||
|
* @param {boolean} [component.droppable=true] If is possible to drop inside other components |
||||
|
* @param {boolean} [component.badgable=true] If the badge is visible when the component is selected |
||||
|
* @param {boolean} [component.stylable=true] If is possible to style component |
||||
|
* @param {boolean} [component.copyable=true] If is possible to copy&paste the component |
||||
|
* @param {string} [component.content=''] String inside component |
||||
|
* @param {Object} [component.style={}] Style object |
||||
|
* @param {Object} [component.attributes={}] Attribute object |
||||
|
* @return {Component|Array<Component>} Component/s added |
||||
|
* @example |
||||
|
* // Example of a new component with some extra property
|
||||
|
* var comp1 = domComponents.addComponent({ |
||||
|
* tagName: 'div', |
||||
|
* removable: true, // Can't remove it
|
||||
|
* draggable: true, // Can't move it
|
||||
|
* copyable: true, // Disable copy/past
|
||||
|
* content: 'Content text', // Text inside component
|
||||
|
* style: { color: 'red'}, |
||||
|
* attributes: { title: 'here' } |
||||
|
* }); |
||||
*/ |
*/ |
||||
render : function(){ |
addComponent: function(component){ |
||||
return this.ComponentView.render().el; |
return this.getComponents().add(component); |
||||
}, |
}, |
||||
}; |
|
||||
|
|
||||
return Components; |
/** |
||||
}); |
* Render and returns wrapper element with all components inside. |
||||
|
* Once the wrapper is rendered, and it's what happens when you init the editor, |
||||
|
* the all new components will be added automatically and property changes are all |
||||
|
* updated immediately |
||||
|
* @return {HTMLElement} |
||||
|
*/ |
||||
|
render: function(){ |
||||
|
return componentView.render().el; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Remove all components |
||||
|
* @return {this} |
||||
|
*/ |
||||
|
clear: function(){ |
||||
|
var c = this.getComponents(); |
||||
|
for(var i = 0, len = c.length; i < len; i++) |
||||
|
c.pop(); |
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Set components |
||||
|
* @param {Object|string} components HTML string or components model |
||||
|
* @return {this} |
||||
|
* @private |
||||
|
*/ |
||||
|
setComponents: function(components){ |
||||
|
this.clear().addComponent(components); |
||||
|
}, |
||||
|
|
||||
|
}; |
||||
|
}; |
||||
|
}); |
||||
|
|||||
@ -1,14 +1,13 @@ |
|||||
define(['./Component'], |
define(['./Component'], |
||||
function (Component) { |
function (Component) { |
||||
/** |
|
||||
* @class ComponentImage |
return Component.extend({ |
||||
* */ |
|
||||
return Component.extend({ |
|
||||
|
|
||||
defaults: _.extend({}, Component.prototype.defaults, { |
defaults: _.extend({}, Component.prototype.defaults, { |
||||
src : '', |
src: '', |
||||
droppable : false, |
droppable: false, |
||||
|
traits: ['alt'], |
||||
}), |
}), |
||||
|
|
||||
}); |
}); |
||||
}); |
}); |
||||
|
|||||
@ -0,0 +1,13 @@ |
|||||
|
define(['./Component'], |
||||
|
function (Component) { |
||||
|
|
||||
|
return Component.extend({ |
||||
|
|
||||
|
defaults: _.extend({}, Component.prototype.defaults, { |
||||
|
tagName: 'a', |
||||
|
droppable: false, |
||||
|
traits: ['title', 'href', 'target'], |
||||
|
}), |
||||
|
|
||||
|
}); |
||||
|
}); |
||||
@ -1,14 +1,12 @@ |
|||||
define(['./Component'], |
define(['./Component'], |
||||
function (Component) { |
function (Component) { |
||||
/** |
|
||||
* @class ComponentText |
return Component.extend({ |
||||
* */ |
|
||||
return Component.extend({ |
|
||||
|
|
||||
defaults: _.extend({}, Component.prototype.defaults, { |
defaults: _.extend({}, Component.prototype.defaults, { |
||||
content : '', |
content : '', |
||||
droppable : false, |
droppable : false, |
||||
}), |
}), |
||||
|
|
||||
}); |
}); |
||||
}); |
}); |
||||
|
|||||
@ -0,0 +1,15 @@ |
|||||
|
define(['backbone', './ComponentView'], |
||||
|
function (Backbone, ComponentView) { |
||||
|
|
||||
|
return ComponentView.extend({ |
||||
|
|
||||
|
events: { |
||||
|
'click': 'onClick', |
||||
|
}, |
||||
|
|
||||
|
onClick: function(e) { |
||||
|
e.preventDefault(); |
||||
|
}, |
||||
|
|
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,68 @@ |
|||||
|
define(['backbone'], |
||||
|
function(Backbone) { |
||||
|
|
||||
|
return Backbone.View.extend({ |
||||
|
|
||||
|
itemView: '', |
||||
|
|
||||
|
// Defines the View per type
|
||||
|
itemsView: '', |
||||
|
|
||||
|
itemType: 'type', |
||||
|
|
||||
|
initialize: function(opts, config) { |
||||
|
this.config = config || {}; |
||||
|
}, |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* Add new model to the collection |
||||
|
* @param {Model} model |
||||
|
* @private |
||||
|
* */ |
||||
|
addTo: function(model){ |
||||
|
this.add(model); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Render new model inside the view |
||||
|
* @param {Model} model |
||||
|
* @param {Object} fragment Fragment collection |
||||
|
* @private |
||||
|
* */ |
||||
|
add: function(model, fragment){ |
||||
|
var frag = fragment || null; |
||||
|
var itemView = this.itemView; |
||||
|
if(this.itemsView){ |
||||
|
var typeField = model.get(this.itemType); |
||||
|
itemView = this.itemsView[typeField]; |
||||
|
} |
||||
|
var view = new itemView({ |
||||
|
model: model, |
||||
|
config: this.config |
||||
|
}, this.config); |
||||
|
var rendered = view.render().el; |
||||
|
|
||||
|
if(frag) |
||||
|
frag.appendChild(rendered); |
||||
|
else |
||||
|
this.$el.append(rendered); |
||||
|
}, |
||||
|
|
||||
|
|
||||
|
|
||||
|
render: function() { |
||||
|
var frag = document.createDocumentFragment(); |
||||
|
this.$el.empty(); |
||||
|
|
||||
|
if(this.collection.length) |
||||
|
this.collection.each(function(model){ |
||||
|
this.add(model, frag); |
||||
|
}, this); |
||||
|
|
||||
|
this.$el.append(frag); |
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
|
}); |
||||
|
}); |
||||
@ -1,72 +1,183 @@ |
|||||
define(function () { |
define(function () { |
||||
var config = { |
var blkStyle = '.blk-row::after{ content: ""; clear: both; display: block;} .blk-row{padding: 10px;}'; |
||||
|
return { |
||||
|
|
||||
// Style prefix
|
// Style prefix
|
||||
stylePrefix: 'wte-', |
stylePrefix: 'gjs-', |
||||
|
|
||||
// Prefix to use inside local storage name
|
//TEMP
|
||||
storagePrefix: 'wte-', |
components: '', |
||||
|
|
||||
// Editor ID. Useful in case of multiple editors on the same page
|
// Enable/Disable possibility to copy(ctrl + c) & paste(ctrl + v) components
|
||||
id : '', |
copyPaste: true, |
||||
|
|
||||
// Where render the editor
|
// Enable/Disable undo manager
|
||||
container : '', |
undoManager: true, |
||||
|
|
||||
idCanvas : 'canvas', |
// Height for the editor container
|
||||
|
height: '900px', |
||||
|
|
||||
idCanvasOverlay : 'canvas-overlay', |
// Width for the editor container
|
||||
|
width: '100%', |
||||
|
|
||||
idWrapper : 'wrapper', |
// The css that could only be seen (for instance, inside the code viewer)
|
||||
|
protectedCss: '*{box-sizing: border-box;}body{margin:0;height:100%}#wrapper{min-height:100%; overflow:auto}', |
||||
|
|
||||
// Enable/Disable possibility to copy(ctrl + c) & paste(ctrl + v) components
|
// Default command
|
||||
copyPaste : true, |
defaultCommand: 'select-comp', |
||||
|
|
||||
// Enable/Disable undo manager
|
// If true render a select of available devices
|
||||
undoManager : true, |
showDevices: 1, |
||||
|
|
||||
//Indicates which storage to use. Available: local | remote | none
|
// Dom element
|
||||
storageType : 'local', |
el: '', |
||||
|
|
||||
//Configurations for Asset Manager
|
//Configurations for Asset Manager
|
||||
assetManager : {}, |
assetManager: {}, |
||||
|
|
||||
//Configurations for Canvas
|
//Configurations for Canvas
|
||||
canvas : {}, |
canvas: {}, |
||||
|
|
||||
//Configurations for Style Manager
|
//Configurations for Style Manager
|
||||
styleManager : {}, |
styleManager: {}, |
||||
|
|
||||
//Configurations for Layers
|
//Configurations for Layers
|
||||
layers : {}, |
layers: {}, |
||||
|
|
||||
//Configurations for Storage Manager
|
//Configurations for Storage Manager
|
||||
storageManager : {}, |
storageManager: {}, |
||||
|
|
||||
//Configurations for Rich Text Editor
|
//Configurations for Rich Text Editor
|
||||
rte : {}, |
rte: {}, |
||||
|
|
||||
//Configurations for Components
|
//Configurations for DomComponents
|
||||
components : {}, |
domComponents: {}, |
||||
|
|
||||
//Configurations for Modal Dialog
|
//Configurations for Modal Dialog
|
||||
modal : {}, |
modal: {}, |
||||
|
|
||||
//Configurations for Code Manager
|
//Configurations for Code Manager
|
||||
codeManager : {}, |
codeManager: {}, |
||||
|
|
||||
//Configurations for Panels
|
//Configurations for Panels
|
||||
panels : {}, |
panels: {}, |
||||
|
|
||||
//Configurations for Commands
|
//Configurations for Commands
|
||||
commands : {}, |
commands: {}, |
||||
|
|
||||
//Configurations for Class Manager
|
|
||||
classManager : {}, |
|
||||
|
|
||||
//Configurations for Css Composer
|
//Configurations for Css Composer
|
||||
cssComposer : {}, |
cssComposer: {}, |
||||
|
|
||||
|
//Configurations for Selector Manager
|
||||
|
selectorManager: {}, |
||||
|
|
||||
|
//Configurations for Device Manager
|
||||
|
deviceManager: { |
||||
|
'devices': [{ |
||||
|
name: 'Desktop', |
||||
|
width: '', |
||||
|
},{ |
||||
|
name: 'Tablet', |
||||
|
width: '992px', |
||||
|
},{ |
||||
|
name: 'Mobile landscape', |
||||
|
width: '768px', |
||||
|
},{ |
||||
|
name: 'Mobile portrait', |
||||
|
width: '480px', |
||||
|
}], |
||||
|
}, |
||||
|
|
||||
|
//Configurations for Block Manager
|
||||
|
blockManager: { |
||||
|
'blocks': [{ |
||||
|
id: 'b1', |
||||
|
label: '1 Block', |
||||
|
content: '<div class="blk-row"><div class="blk1"></div></div><style>'+ blkStyle +'.blk1{width: 100%;padding: 10px;min-height: 75px;}</style>', |
||||
|
attributes: {class:'gjs-fonts gjs-f-b1'} |
||||
|
},{ |
||||
|
id: 'b2', |
||||
|
label: '2 Blocks', |
||||
|
content: '<div class="blk-row"><div class="blk2"></div><div class="blk2"></div></div><style>'+ blkStyle +'.blk2{float: left;width: 50%;padding: 10px;min-height: 75px;}</style>', |
||||
|
attributes: {class:'gjs-fonts gjs-f-b2'} |
||||
|
},{ |
||||
|
id: 'b3', |
||||
|
label: '3 Blocks', |
||||
|
content: '<div class="blk-row"><div class="blk3"></div><div class="blk3"></div><div class="blk3"></div></div><style>'+ blkStyle +'.blk3{float: left;width: 33.3333%;padding: 10px;min-height: 75px;}</style>', |
||||
|
attributes: {class:'gjs-fonts gjs-f-b3'} |
||||
|
},{ |
||||
|
id: 'b4', |
||||
|
label: '3/7 Block', |
||||
|
content: '<div class="blk-row"><div class="blk37l"></div><div class="blk37r"></div></div></div><style>'+ blkStyle +'.blk37l{float: left;width: 30%;padding: 10px;min-height: 75px;}.blk37r{float: left;width: 70%;padding: 10px;min-height: 75px;}</style>', |
||||
|
attributes: {class:'gjs-fonts gjs-f-b37'} |
||||
|
},{ |
||||
|
id: 'hero', |
||||
|
label: 'Hero section', |
||||
|
content: '<header class="header-banner"> <div class="container-width">'+ |
||||
|
'<div class="logo-container"><div class="logo">GrapesJS</div></div>'+ |
||||
|
'<nav class="navbar">'+ |
||||
|
'<div class="menu-item">BUILDER</div><div class="menu-item">TEMPLATE</div><div class="menu-item">WEB</div>'+ |
||||
|
'</nav><div class="clearfix"></div>'+ |
||||
|
'<div class="lead-title">Build your templates without coding</div>'+ |
||||
|
'<div class="lead-btn">Try it now</div></div></header>', |
||||
|
attributes: {class:'gjs-fonts gjs-f-hero'} |
||||
|
},{ |
||||
|
id: 'h1p', |
||||
|
label: 'Text section', |
||||
|
content: '<h1 class="heading">Insert title here</h1><p class="paragraph">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua</p>', |
||||
|
attributes: {class:'gjs-fonts gjs-f-h1p'} |
||||
|
},{ |
||||
|
id: '3ba', |
||||
|
label: 'Badges', |
||||
|
content: '<div class="badges">'+ |
||||
|
'<div class="badge">'+ |
||||
|
'<div class="badge-header"></div>'+ |
||||
|
'<img class="badge-avatar" src="img/team1.jpg">'+ |
||||
|
'<div class="badge-body">'+ |
||||
|
'<div class="badge-name">Adam Smith</div><div class="badge-role">CEO</div><div class="badge-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</div>'+ |
||||
|
'</div>'+ |
||||
|
'<div class="badge-foot"><span class="badge-link">f</span><span class="badge-link">t</span><span class="badge-link">ln</span></div>'+ |
||||
|
'</div>'+ |
||||
|
'<div class="badge">'+ |
||||
|
'<div class="badge-header"></div>'+ |
||||
|
'<img class="badge-avatar" src="img/team2.jpg">'+ |
||||
|
'<div class="badge-body">'+ |
||||
|
'<div class="badge-name">John Black</div><div class="badge-role">Software Engineer</div><div class="badge-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</div>'+ |
||||
|
'</div>'+ |
||||
|
'<div class="badge-foot"><span class="badge-link">f</span><span class="badge-link">t</span><span class="badge-link">ln</span></div>'+ |
||||
|
'</div>'+ |
||||
|
'<div class="badge">'+ |
||||
|
'<div class="badge-header"></div>'+ |
||||
|
'<img class="badge-avatar" src="img/team3.jpg">'+ |
||||
|
'<div class="badge-body">'+ |
||||
|
'<div class="badge-name">Jessica White</div><div class="badge-role">Web Designer</div><div class="badge-desc">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</div>'+ |
||||
|
'</div>'+ |
||||
|
'<div class="badge-foot"><span class="badge-link">f</span><span class="badge-link">t</span><span class="badge-link">ln</span>'+ |
||||
|
'</div>'+ |
||||
|
'</div></div>', |
||||
|
attributes: {class:'gjs-fonts gjs-f-3ba'} |
||||
|
},{ |
||||
|
id: 'text', |
||||
|
label: 'Text', |
||||
|
attributes: {class:'gjs-fonts gjs-f-text'}, |
||||
|
content: { |
||||
|
type:'text', |
||||
|
content:'Insert your text here', |
||||
|
style: {padding: '10px' }, |
||||
|
activeOnRender: 1 |
||||
|
}, |
||||
|
},{ |
||||
|
id: 'image', |
||||
|
label: 'Image', |
||||
|
attributes: {class:'gjs-fonts gjs-f-image'}, |
||||
|
content: { type:'image', activeOnRender: 1}, |
||||
|
},{ |
||||
|
id: 'quo', |
||||
|
label: 'Quote', |
||||
|
content: '<blockquote class="quote">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ipsum dolor sit</blockquote>', |
||||
|
attributes: {class:'gjs-fonts gjs-f-quo'} |
||||
|
}], |
||||
|
}, |
||||
|
|
||||
}; |
}; |
||||
return config; |
}); |
||||
}); |
|
||||
|
|||||
@ -1,39 +1,402 @@ |
|||||
|
/** |
||||
|
* |
||||
|
* * [getConfig](#getconfig) |
||||
|
* * [getHtml](#gethtml) |
||||
|
* * [getCss](#getcss) |
||||
|
* * [getComponents](#getcomponents) |
||||
|
* * [setComponents](#setcomponents) |
||||
|
* * [getStyle](#getstyle) |
||||
|
* * [setStyle](#setstyle) |
||||
|
* * [getSelected](#getselected) |
||||
|
* * [setDevice](#setdevice) |
||||
|
* * [getDevice](#getdevice) |
||||
|
* * [runCommand](#runcommand) |
||||
|
* * [stopCommand](#stopcommand) |
||||
|
* * [store](#store) |
||||
|
* * [load](#load) |
||||
|
* * [getContainer](#getcontainer) |
||||
|
* * [on](#on) |
||||
|
* * [trigger](#trigger) |
||||
|
* * [render](#render) |
||||
|
* |
||||
|
* Editor class contains the top level API which you'll probably use to custom the editor or extend it with plugins. |
||||
|
* You get the Editor instance on init method |
||||
|
* |
||||
|
* ```js
|
||||
|
* var editor = grapesjs.init({...}); |
||||
|
* ``` |
||||
|
* Available events |
||||
|
* #run:{commandName} |
||||
|
* #stop:{commandName} |
||||
|
* #load - When the editor is loaded |
||||
|
* |
||||
|
* @module Editor |
||||
|
* @param {Object} config Configurations |
||||
|
* @param {string} config.container='' Selector for the editor container, eg. '#myEditor' |
||||
|
* @param {string|Array<Object>} [config.components=''] HTML string or object of components |
||||
|
* @param {string|Array<Object>} [config.style=''] CSS string or object of rules |
||||
|
* @param {Boolean} [config.fromElement=false] If true, will fetch HTML and CSS from selected container |
||||
|
* @param {Boolean} [config.copyPaste=true] Enable/Disable the possibility to copy(ctrl + c) & paste(ctrl + v) components |
||||
|
* @param {Boolean} [config.undoManager=true] Enable/Disable undo manager |
||||
|
* @param {Boolean} [config.autorender=true] If true renders editor on init |
||||
|
* @param {Boolean} [config.noticeOnUnload=true] Enable/Disable alert message before unload the page |
||||
|
* @param {string} [config.height='900px'] Height for the editor container |
||||
|
* @param {string} [config.width='100%'] Width for the editor container |
||||
|
* @param {Object} [config.storage={}] Storage manager configuration, see the relative documentation |
||||
|
* @param {Object} [config.styleManager={}] Style manager configuration, see the relative documentation |
||||
|
* @param {Object} [config.commands={}] Commands configuration, see the relative documentation |
||||
|
* @param {Object} [config.domComponents={}] Components configuration, see the relative documentation |
||||
|
* @param {Object} [config.panels={}] Panels configuration, see the relative documentation |
||||
|
* @param {Object} [config.showDevices=true] If true render a select of available devices inside style manager panel |
||||
|
* @param {string} [config.defaultCommand='select-comp'] Command to execute when no other command is running |
||||
|
* @param {Array} [config.plugins=[]] Array of plugins to execute on start |
||||
|
* @param {Object} [config.pluginsOpts={}] Custom options for plugins |
||||
|
* @example |
||||
|
* var editor = grapesjs.init({ |
||||
|
* container : '#gjs', |
||||
|
* components: '<div class="txt-red">Hello world!</div>', |
||||
|
* style: '.txt-red{color: red}', |
||||
|
* }); |
||||
|
*/ |
||||
define(function (require){ |
define(function (require){ |
||||
/** |
|
||||
* @class Grapes |
var Editor = function(config) { |
||||
* @param {Object} Configurations |
var c = config || {}, |
||||
* |
defaults = require('./config/config'), |
||||
* @return {Object} |
EditorModel = require('./model/Editor'), |
||||
* */ |
EditorView = require('./view/EditorView'); |
||||
var Grapes = function(config) |
|
||||
{ |
|
||||
var c = config || {}, |
|
||||
defaults = require('./config/config'), |
|
||||
Editor = require('./model/Editor'), |
|
||||
EditorView = require('./view/EditorView'); |
|
||||
|
|
||||
for (var name in defaults) { |
for (var name in defaults) { |
||||
if (!(name in c)) |
if (!(name in c)) |
||||
c[name] = defaults[name]; |
c[name] = defaults[name]; |
||||
} |
} |
||||
|
c.pStylePrefix = c.stylePrefix; |
||||
|
|
||||
this.editor = new Editor(c); |
var em = new EditorModel(c); |
||||
var obj = { |
|
||||
model : this.editor, |
|
||||
config : c, |
|
||||
}; |
|
||||
|
|
||||
this.editorView = new EditorView(obj); |
var editorView = new EditorView({ |
||||
}; |
model: em, |
||||
|
config: c, |
||||
|
}); |
||||
|
|
||||
Grapes.prototype = { |
return { |
||||
|
|
||||
render : function() |
/** |
||||
{ |
* @property {EditorModel} |
||||
return this.editorView.render().$el; |
* @private |
||||
} |
*/ |
||||
|
editor: em, |
||||
|
|
||||
|
/** |
||||
|
* @property {DomComponents} |
||||
|
*/ |
||||
|
DomComponents: em.get('DomComponents'), |
||||
|
|
||||
|
/** |
||||
|
* @property {CssComposer} |
||||
|
*/ |
||||
|
CssComposer: em.get('CssComposer'), |
||||
|
|
||||
|
/** |
||||
|
* @property {StorageManager} |
||||
|
*/ |
||||
|
StorageManager: em.get('StorageManager'), |
||||
|
|
||||
|
/** |
||||
|
* @property {AssetManager} |
||||
|
*/ |
||||
|
AssetManager: em.get('AssetManager'), |
||||
|
|
||||
|
/** |
||||
|
* @property {BlockManager} |
||||
|
*/ |
||||
|
BlockManager: em.get('BlockManager'), |
||||
|
|
||||
|
/** |
||||
|
* @property {TraitManager} |
||||
|
*/ |
||||
|
TraitManager: em.get('TraitManager'), |
||||
|
|
||||
|
/** |
||||
|
* @property {SelectorManager} |
||||
|
*/ |
||||
|
SelectorManager: em.get('SelectorManager'), |
||||
|
|
||||
|
/** |
||||
|
* @property {CodeManager} |
||||
|
*/ |
||||
|
CodeManager: em.get('CodeManager'), |
||||
|
|
||||
|
/** |
||||
|
* @property {Commands} |
||||
|
*/ |
||||
|
Commands: em.get('Commands'), |
||||
|
|
||||
|
/** |
||||
|
* @property {Modal} |
||||
|
*/ |
||||
|
Modal: em.get('Modal'), |
||||
|
|
||||
|
/** |
||||
|
* @property {Panels} |
||||
|
*/ |
||||
|
Panels: em.get('Panels'), |
||||
|
|
||||
|
/** |
||||
|
* @property {StyleManager} |
||||
|
*/ |
||||
|
StyleManager: em.get('StyleManager'), |
||||
|
|
||||
|
/** |
||||
|
* @property {Canvas} |
||||
|
*/ |
||||
|
Canvas: em.get('Canvas'), |
||||
|
|
||||
|
/** |
||||
|
* @property {UndoManager} |
||||
|
*/ |
||||
|
UndoManager: em.get('UndoManager'), |
||||
|
|
||||
|
/** |
||||
|
* @property {DeviceManager} |
||||
|
*/ |
||||
|
DeviceManager: em.get('DeviceManager'), |
||||
|
|
||||
|
/** |
||||
|
* @property {RichTextEditor} |
||||
|
*/ |
||||
|
RichTextEditor: em.get('rte'), |
||||
|
|
||||
|
/** |
||||
|
* @property {Utils} |
||||
|
*/ |
||||
|
Utils: em.get('Utils'), |
||||
|
|
||||
|
/** |
||||
|
* @property {Utils} |
||||
|
*/ |
||||
|
Config: em.get('Config'), |
||||
|
|
||||
|
/** |
||||
|
* Initialize editor model |
||||
|
* @return {this} |
||||
|
* @private |
||||
|
*/ |
||||
|
init: function(){ |
||||
|
em.init(this); |
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns configuration object |
||||
|
* @return {Object} |
||||
|
*/ |
||||
|
getConfig: function(){ |
||||
|
return c; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns HTML built inside canvas |
||||
|
* @return {string} HTML string |
||||
|
*/ |
||||
|
getHtml: function(){ |
||||
|
return em.getHtml(); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns CSS built inside canvas |
||||
|
* @return {string} CSS string |
||||
|
*/ |
||||
|
getCss: function(){ |
||||
|
return em.getCss(); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns components in JSON format object |
||||
|
* @return {Object} |
||||
|
*/ |
||||
|
getComponents: function(){ |
||||
|
return em.get('DomComponents').getComponents(); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Set components inside editor's canvas. This method overrides actual components |
||||
|
* @param {Array<Object>|Object|string} components HTML string or components model |
||||
|
* @return {this} |
||||
|
* @example |
||||
|
* editor.setComponents('<div class="cls">New component</div>'); |
||||
|
* // or
|
||||
|
* editor.setComponents({ |
||||
|
* type: 'text', |
||||
|
* classes:['cls'], |
||||
|
* content: 'New component' |
||||
|
* }); |
||||
|
*/ |
||||
|
setComponents: function(components){ |
||||
|
em.setComponents(components); |
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns style in JSON format object |
||||
|
* @return {Object} |
||||
|
*/ |
||||
|
getStyle: function(){ |
||||
|
return em.get('CssComposer').getAll(); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Set style inside editor's canvas. This method overrides actual style |
||||
|
* @param {Array<Object>|Object|string} style CSS string or style model |
||||
|
* @return {this} |
||||
|
* @example |
||||
|
* editor.setStyle('.cls{color: red}'); |
||||
|
* //or
|
||||
|
* editor.setStyle({ |
||||
|
* selectors: ['cls'] |
||||
|
* style: { color: 'red' } |
||||
|
* }); |
||||
|
*/ |
||||
|
setStyle: function(style){ |
||||
|
em.setStyle(style); |
||||
|
return this; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns selected component, if there is one |
||||
|
* @return {grapesjs.Component} |
||||
|
*/ |
||||
|
getSelected: function(){ |
||||
|
return em.getSelected(); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Set device to the editor. If the device exists it will |
||||
|
* change the canvas to the proper width |
||||
|
* @return {this} |
||||
|
* @example |
||||
|
* editor.setDevice('Tablet'); |
||||
|
*/ |
||||
|
setDevice: function(name){ |
||||
|
return em.set('device', name); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Return the actual active device |
||||
|
* @return {string} Device name |
||||
|
* @example |
||||
|
* var device = editor.getDevice(); |
||||
|
* console.log(device); |
||||
|
* // 'Tablet'
|
||||
|
*/ |
||||
|
getDevice: function(){ |
||||
|
return em.get('device'); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Execute command |
||||
|
* @param {string} id Command ID |
||||
|
* @param {Object} options Custom options |
||||
|
* @example |
||||
|
* editor.runCommand('myCommand', {someValue: 1}); |
||||
|
*/ |
||||
|
runCommand: function(id, options) { |
||||
|
var command = em.get('Commands').get(id); |
||||
|
|
||||
|
if(command){ |
||||
|
command.run(this, this, options); |
||||
|
this.trigger('run:' + id); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Stop the command if stop method was provided |
||||
|
* @param {string} id Command ID |
||||
|
* @param {Object} options Custom options |
||||
|
* @example |
||||
|
* editor.stopCommand('myCommand', {someValue: 1}); |
||||
|
*/ |
||||
|
stopCommand: function(id, options) { |
||||
|
var command = em.get('Commands').get(id); |
||||
|
|
||||
|
if(command){ |
||||
|
command.stop(this, this, options); |
||||
|
this.trigger('stop:' + id); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Store data to the current storage |
||||
|
* @return {Object} Stored data |
||||
|
*/ |
||||
|
store: function(){ |
||||
|
return em.store(); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Load data from the current storage |
||||
|
* @return {Object} Stored data |
||||
|
*/ |
||||
|
load: function(){ |
||||
|
return em.load(); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns container element. The one which was indicated as 'container' on init method |
||||
|
* @return {HTMLElement} |
||||
|
*/ |
||||
|
getContainer: function(){ |
||||
|
return c.el; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Attach event |
||||
|
* @param {string} event Event name |
||||
|
* @param {Function} callback Callback function |
||||
|
* @return {this} |
||||
|
*/ |
||||
|
on: function(event, callback){ |
||||
|
return em.on(event, callback); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Trigger event |
||||
|
* @param {string} event Event to trigger |
||||
|
* @return {this} |
||||
|
*/ |
||||
|
trigger: function(event){ |
||||
|
return em.trigger(event); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns editor element |
||||
|
* @return {HTMLElement} |
||||
|
* @private |
||||
|
*/ |
||||
|
getEl: function(){ |
||||
|
return editorView.el; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Returns editor model |
||||
|
* @return {Model} |
||||
|
* @private |
||||
|
*/ |
||||
|
getModel: function(){ |
||||
|
return em; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Render editor |
||||
|
* @return {HTMLElement} |
||||
|
*/ |
||||
|
render: function() { |
||||
|
return editorView.render().el; |
||||
|
}, |
||||
|
|
||||
|
}; |
||||
|
|
||||
}; |
}; |
||||
|
|
||||
return Grapes; |
return Editor; |
||||
}); |
}); |
||||
|
|||||
@ -1,41 +1,41 @@ |
|||||
define(['backbone'], |
define(['backbone'], |
||||
function(Backbone){ |
function(Backbone){ |
||||
/** |
|
||||
* @class EditorView |
|
||||
* */ |
|
||||
return Backbone.View.extend({ |
return Backbone.View.extend({ |
||||
|
|
||||
initialize: function() { |
initialize: function() { |
||||
this.cv = this.model.get('Canvas'); |
|
||||
this.pn = this.model.get('Panels'); |
this.pn = this.model.get('Panels'); |
||||
this.css = this.model.get('CssComposer'); |
this.conf = this.model.config; |
||||
this.className = this.model.config.stylePrefix + 'editor'; |
this.className = this.conf.stylePrefix + 'editor'; |
||||
|
this.model.on('loaded', function(){ |
||||
|
this.pn.active(); |
||||
|
this.model.runDefault(); |
||||
|
this.model.trigger('load'); |
||||
|
}, this); |
||||
}, |
}, |
||||
|
|
||||
render: function(){ |
render: function(){ |
||||
|
var conf = this.conf; |
||||
this.$el.empty(); |
this.$el.empty(); |
||||
|
this.$cont = $(conf.el || ('body ' + conf.container)); |
||||
|
|
||||
this.$cont = $('body ' + this.model.config.container); |
if(conf.width) |
||||
|
this.$cont.css('width', conf.width); |
||||
this.model.set('$editor', this.$el); |
|
||||
|
|
||||
if(this.cv) |
if(conf.height) |
||||
this.$el.append(this.cv.render()); |
this.$cont.css('height', conf.height); |
||||
|
|
||||
if(this.pn) |
// Canvas
|
||||
this.$el.append(this.pn.render()); |
this.$el.append(this.model.get('Canvas').render()); |
||||
|
|
||||
if(this.css) |
// Panels
|
||||
this.$el.append(this.css.render()); |
this.$el.append(this.pn.render()); |
||||
|
|
||||
this.$el.attr('class', this.className); |
this.$el.attr('class', this.className); |
||||
|
|
||||
this.$cont.html(this.$el); |
this.$cont.html(this.$el); |
||||
|
|
||||
if(this.pn) |
|
||||
this.pn.active(); |
|
||||
|
|
||||
return this; |
return this; |
||||
} |
} |
||||
}); |
}); |
||||
}); |
}); |
||||
|
|||||
@ -0,0 +1,37 @@ |
|||||
|
define(function () { |
||||
|
return { |
||||
|
// If true renders editor on init
|
||||
|
autorender: 1, |
||||
|
|
||||
|
// Where init the editor
|
||||
|
container: '', |
||||
|
|
||||
|
// HTML string or object of components
|
||||
|
components: '', |
||||
|
|
||||
|
// CSS string or object of rules
|
||||
|
style: '', |
||||
|
|
||||
|
// If true, will fetch HTML and CSS from selected container
|
||||
|
fromElement: 0, |
||||
|
|
||||
|
// ---
|
||||
|
// Enable/Disable the possibility to copy(ctrl + c) & paste(ctrl + v) components
|
||||
|
copyPaste: true, |
||||
|
|
||||
|
// Enable/Disable undo manager
|
||||
|
undoManager: true, |
||||
|
|
||||
|
// Show an alert before unload the page
|
||||
|
noticeOnUnload: true, |
||||
|
|
||||
|
// Storage Manager
|
||||
|
storageManager: {}, |
||||
|
|
||||
|
// Array of plugins to init
|
||||
|
plugins: [], |
||||
|
|
||||
|
// Custom options for plugins
|
||||
|
pluginsOpts: {} |
||||
|
}; |
||||
|
}); |
||||
@ -0,0 +1,79 @@ |
|||||
|
define(function (require) { |
||||
|
|
||||
|
return function(config) { |
||||
|
|
||||
|
var c = config || {}, |
||||
|
defaults = require('./config/config'), |
||||
|
Editor = require('editor/main'), |
||||
|
PluginManager = require('PluginManager'); |
||||
|
|
||||
|
var plugins = new PluginManager(); |
||||
|
var editors = []; |
||||
|
|
||||
|
return { |
||||
|
|
||||
|
plugins: plugins, |
||||
|
|
||||
|
/** |
||||
|
* Initializes an editor based on passed options |
||||
|
* @param {Object} config Configuration object |
||||
|
* @param {string} config.container Selector which indicates where render the editor |
||||
|
* @param {Object|string} config.components='' HTML string or Component model in JSON format |
||||
|
* @param {Object|string} config.style='' CSS string or CSS model in JSON format |
||||
|
* @param {Boolean} [config.fromElement=false] If true, will fetch HTML and CSS from selected container |
||||
|
* @param {Boolean} [config.copyPaste=true] Enable/Disable the possibility to copy(ctrl+c) & paste(ctrl+v) components |
||||
|
* @param {Boolean} [config.undoManager=true] Enable/Disable undo manager |
||||
|
* @param {Array} [config.plugins=[]] Array of plugins to execute on start |
||||
|
* @return {grapesjs.Editor} GrapesJS editor instance |
||||
|
* @example |
||||
|
* var editor = grapesjs.init({ |
||||
|
* container: '#myeditor', |
||||
|
* components: '<article class="hello">Hello world</article>', |
||||
|
* style: '.hello{color: red}', |
||||
|
* }) |
||||
|
*/ |
||||
|
init: function(config) { |
||||
|
var c = config || {}; |
||||
|
var els = c.container; |
||||
|
|
||||
|
// Set default options
|
||||
|
for (var name in defaults) { |
||||
|
if (!(name in c)) |
||||
|
c[name] = defaults[name]; |
||||
|
} |
||||
|
|
||||
|
if(!els) |
||||
|
throw new Error("'container' is required"); |
||||
|
|
||||
|
if(c.noticeOnUnload) |
||||
|
window.onbeforeunload = function(e) { |
||||
|
return 1; |
||||
|
}; |
||||
|
|
||||
|
c.el = document.querySelector(els); |
||||
|
var editor = new Editor(c).init(); |
||||
|
|
||||
|
// Execute all plugins
|
||||
|
var plugs = plugins.getAll(); |
||||
|
for (var id in plugs){ |
||||
|
// Check if plugin is requested
|
||||
|
if(c.plugins.indexOf(id) < 0) |
||||
|
continue; |
||||
|
|
||||
|
var opts = c.pluginsOpts[id] || {}; |
||||
|
var plug = plugins.get(id); |
||||
|
plug(editor, opts); |
||||
|
} |
||||
|
|
||||
|
if(c.autorender) |
||||
|
editor.render(); |
||||
|
|
||||
|
editors.push(editor); |
||||
|
return editor; |
||||
|
}, |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
}(); |
||||
|
|
||||
|
}); |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue