Created
December 6, 2011 16:10
-
-
Save adjohu/1438739 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Defaults | |
grid = [10,10] | |
container = "#container" | |
# Button types | |
window.buttons = | |
text : | |
html : '<div> | |
<div class="content"> | |
Double click to edit me | |
</div> | |
</div>' | |
dblclick : -> | |
# Capture arguments in case this is being called by something else | |
button_type = arguments[0] | |
ckeditor_change_callback = arguments[1] or -> true | |
content_el = $(this).children('.content') | |
content = content_el.html() | |
$this = $(this) | |
# Make new instance of CKEditor -> remove current if exists. | |
editor = CKEDITOR.instances.editor | |
if editor? | |
CKEDITOR.remove editor | |
editor.destroy() | |
ckeditor_config = {} | |
if button_type? and button_type is 'Source' | |
ckeditor_config.toolbar = 'Source' | |
ckeditor_config.startupMode = 'source' | |
$('#editor').ckeditor(ckeditor_config) | |
editor = CKEDITOR.instances.editor | |
# Add .edits content to ckeditor, add event handler to update .edit | |
editor.setData(content) | |
editor.on 'change', -> | |
if ckeditor_change_callback editor | |
content_el.html editor.getData() | |
resizeBox($this) | |
layer.update $this.data('layer') | |
getValue : -> | |
$(this).children('.content').html() | |
layerTitle : -> | |
if($(@).text(buttons.text.getValue()).substring(0,20) != '') | |
$(@).text(buttons.text.getValue()).substring(0,20) + '...' | |
else | |
$(@).text(buttons.text.getValue()) | |
image : | |
html : '<div> | |
<a href="#null"/> | |
<strong>Doubleclick to upload image</strong> | |
</div>' | |
dblclick : -> | |
$.modal({ | |
ajax: '/editor/ajax/images/id/'+id, | |
title: 'Image Library', | |
overlayClose: true, | |
beforeLoad: -> | |
$.upload() | |
}) | |
$(@).children('strong').remove() | |
getValue : -> | |
$this = $(this) | |
value = $this.children('a') | |
randomcontainer = $('<div />') | |
value.clone().appendTo(randomcontainer) | |
value = randomcontainer.html() | |
layerTitle : -> | |
'Image' | |
box : | |
html: '<div style="border:3px solid #ccc;"> | |
</div>' | |
dblclick : -> | |
getValue : -> | |
layerTitle : -> | |
'Box' | |
media : | |
html : '<div> | |
<div class="content"> | |
Double click to edit me | |
</div> | |
<div class="content_cover"> | |
</div> | |
</div>' | |
dblclick : -> | |
# Do the same a text button (CKEDITOR) but make it a source only ckeditor | |
self = @ | |
args = ['Source', (editor) -> | |
editor = CKEDITOR.instances.editor | |
$self = $ self | |
content_el = $self.find '.content' | |
# Work with editor content | |
new_content = editor.getData() | |
# Try to force youtube to html5 mode | |
new_content = new_content.replace /(<iframe.*? src=")(.*?youtube.*?)?(".*?>)/gi, '$1$2?html5=1$3' | |
# Append html | |
content_el.html new_content | |
# .edit should resize to fit media | |
height = 0 | |
width = 0 | |
media = content_el.children(':visible') | |
media.each -> | |
$this = $(this) | |
this_height = $(this).outerHeight true | |
this_width = $(this).outerWidth true | |
height = this_height if this_height > height | |
width = this_width if this_width > width | |
$self.css(width:width,height:height) | |
false | |
] | |
buttons['text'].dblclick.apply @, args | |
getValue : -> | |
$(this).children('.content')?.html() | |
layerTitle : -> | |
'media' | |
form : # We extend buttons.media for other properties. Anything defined here takes precedence. | |
inherits : 'media' | |
layerTitle : -> | |
'form' | |
background : | |
html: '<div></div>' | |
dblclick: -> | |
getValue : -> | |
layerTitle : -> | |
'Background' | |
# Sort out button inheritance. Really should have used classes for this but fuck it :D | |
$.each(buttons, (idx, val)-> | |
if this.hasOwnProperty 'inherits' | |
inherits = this.inherits | |
buttons[idx] = $.extend true, {}, buttons[inherits], this | |
) | |
# Edit object containing functions that will run on .edits | |
edit = | |
select : (el, calledByLayer = false) -> | |
#select the matching layer | |
layer.select( $(el).data('layer'), true ) if not calledByLayer | |
# This should be the only edit with class .current and only draggable .edit | |
$('.edit.current').removeClass('current').draggable('disable').resizable('disable') | |
type = $(el).data('type') | |
$(el).addClass('current') | |
if type isnt 'background' # Background shouldn't be dragged/resized | |
$(el).draggable('enable').resizable('enable') | |
delete : (el) -> | |
el = $(el) | |
if el.length > 0 | |
return if el.data('type') is 'background' | |
#$.get('/ajax/globe/delete/tbl/e/id/' + el.data('id')) | |
$('#layers').data('deleted').push(el.data('id')) | |
el.data('layer').remove() | |
el.remove() | |
# Layer object containing functions to run on layers | |
layer = | |
select : (el, calledByEdit = false) -> | |
editEl = $(el).data 'el' | |
type = $(el).data 'type' | |
# Select matching .edit | |
edit.select(editEl, true) if not calledByEdit | |
# make this the selected layer | |
$('#layers .current').removeClass 'current' | |
$(el).addClass 'current' | |
# Display relevant properties panel | |
# also, populate it with the current properties | |
$('#properties input').each(-> | |
# Current value | |
val = @.value | |
# Special cases dependant on name | |
switch this.name | |
when "link" | |
val = $(editEl).children('a').attr 'href' | |
else #Css property | |
val = $(editEl).css $(this).attr('name') | |
if this.name.indexOf('color') > -1 | |
if val.length > 7 | |
val = rgb2hex(val) | |
$(this).val val | |
$(this).trigger('change') | |
) | |
$('#properties li').hide() | |
$('.properties_' + type).show() | |
# Updates the layer title | |
update : (el) -> | |
$this = $(el) | |
title = buttons[$this.data 'type'].layerTitle.apply $this.data 'el' | |
$this.children('a.title').html(title) | |
setImageAttr = (el) -> | |
$(el).attr('alt') | |
# Arrange layers by z-index (highest at top) | |
orderLayers = -> | |
layers = $('#layers') | |
$('li:not(:last)', layers).sortElements (a,b) -> | |
$(b).data('z-index') - $(a).data('z-index') | |
resizeBox = (el) -> | |
el.stop().animate( | |
height: el.children('.content').height() + "px" | |
) | |
### | |
Adds a layer to the layers panel and links the layer to the .edit and vice versa | |
@param htmlElement el - the .edit to link to the layer | |
### | |
addLayer = (el) -> | |
layers = $('#layers') | |
# Sort out z-index of edit | |
zIndex = parseInt( $(el).css('z-index') ,10) | |
zIndex = 0 if isNaN zIndex | |
# Add new layer | |
new_layer = $('<li />') | |
.data( | |
'el' : el | |
'type' : $(el).data 'type' | |
'z-index' : zIndex | |
) | |
.click -> | |
layer.select(this) | |
# This array contains the innerHTML of the layer | |
layerContents = [ | |
$('<a class="title" href="#">Layer</a>'), | |
$('<a class="delete_edit" href="#">x</a>').click -> | |
edit.delete el | |
] | |
$.each layerContents, -> | |
new_layer.append this | |
# Brand New layer (rather than loaded layer) should be highest | |
if zIndex is 0 | |
highest_zindex = layers.children().eq(0).css('z-index') | |
new_layer.data 'z-index', highest_zindex + 1 | |
new_layer.css 'z-index', new_layer.data 'z-index' | |
layers.prepend new_layer | |
# Link this layer to the .edit | |
$(el).data( | |
'layer' : new_layer | |
) | |
layer.update new_layer | |
orderLayers() | |
window.observeEdits = -> | |
$('.edit:not(.ui-draggable)') | |
.append('<div class="gridline gridline_x"></div><div class="gridline gridline_y"></div>') | |
.mouseup -> | |
edit.select @ | |
.dblclick -> | |
# get edit type (eg. text / img etc) and match against buttons to find dblclick function | |
buttons[$(@).data 'type'].dblclick.apply @ | |
.draggable( | |
disabled : true | |
grid : grid | |
#containment : container | |
# functions to run during drag events | |
start : (evt) -> | |
# show gridlines | |
$(this).children('.gridline').show() | |
drag : (evt, ui) -> | |
# Force draggable to stay on grid | |
ui.position.left -= ui.position.left % grid[0] | |
ui.position.top -= ui.position.top % grid[1] | |
# Make sure draggable doesn't completely leave container | |
[left,top,the_edit] = [ui.position.left, ui.position.top, ui.helper] | |
min_left = 0 - the_edit.width() | |
min_top = 0 - the_edit.height() | |
max_left = $(container).width() | |
max_top = $(container).height() | |
ui.position.left = min_left + 10 if left <= min_left | |
ui.position.top = min_top + 10 if top <= min_top | |
ui.position.left = max_left - 10 if left >= max_left | |
ui.position.top = max_top - 10 if top >= max_top | |
stop : (evt) -> | |
# hide gridlines | |
$(this).children('.gridline').hide() | |
) | |
.resizable( | |
disabled : true | |
grid : grid | |
handles : 'n, e, s, w, ne, se, sw, nw ' | |
resize : (evt,ui) -> | |
# Force resize to stay on grid | |
if $(ui.element).hasClass('edit_text') | |
ui.size.width -= ui.size.width % grid[0] | |
else | |
$(this).find('img').css('height', ui.size.height + 'px').css('width', ui.size.width + 'px') | |
stop: (event, ui) -> | |
if $(ui.element).hasClass 'edit_text' | |
resizeBox $(this) | |
) | |
.each -> | |
console.log this | |
type = @className.match(/edit_([^\s]*)/)[1] | |
$(@).data('type', type) | |
if type == 'image' | |
$(this).resizable( "option", "grid", [0,0] ) | |
addLayer @ | |
# Make sure there are no unused layers kicking around. | |
$('#layers li').each(-> | |
# Is the linked edit in the DOM | |
el = $(this).data 'el' | |
html = document.body.parentNode | |
while (el) | |
if el is html | |
return true | |
el = el.parentNode | |
$(this).remove() | |
) | |
class Clipboard | |
contents = null | |
copy : (el, oncomplete) -> | |
if not oncomplete then oncomplete = -> | |
$el = $(el) | |
new_el = $el.clone() | |
new_el.removeClass('ui-draggable') | |
contents = new_el | |
oncomplete() | |
cut : (el) -> | |
@copy el, -> | |
edit.delete(el) | |
paste : -> | |
$(container).append contents.clone() | |
observeEdits() | |
window.clipboard = new Clipboard | |
class Undo | |
undoStack = [] | |
pushState : -> | |
state = $('#content').children().eq(0).clone(true,true) | |
undoStack.push state | |
popState : -> | |
if undoStack.length > 0 | |
$('#content').children().eq(0).remove() | |
$('#content').prepend(undoStack.pop()) | |
observeEdits() | |
window.undo = new Undo | |
$ -> | |
# Events for copy and paste. | |
document.oncopy = -> | |
clipboard.copy $('.edit.current') | |
document.oncut = -> | |
clipboard.cut $('.edit.current') | |
document.onpaste = -> | |
clipboard.paste() | |
# Right click menu - we are using a modified version of context menu seen here: | |
# http://stackoverflow.com/questions/6778258/jquery-contextmenu-live | |
# it works using jquery .live so will apply to all elements that match selector, | |
# now and in the future. | |
menu = $('#rightclickmenu') | |
# Disable some options for background | |
###$('.edit').live 'mousedown', -> menu.enableContextMenuItems() | |
$('.edit.edit_background').live 'mousedown', -> | |
menu.disableContextMenuItems() | |
allowed_for_background = "#paste" | |
menu.enableContextMenuItems allowed_for_background | |
### | |
# Set up menu | |
### | |
$('.edit').contextMenu { menu : 'rightclickmenu' }, (action,el,pos) -> | |
$el = $(el) | |
switch action | |
when "edit" | |
buttons[$el.data 'type'].dblclick.apply el | |
when "copy" | |
clipboard.copy $el | |
when "cut" | |
clipboard.cut $el | |
when "paste" | |
clipboard.paste() | |
when "delete" | |
edit.delete $el | |
### | |
# Set up context menu | |
when_background = (command, opt) -> | |
$trigger = opt.$trigger | |
console.log($trigger.hasClass('edit_background')) | |
$trigger.hasClass 'edit_background' and command isnt 'paste' | |
$.contextMenu | |
selector : '.edit' | |
items : | |
cut : | |
name : 'cut' | |
disabled : when_background | |
copy : | |
name : 'copy' | |
disabled: when_background | |
# Initialize stuff | |
$('#layers').data 'deleted',[] | |
if vid is 0 | |
variation(0,true) | |
if $('.edit_background').length is 0 | |
do createBackground = -> | |
edit_background = $('<div class="edit edit_background"/>').css( | |
left: 0 | |
top: 0 | |
width: '100%' | |
height: '100%' | |
).appendTo('#container') | |
# Click on background layer | |
$('.edit_background').trigger 'click' | |
observeEdits() | |
# key shortcuts | |
$(window).keyup (event) -> | |
if event.keyCode is 46 # Delete key - delete current .edit | |
edit.delete $('.edit.current') | |
# Button actions | |
$('.save').click -> | |
$.loading(true,'Saving.. items') | |
savePage(false) | |
false | |
$('.newVar').click -> | |
$.loading(true,'Saving..') | |
variation(0,true) | |
false | |
$('.finishBox a').click -> | |
$.loading(true,'Saving..') | |
savePage(true) | |
false | |
# Changes a property on the current .edit | |
window.changeProperty = (prop, val) -> | |
# Not entiiirreeely sure why this isn't .edit.current | |
el = $('#layers .current').data('el') | |
# Special cases | |
switch prop | |
when "link" | |
$(el).children('a').attr 'href', val | |
else | |
$(el).css prop,val | |
# Observe properties inputs to change css props on current selected .edit | |
$('#properties input') | |
.change -> | |
# Get property and value | |
prop = @.name | |
val = @.value | |
changeProperty prop, val | |
.each -> | |
self = @ | |
# make it a colorpicker | |
if this.name.indexOf('color') > -1 | |
val = @.value | |
$(this).ColorPicker( | |
onBeforeShow : -> | |
$(@).ColorPickerSetColor(val) | |
onChange : (hsb,hex,rgb) -> | |
$(self).val("#" + hex).trigger 'change' | |
# Special case for background, invert outline of edits | |
if self.id is 'main-background-color' | |
$('.edit').css( | |
'outline-color', invert(rgb) | |
) | |
) | |
$(@).val rgb2hex(val) | |
if $(this).hasClass 'slider' | |
$(this).slider() | |
# Toolbox items can be dragged onto the container to create new | |
# text fields/ images / etc... | |
$('#toolbox a') | |
.draggable( | |
helper : 'clone' | |
) | |
# We will clone the 'helper' when we finish dragging | |
.bind 'dragstop', (event,ui) -> | |
# Find the position relative to the container | |
{left,top} = ui.position | |
left -= $(container).position().left | |
top -= $(container).position().top | |
# Find the button in the button object that matches this button | |
button_class = @className.match(/button_([^\s]*)/)[1] | |
button = buttons[button_class] | |
$(button.html) | |
.addClass('edit') | |
.addClass("edit_#{button_class}") | |
.appendTo(container) | |
.css | |
left : left | |
top : top | |
# Call observeEdits to make this act as one of the .edits | |
observeEdits() | |
# Make layers draggable and auto become draggable. | |
$('#layers').sortable( | |
axis : 'y' | |
# Background shouldn't be sortable | |
items : '> li:not(:last)' | |
update : (evt, ui) -> | |
# Order the edits by z-index consecutively. | |
layers = $('#layers > li') | |
layers.reverse() | |
layers.each (idx) -> | |
zidx = idx + 1 | |
$(this).data 'z-index', zidx | |
$($(this).data 'el').css 'z-index', zidx | |
layer.update @ | |
) | |
# Stop .edit inner <a>s from leaving the page. | |
$('.edit a').live('click', -> false) | |
changePreviewLink = (v) -> | |
$('.previewBox a').attr('href', path + '/' + id + '&vid=' + v + '&mode=preview') | |
# Create a new variation | |
variation = (v, hide) -> | |
editData = { | |
cid: id | |
vid: v | |
# Position data | |
width : $(container).width() | |
height : $(container).height() | |
bgcolor: $(container).css('background-color') | |
} | |
$.post( | |
'/editor/ajax/variation/' | |
editData | |
(data) -> | |
if hide | |
$.loading(false,'') | |
window.vid = data | |
if v is 0 | |
$('.variationBox ul').find('.current').removeClass('current') | |
$('.variationBox ul').append('<li><p class="current"><a href="/editor/def/edit/id/'+id+'/vid/'+data+'">'+data+'</a><a href="/ajax/globe/delete/tbl/v/id/'+data+'" class="delete" data-remove="p" data-item="v" data-action="delete"><img src="/front/images/editor/x.png" alt="Delete"></a></p></li>') | |
changePreviewLink(data) | |
'text') | |
# Add background layer if new variation | |
observeEdits() | |
# Save action | |
window.savePage = (redirect) -> | |
orderLayers() | |
variation(vid,false) | |
num = $('.edit').length | |
count = 0 | |
# Delete deleted layers | |
$.post( | |
'/ajax/globe/delete/tbl/e/id/1/' | |
deleted : $('#layers').data('deleted') | |
) | |
# edits = [] | |
# Save each edit | |
$('.edit').each -> | |
$this = $(@) | |
pos = $this.position() | |
value = buttons[$this.data 'type'].getValue.apply @ | |
editData = { | |
type: $this.data('type') | |
zindex: $this.attr('z-index') | |
id : $this.data('id') | |
vid : window.vid | |
# Position data | |
xPos : pos.left | |
yPos : pos.top | |
width : $this.width() | |
height : $this.height() | |
html : value | |
css : $this.attr('style') | |
} | |
$.post( | |
'/editor/ajax/save/' | |
editData | |
(data) -> | |
$this.data('id', data) | |
count++ | |
if count is num | |
$.loading(false,'') | |
if redirect | |
window.location = '/campaign/def/details/id/'+id | |
'text') | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment