first draft of the new inline editing toolbar
4
Gemfile
@ -13,8 +13,8 @@ gem 'mongo', '~> 1.5.2'
|
|||||||
gem 'bson_ext', '~> 1.5.2'
|
gem 'bson_ext', '~> 1.5.2'
|
||||||
gem 'mongoid', '~> 2.4.0'
|
gem 'mongoid', '~> 2.4.0'
|
||||||
gem 'locomotive_mongoid_acts_as_tree', :git => 'git@github.com:locomotivecms/mongoid_acts_as_tree.git'
|
gem 'locomotive_mongoid_acts_as_tree', :git => 'git@github.com:locomotivecms/mongoid_acts_as_tree.git'
|
||||||
# gem 'custom_fields', :path => '../gems/custom_fields' # DEV
|
gem 'custom_fields', :path => '../gems/custom_fields' # DEV
|
||||||
gem 'custom_fields', :git => 'git://github.com/locomotivecms/custom_fields.git', :branch => 'experimental'
|
# gem 'custom_fields', :git => 'git://github.com/locomotivecms/custom_fields.git', :branch => 'experimental'
|
||||||
gem 'kaminari'
|
gem 'kaminari'
|
||||||
|
|
||||||
gem 'haml', '~> 3.1.3'
|
gem 'haml', '~> 3.1.3'
|
||||||
|
20
app/assets/javascripts/locomotive/inline_editor.js.coffee
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#= require jquery
|
||||||
|
#= require jquery-ui
|
||||||
|
#= require jquery_ujs
|
||||||
|
#= require underscore
|
||||||
|
#= require backbone
|
||||||
|
#= require locomotive/growl
|
||||||
|
#= require locomotive/handlebars
|
||||||
|
#= require locomotive/ICanHandlebarz
|
||||||
|
#= require locomotive/resize
|
||||||
|
#= require locomotive/toggle
|
||||||
|
#= require_self
|
||||||
|
#= require_tree ./utils
|
||||||
|
#= require_tree ./models
|
||||||
|
#= require_tree ./views/inline_editor
|
||||||
|
|
||||||
|
window.Locomotive =
|
||||||
|
mounted_on: '/locomotive' # default path
|
||||||
|
Models: {}
|
||||||
|
Collections: {}
|
||||||
|
Views: {}
|
@ -43,5 +43,25 @@
|
|||||||
//this will reload the page, it's likely better to store this until finished
|
//this will reload the page, it's likely better to store this until finished
|
||||||
document.location.search = kvp.join('&');
|
document.location.search = kvp.join('&');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.addJavascript = function(doc, src, options) {
|
||||||
|
var script = doc.createElement('script');
|
||||||
|
script.type = 'text/javascript';
|
||||||
|
script.src = src;
|
||||||
|
for (var key in options) {
|
||||||
|
script.setAttribute(key, options[key]);
|
||||||
|
}
|
||||||
|
doc.body.appendChild(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addStylesheet = function(doc, src, options) {
|
||||||
|
var stylesheet = doc.createElement('link');
|
||||||
|
stylesheet.style = 'text/css';
|
||||||
|
stylesheet.href = src;
|
||||||
|
stylesheet.media = 'screen';
|
||||||
|
stylesheet.rel = 'stylesheet';
|
||||||
|
doc.head.appendChild(stylesheet);
|
||||||
|
}
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -0,0 +1,133 @@
|
|||||||
|
Locomotive.Views.InlinEditor ||= {}
|
||||||
|
|
||||||
|
#= require ./toolbar_view
|
||||||
|
|
||||||
|
class Locomotive.Views.InlinEditor.ApplicationView extends Backbone.View
|
||||||
|
|
||||||
|
el: 'body'
|
||||||
|
|
||||||
|
initialize: ->
|
||||||
|
super
|
||||||
|
|
||||||
|
@iframe = @$('#page iframe')
|
||||||
|
|
||||||
|
@toolbar_view = new Locomotive.Views.InlinEditor.ToolbarView(target: @iframe)
|
||||||
|
|
||||||
|
render: ->
|
||||||
|
super
|
||||||
|
|
||||||
|
@decorate_iframe()
|
||||||
|
|
||||||
|
set_page: (attributes) ->
|
||||||
|
@page = new Locomotive.Models.Page(attributes)
|
||||||
|
window.foo = @page
|
||||||
|
|
||||||
|
@toolbar_view.model = @page
|
||||||
|
|
||||||
|
@$('#toolbar .inner').html(@toolbar_view.refresh().render().el)
|
||||||
|
|
||||||
|
decorate_iframe: ->
|
||||||
|
console.log('decorating iframe')
|
||||||
|
|
||||||
|
iframe = @iframe
|
||||||
|
iframe.load =>
|
||||||
|
# add js / css
|
||||||
|
doc = iframe[0].contentWindow.document
|
||||||
|
window.addJavascript doc, '/assets/locomotive/aloha/lib/aloha.js',
|
||||||
|
'data-aloha-plugins': 'common/format,common/highlighteditables,common/list,common/link,common/undo,common/paste'
|
||||||
|
window.addStylesheet doc, '/assets/locomotive/aloha/css/aloha.css'
|
||||||
|
|
||||||
|
# bind the resize event. When the iFrame's size changes, update its height
|
||||||
|
iframe_content = iframe.contents().find('body')
|
||||||
|
iframe_content.resize ->
|
||||||
|
elem = $(this)
|
||||||
|
|
||||||
|
if elem.outerHeight(true) > $('body').outerHeight(true) # Resize the iFrame.
|
||||||
|
iframe.css height: elem.outerHeight(true)
|
||||||
|
|
||||||
|
# Resize the iFrame immediately.
|
||||||
|
iframe_content.resize()
|
||||||
|
|
||||||
|
# render: ->
|
||||||
|
# super
|
||||||
|
#
|
||||||
|
# console.log('rendering')
|
||||||
|
#
|
||||||
|
# @enable_iframe_auto_height()
|
||||||
|
#
|
||||||
|
# @foo()
|
||||||
|
#
|
||||||
|
# @toolbar_view = new Locomotive.Views.
|
||||||
|
#
|
||||||
|
# set_page: (attributes) ->
|
||||||
|
# @page = new Locomotive.Models.Page(attributes)
|
||||||
|
#
|
||||||
|
# window.foo = @page
|
||||||
|
#
|
||||||
|
# @$('#toolbar .inner').html(ich.toolbar(@page.toJSON()))
|
||||||
|
#
|
||||||
|
# enable_iframe_auto_height: ->
|
||||||
|
# console.log('decorating iframe')
|
||||||
|
#
|
||||||
|
# console.log(@$('#page iframe html'))
|
||||||
|
#
|
||||||
|
# # @$('#page iframe').iframeAutoHeight
|
||||||
|
# # debug: true
|
||||||
|
# # callback: (callbackObject) =>
|
||||||
|
# # $('body').css('overflow': 'visible')
|
||||||
|
#
|
||||||
|
# iframe = @$('#page iframe')
|
||||||
|
# iframe.load =>
|
||||||
|
# iframe_content = iframe.contents().find('body')
|
||||||
|
#
|
||||||
|
# # Bind the resize event. When the iframe's size changes, update its height as
|
||||||
|
# # well as the corresponding info div.
|
||||||
|
# iframe_content.resize ->
|
||||||
|
# elem = $(this)
|
||||||
|
#
|
||||||
|
# # Resize the IFrame.
|
||||||
|
# if elem.outerHeight(true) > $('body').outerHeight(true)
|
||||||
|
# iframe.css height: elem.outerHeight(true)
|
||||||
|
#
|
||||||
|
# # Update the info div width and height.
|
||||||
|
# # $('#iframe-info').text( 'IFRAME width: ' + elem.width() + ', height: ' + elem.height() );
|
||||||
|
#
|
||||||
|
# # Resize the Iframe and update the info div immediately.
|
||||||
|
# iframe_content.resize()
|
||||||
|
#
|
||||||
|
# foo: ->
|
||||||
|
# @$('#page iframe').load =>
|
||||||
|
# # console.log 'iframe loaded'
|
||||||
|
# doc = @$('#page iframe')[0].contentWindow.document
|
||||||
|
#
|
||||||
|
# script = doc.createElement('script')
|
||||||
|
# script.type = 'text/javascript'
|
||||||
|
# script.src = '/assets/locomotive/aloha/lib/aloha.js'
|
||||||
|
# script.setAttribute('data-aloha-plugins', 'common/format,common/highlighteditables,common/list,common/link,common/undo,common/paste')
|
||||||
|
# doc.body.appendChild(script)
|
||||||
|
#
|
||||||
|
# stylesheet = doc.createElement('link')
|
||||||
|
# stylesheet.style = 'text/css'
|
||||||
|
# stylesheet.href = '/assets/locomotive/aloha/css/aloha.css'
|
||||||
|
# stylesheet.media = 'screen'
|
||||||
|
# stylesheet.rel = 'stylesheet'
|
||||||
|
# doc.head.appendChild(stylesheet)
|
||||||
|
#
|
||||||
|
# # <link href="/assets/locomotive/aloha/css/aloha.css" media="screen" rel="stylesheet" type="text/css" />
|
||||||
|
# # {{ '/assets/locomotive/aloha/css/aloha.css' | stylesheet_tag }}
|
||||||
|
#
|
||||||
|
# # $("body", doc).append(script)
|
||||||
|
#
|
||||||
|
# # $('body', doc).append('<script src="assets/locomotive/aloha/lib/aloha.js" data-aloha-plugins="common/format,common/highlighteditables,common/list,common/link,common/undo,common/paste,common/block"></script>')
|
||||||
|
#
|
||||||
|
# # <script src="/assets/locomotive/aloha/lib/aloha.js" data-aloha-plugins="common/format,common/highlighteditables,common/list,common/link,common/undo,common/paste,common/block"></script>
|
||||||
|
# # $('body', doc).append('<p>Hello world</p>');
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# # register_page_content: (iframe) ->
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# # console.log 'he he'
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
@ -0,0 +1,43 @@
|
|||||||
|
Locomotive.Views.InlinEditor ||= {}
|
||||||
|
|
||||||
|
class Locomotive.Views.InlinEditor.ToolbarView extends Backbone.View
|
||||||
|
|
||||||
|
tagName: 'div'
|
||||||
|
|
||||||
|
className: 'toolbar-view'
|
||||||
|
|
||||||
|
events:
|
||||||
|
'change .edit input[type=checkbox]': 'toggle_inline_editing'
|
||||||
|
|
||||||
|
initialize: ->
|
||||||
|
super
|
||||||
|
|
||||||
|
render: ->
|
||||||
|
super
|
||||||
|
$(@el).html(ich.toolbar(@model.toJSON()))
|
||||||
|
|
||||||
|
@enable_edit_checkbox()
|
||||||
|
|
||||||
|
@
|
||||||
|
|
||||||
|
toggle_inline_editing: (event) ->
|
||||||
|
console.log('toggle_inline_editing !!!')
|
||||||
|
if $(event.target).is(':checked')
|
||||||
|
@editable_elements().aloha()
|
||||||
|
else
|
||||||
|
@editable_elements().removeClass('aloha-editable-highlight').mahalo()
|
||||||
|
|
||||||
|
editable_elements: ->
|
||||||
|
@options.target[0].contentWindow.Aloha.jQuery('.editable-long-text, .editable-short-text')
|
||||||
|
|
||||||
|
enable_edit_checkbox: ->
|
||||||
|
@$('.edit input[type=checkbox]').checkToggle
|
||||||
|
on_label_color: '#fff'
|
||||||
|
off_label_color: '#bbb'
|
||||||
|
|
||||||
|
refresh: ->
|
||||||
|
console.log('refreshing...')
|
||||||
|
@
|
||||||
|
|
||||||
|
remove: ->
|
||||||
|
super
|
@ -16,6 +16,8 @@ class Locomotive.Views.Pages.FormView extends Locomotive.Views.Shared.FormView
|
|||||||
|
|
||||||
@model = new Locomotive.Models.Page(@options.page)
|
@model = new Locomotive.Models.Page(@options.page)
|
||||||
|
|
||||||
|
window.foo = @model
|
||||||
|
|
||||||
@touched_url = false
|
@touched_url = false
|
||||||
|
|
||||||
@image_picker_view = new Locomotive.Views.ThemeAssets.ImagePickerView
|
@image_picker_view = new Locomotive.Views.ThemeAssets.ImagePickerView
|
||||||
|
@ -10,5 +10,5 @@
|
|||||||
*= require codemirror/themes/default
|
*= require codemirror/themes/default
|
||||||
*= require locomotive/toggle.css
|
*= require locomotive/toggle.css
|
||||||
*= require locomotive/liquid_mode.css
|
*= require locomotive/liquid_mode.css
|
||||||
*= require_tree ./locomotive
|
*= require_tree ./locomotive/backoffice
|
||||||
*/
|
*/
|
||||||
|
8
app/assets/stylesheets/locomotive/inline_editor.css
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* This is a manifest file that'll automatically include all the stylesheets available in this directory
|
||||||
|
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
|
||||||
|
* the top of the compiled file, but it's generally better to create a new file per style scope.
|
||||||
|
*= require locomotive/blueprint/screen.css
|
||||||
|
*= require locomotive/toggle.css
|
||||||
|
*= require_tree ./inline_editor
|
||||||
|
*/
|
@ -0,0 +1,16 @@
|
|||||||
|
body {
|
||||||
|
position: relative;
|
||||||
|
background: transparent;
|
||||||
|
// overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#page {
|
||||||
|
position: relative;
|
||||||
|
padding-top: 50px;
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
@import "compass/css3/images";
|
||||||
|
@import "compass/css3/opacity";
|
||||||
|
@import "compass/css3/box-shadow";
|
||||||
|
@import "compass/css3/text-shadow";
|
||||||
|
|
||||||
|
#toolbar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
|
||||||
|
height: 50px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.background {
|
||||||
|
z-index: 100;
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
@include background-image(linear-gradient(#303138, #23242B));
|
||||||
|
@include box-shadow(rgba(0, 0, 0, 0.6) 0px -1px 10px 0px, rgba(0, 0, 0, 0.2) 0px 4px 6px 4px, rgba(255, 255, 255, 0.4) 0 1px 0 0 inset);
|
||||||
|
@include opacity(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 200;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
float: left;
|
||||||
|
margin: 0 0 0 10px;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
top: -2px;
|
||||||
|
|
||||||
|
width: 200px;
|
||||||
|
|
||||||
|
line-height: 50px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
@include single-text-shadow(#000, 0px, 1px, 0px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit {
|
||||||
|
float: right;
|
||||||
|
margin-right: 10px;
|
||||||
|
line-height: 50px;
|
||||||
|
|
||||||
|
.toggleSwitch {
|
||||||
|
position: relative;
|
||||||
|
top: 0px;
|
||||||
|
margin-left: 3px;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
div.switchArea {
|
||||||
|
@include box-shadow(rgba(0, 0, 0, 0.8) 0px 1px 0px 0px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
module Locomotive
|
module Locomotive
|
||||||
module Public
|
module Public
|
||||||
class RenderingController < ApplicationController
|
class PagesController < ApplicationController
|
||||||
|
|
||||||
include Locomotive::Routing::SiteDispatcher
|
include Locomotive::Routing::SiteDispatcher
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ module Locomotive
|
|||||||
|
|
||||||
before_filter :require_site
|
before_filter :require_site
|
||||||
|
|
||||||
before_filter :authenticate_admin!, :only => [:edit]
|
before_filter :authenticate_locomotive_account!, :only => [:edit]
|
||||||
|
|
||||||
before_filter :validate_site_membership, :only => [:edit]
|
before_filter :validate_site_membership, :only => [:edit]
|
||||||
|
|
||||||
@ -17,8 +17,12 @@ module Locomotive
|
|||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
@editing = true
|
if params[:noiframe]
|
||||||
render_locomotive_page
|
@editing = true
|
||||||
|
render_locomotive_page
|
||||||
|
else
|
||||||
|
render :layout => false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
34
app/middlewares/locomotive/inline_editor_middleware.rb
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
module Locomotive
|
||||||
|
class InlineEditorMiddleware
|
||||||
|
|
||||||
|
def initialize(app, opts = {})
|
||||||
|
@app = app
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
response = @app.call(env)
|
||||||
|
|
||||||
|
# if env['warden'].authenticated?(:locomotive_account) &&
|
||||||
|
# !env['PATH_INFO'].starts_with?("/assets") &&
|
||||||
|
# !env['PATH_INFO'].starts_with?("/#{Locomotive.mounted_on}/") &&
|
||||||
|
# !env['PATH_INFO'].ends_with?("/edit")
|
||||||
|
|
||||||
|
Rails.logger.debug "headers = #{response[1].inspect}"
|
||||||
|
|
||||||
|
unless response[1]['Editable'].blank?
|
||||||
|
Rails.logger.debug "==> #{ENV['PATH_INFO'].inspect}, warden ? #{env['warden'].inspect} /#{env['warden'].user} / #{env['warden'].authenticated?(:locomotive_account)}"
|
||||||
|
|
||||||
|
html = response.last.body.to_s.gsub '</body>', %(
|
||||||
|
<a href="_edit">Edit</a>
|
||||||
|
</body>
|
||||||
|
)
|
||||||
|
|
||||||
|
[response[0], response[1], [html]]
|
||||||
|
else
|
||||||
|
response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
@ -1,7 +1,7 @@
|
|||||||
module Locomotive
|
module Locomotive
|
||||||
class PagePresenter < BasePresenter
|
class PagePresenter < BasePresenter
|
||||||
|
|
||||||
delegate :title, :slug, :fullpath, :raw_template, :published, :template_changed, :cache_strategy, :to => :source
|
delegate :title, :slug, :fullpath, :raw_template, :published, :listed, :templatized, :redirect, :redirect_url, :template_changed, :cache_strategy, :to => :source
|
||||||
|
|
||||||
def escaped_raw_template
|
def escaped_raw_template
|
||||||
h(self.source.raw_template)
|
h(self.source.raw_template)
|
||||||
@ -12,7 +12,7 @@ module Locomotive
|
|||||||
end
|
end
|
||||||
|
|
||||||
def included_methods
|
def included_methods
|
||||||
super + %w(title slug fullpath raw_template published published cache_strategy template_changed editable_elements)
|
super + %w(title slug fullpath raw_template published listed templatized redirect redirect_url cache_strategy template_changed editable_elements)
|
||||||
end
|
end
|
||||||
|
|
||||||
def as_json_for_html_view
|
def as_json_for_html_view
|
||||||
|
50
app/views/locomotive/public/pages/edit.html.haml
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
!!! XML
|
||||||
|
!!!
|
||||||
|
%html{ :xmlns => 'http://www.w3.org/1999/xhtml' }
|
||||||
|
%head
|
||||||
|
%title Locomotive
|
||||||
|
|
||||||
|
= csrf_meta_tag
|
||||||
|
|
||||||
|
- key = Rails.application.config.session_options[:key]
|
||||||
|
|
||||||
|
%meta{ :name => 'key-param', :content => Rails.application.config.session_options[:key] }
|
||||||
|
%meta{ :name => 'key-token', :content => cookies[key] }
|
||||||
|
|
||||||
|
= stylesheet_link_tag 'locomotive/inline_editor', :media => 'screen'
|
||||||
|
= javascript_include_tag 'locomotive/inline_editor'
|
||||||
|
|
||||||
|
%script{ :type => 'text/javascript' }
|
||||||
|
:plain
|
||||||
|
window.locale = '#{I18n.locale}';
|
||||||
|
|
||||||
|
Locomotive.mounted_on = '#{Locomotive.mounted_on}';
|
||||||
|
Locomotive.current_site = new Locomotive.Models.Site(#{current_site.to_json});
|
||||||
|
Locomotive.current_account = new Locomotive.Models.Account(#{current_locomotive_account.to_json});
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
|
||||||
|
window.application_view = new Locomotive.Views.InlinEditor.ApplicationView({
|
||||||
|
flash: #{flash.to_json}
|
||||||
|
});
|
||||||
|
window.application_view.render();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
%script{ :type => 'text/html', :id => 'toolbar' }
|
||||||
|
|
||||||
|
%h1 {{title}}
|
||||||
|
|
||||||
|
.edit
|
||||||
|
%label Editing mode:
|
||||||
|
= check_box_tag 'edit', '1', false
|
||||||
|
|
||||||
|
%body
|
||||||
|
#page
|
||||||
|
%iframe{ :src => %(#{request.fullpath}?noiframe=true') , :scrolling => 'no', :frameborder => '0' }
|
||||||
|
|
||||||
|
#toolbar
|
||||||
|
.background
|
||||||
|
.inner
|
||||||
|
|
||||||
|
|
@ -65,7 +65,10 @@ Rails.application.routes.draw do
|
|||||||
resources :locomotive_entry_submissions, :controller => 'locomotive/public/content_entries', :path => 'entry_submissions/:slug'
|
resources :locomotive_entry_submissions, :controller => 'locomotive/public/content_entries', :path => 'entry_submissions/:slug'
|
||||||
|
|
||||||
# magic urls
|
# magic urls
|
||||||
match '/' => 'locomotive/public/rendering#show'
|
# match '/editable' => 'locomotive/public/rendering#edit', :path => '/'
|
||||||
match '*path/edit' => 'locomotive/public/rendering#edit'
|
match '/' => 'locomotive/public/pages#show'
|
||||||
match '*path' => 'locomotive/public/rendering#show'
|
match '/edit' => 'locomotive/public/pages#edit'
|
||||||
|
match '*path/edit' => 'locomotive/public/pages#edit'
|
||||||
|
# match '_/*path' => 'locomotive/public/rendering#show'
|
||||||
|
match '*path' => 'locomotive/public/pages#show'
|
||||||
end
|
end
|
@ -4,45 +4,55 @@ module Liquid
|
|||||||
class InlineEditor < ::Liquid::Tag
|
class InlineEditor < ::Liquid::Tag
|
||||||
|
|
||||||
def render(context)
|
def render(context)
|
||||||
if context.registers[:current_locomotive_account]
|
if context.registers[:current_locomotive_account] && context.registers[:inline_editor]
|
||||||
output = %{
|
%{
|
||||||
<meta name="locale" content="#{context.registers[:current_locomotive_account].locale}" />
|
<script type="text/javascript">
|
||||||
<meta name="page-fullpath" content="/#{context.registers[:page].fullpath}" />
|
window.parent.application_view.set_page(#{context.registers[:page].to_json});
|
||||||
<meta name="edit-page-url" content="#{context.registers[:controller].send(:edit_page_url, context.registers[:page])}" />
|
</script>
|
||||||
}
|
|
||||||
|
|
||||||
if context.registers[:inline_editor]
|
|
||||||
controller = context.registers[:controller]
|
|
||||||
|
|
||||||
output << %{
|
|
||||||
<meta name="page-url" content="#{context.registers[:controller].send(:page_url, context.registers[:page], :json)}" />
|
|
||||||
<meta name="page-elements-count" content="#{context.registers[:page].editable_elements.size}" />
|
|
||||||
|
|
||||||
<script type="text/javascript" src="/javascripts/admin/aloha/aloha.js"></script>
|
|
||||||
<script type="text/javascript" src="/javascripts/admin/aloha/plugins/com.gentics.aloha.plugins.Format/plugin.js"></script>
|
|
||||||
<script type="text/javascript" src="/javascripts/admin/aloha/plugins/com.gentics.aloha.plugins.Table/plugin.js"></script>
|
|
||||||
<script type="text/javascript" src="/javascripts/admin/aloha/plugins/com.gentics.aloha.plugins.List/plugin.js"></script>
|
|
||||||
<script type="text/javascript" src="/javascripts/admin/aloha/plugins/com.gentics.aloha.plugins.Link/plugin.js"></script>
|
|
||||||
<script type="text/javascript" src="/javascripts/admin/aloha/plugins/com.gentics.aloha.plugins.HighlightEditables/plugin.js"></script>
|
|
||||||
}
|
|
||||||
|
|
||||||
if controller.send(:protect_against_forgery?)
|
|
||||||
output << %(<meta name="csrf-param" content="#{Rack::Utils.escape_html(controller.send(:request_forgery_protection_token))}"/>\n<meta name="csrf-token" content="#{Rack::Utils.escape_html(controller.send(:form_authenticity_token))}"/>).html_safe
|
|
||||||
end
|
|
||||||
else
|
|
||||||
output << %{
|
|
||||||
<script src="/javascripts/admin/jquery.js" type="text/javascript"></script>
|
|
||||||
<script src="/javascripts/admin/plugins/cookie.js" type="text/javascript"></script>
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
output << %{
|
|
||||||
<script src="/javascripts/admin/rails.js" type="text/javascript"></script>
|
|
||||||
<script type="text/javascript" src="/javascripts/admin/inline_editor_toolbar.js"></script>
|
|
||||||
<script type="text/javascript" src="/javascripts/admin/inline_editor.js"></script>
|
|
||||||
<link href="/stylesheets/admin/inline_editor.css" media="screen" rel="stylesheet" type="text/css" />
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
''
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# if context.registers[:current_locomotive_account]
|
||||||
|
# output = %{
|
||||||
|
# <meta name="locale" content="#{context.registers[:current_locomotive_account].locale}" />
|
||||||
|
# <meta name="page-fullpath" content="/#{context.registers[:page].fullpath}" />
|
||||||
|
# <meta name="edit-page-url" content="#{context.registers[:controller].send(:edit_page_url, context.registers[:page])}" />
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# if context.registers[:inline_editor]
|
||||||
|
# controller = context.registers[:controller]
|
||||||
|
#
|
||||||
|
# output << %{
|
||||||
|
# <meta name="page-url" content="#{context.registers[:controller].send(:page_url, context.registers[:page], :json)}" />
|
||||||
|
# <meta name="page-elements-count" content="#{context.registers[:page].editable_elements.size}" />
|
||||||
|
#
|
||||||
|
# <script type="text/javascript" src="/javascripts/admin/aloha/aloha.js"></script>
|
||||||
|
# <script type="text/javascript" src="/javascripts/admin/aloha/plugins/com.gentics.aloha.plugins.Format/plugin.js"></script>
|
||||||
|
# <script type="text/javascript" src="/javascripts/admin/aloha/plugins/com.gentics.aloha.plugins.Table/plugin.js"></script>
|
||||||
|
# <script type="text/javascript" src="/javascripts/admin/aloha/plugins/com.gentics.aloha.plugins.List/plugin.js"></script>
|
||||||
|
# <script type="text/javascript" src="/javascripts/admin/aloha/plugins/com.gentics.aloha.plugins.Link/plugin.js"></script>
|
||||||
|
# <script type="text/javascript" src="/javascripts/admin/aloha/plugins/com.gentics.aloha.plugins.HighlightEditables/plugin.js"></script>
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# if controller.send(:protect_against_forgery?)
|
||||||
|
# output << %(<meta name="csrf-param" content="#{Rack::Utils.escape_html(controller.send(:request_forgery_protection_token))}"/>\n<meta name="csrf-token" content="#{Rack::Utils.escape_html(controller.send(:form_authenticity_token))}"/>).html_safe
|
||||||
|
# end
|
||||||
|
# else
|
||||||
|
# output << %{
|
||||||
|
# <script src="/javascripts/admin/jquery.js" type="text/javascript"></script>
|
||||||
|
# <script src="/javascripts/admin/plugins/cookie.js" type="text/javascript"></script>
|
||||||
|
# }
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# output << %{
|
||||||
|
# <script src="/javascripts/admin/rails.js" type="text/javascript"></script>
|
||||||
|
# <script type="text/javascript" src="/javascripts/admin/inline_editor_toolbar.js"></script>
|
||||||
|
# <script type="text/javascript" src="/javascripts/admin/inline_editor.js"></script>
|
||||||
|
# <link href="/stylesheets/admin/inline_editor.css" media="screen" rel="stylesheet" type="text/css" />
|
||||||
|
# }
|
||||||
|
# end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ module Locomotive
|
|||||||
path.gsub!(/\.[a-zA-Z][a-zA-Z0-9]{2,}$/, '') # remove the page extension
|
path.gsub!(/\.[a-zA-Z][a-zA-Z0-9]{2,}$/, '') # remove the page extension
|
||||||
path.gsub!(/^\//, '') # remove the leading slash
|
path.gsub!(/^\//, '') # remove the leading slash
|
||||||
|
|
||||||
path = 'index' if path.blank?
|
path = 'index' if path.blank? || path == 'edit'
|
||||||
|
|
||||||
if path != 'index'
|
if path != 'index'
|
||||||
dirname = File.dirname(path).gsub(/^\.$/, '') # also look for templatized page path
|
dirname = File.dirname(path).gsub(/^\.$/, '') # also look for templatized page path
|
||||||
@ -73,8 +73,6 @@ module Locomotive
|
|||||||
|
|
||||||
assigns.merge!(Locomotive.config.context_assign_extensions)
|
assigns.merge!(Locomotive.config.context_assign_extensions)
|
||||||
|
|
||||||
Rails.logger.debug flash.to_hash.stringify_keys.inspect
|
|
||||||
|
|
||||||
assigns.merge!(flash.to_hash.stringify_keys) # data from public submissions
|
assigns.merge!(flash.to_hash.stringify_keys) # data from public submissions
|
||||||
|
|
||||||
if @page.templatized? # add instance from content type
|
if @page.templatized? # add instance from content type
|
||||||
@ -96,7 +94,8 @@ module Locomotive
|
|||||||
def prepare_and_set_response(output)
|
def prepare_and_set_response(output)
|
||||||
flash.discard
|
flash.discard
|
||||||
|
|
||||||
response.headers['Content-Type'] = 'text/html; charset=utf-8'
|
response.headers['Content-Type'] = 'text/html; charset=utf-8'
|
||||||
|
response.headers['Editable'] = 'true' unless self.editing_page?
|
||||||
|
|
||||||
if @page.with_cache?
|
if @page.with_cache?
|
||||||
fresh_when :etag => @page, :last_modified => @page.updated_at.utc, :public => true
|
fresh_when :etag => @page, :last_modified => @page.updated_at.utc, :public => true
|
||||||
|
@ -43,6 +43,8 @@ module Dummy
|
|||||||
|
|
||||||
# Version of your assets, change this if you want to expire all your assets
|
# Version of your assets, change this if you want to expire all your assets
|
||||||
config.assets.version = '1.0'
|
config.assets.version = '1.0'
|
||||||
|
|
||||||
|
config.middleware.use 'Locomotive::InlineEditorMiddleware'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
44
vendor/assets/javascripts/locomotive/aloha/css/aloha-core.css
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
.aloha-editable {
|
||||||
|
min-height: 1.2em;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.aloha-editable ::selection,
|
||||||
|
.aloha-editable ::-moz-selection {
|
||||||
|
background: #80B5F2;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.aloha-editable-zerowidthfix {
|
||||||
|
padding: 0px 5px 0px 5px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.aloha-logo {
|
||||||
|
width:37px !important;
|
||||||
|
height:25px !important;
|
||||||
|
background-image: url('../img/gentics-logo.png') !important;
|
||||||
|
}
|
||||||
|
.aloha-maximize {
|
||||||
|
background-image: url('../img/gentics-logo.png') !important;
|
||||||
|
}
|
||||||
|
.aloha-fade-out {
|
||||||
|
background-image: url('../img/fade-out.png') !important;
|
||||||
|
width:20px !important;
|
||||||
|
height:20px !important;
|
||||||
|
}
|
||||||
|
.aloha-fade-in {
|
||||||
|
background-image: url('../img/fade-in.png') !important;
|
||||||
|
width:20px !important;
|
||||||
|
height:20px !important;
|
||||||
|
}
|
||||||
|
.aloha-maximize {
|
||||||
|
width:20px !important;
|
||||||
|
height:20px !important;
|
||||||
|
background-image: url('../img/maximize.png') !important;
|
||||||
|
}
|
||||||
|
.aloha-textarea {
|
||||||
|
overflow: hidden;
|
||||||
|
border:1px solid #ccc;
|
||||||
|
padding:6px;
|
||||||
|
overflow:auto;
|
||||||
|
}
|
284
vendor/assets/javascripts/locomotive/aloha/css/aloha-sidebar.css
vendored
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
.aloha-sidebar-bar {
|
||||||
|
z-index: 999999999;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
text-align: left;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
border-right: 1px solid #888;
|
||||||
|
-moz-box-shadow: 0px 0px 10px rgba(0, 0, 0, .5);
|
||||||
|
-webkit-box-shadow: 0px 0px 10px rgba(0, 0, 0, .5);
|
||||||
|
box-shadow: 0px 0px 10px rgba(0, 0, 0, .5);
|
||||||
|
|
||||||
|
}
|
||||||
|
.aloha-sidebar-bar.aloha-sidebar-right {
|
||||||
|
left: auto;
|
||||||
|
right: 0;
|
||||||
|
border-right-width: 0;
|
||||||
|
border-left: 1px solid #888;
|
||||||
|
}
|
||||||
|
.aloha-sidebar-inner {
|
||||||
|
position: relative;
|
||||||
|
background-color: #ccc;
|
||||||
|
background-image: -webkit-radial-gradient(
|
||||||
|
rgba(0, 0, 0, 0.0) 20%,
|
||||||
|
rgba(0, 0, 0, 0.2) 80%,
|
||||||
|
rgba(0, 0, 0, 0.3) 100%
|
||||||
|
);
|
||||||
|
background-image: -moz-radial-gradient(
|
||||||
|
rgba(0, 0, 0, 0.0) 20%,
|
||||||
|
rgba(0, 0, 0, 0.2) 80%,
|
||||||
|
rgba(0, 0, 0, 0.3) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panels {
|
||||||
|
margin: 0;
|
||||||
|
padding: 15px 0;
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panels li {
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panel-top {
|
||||||
|
-webkit-border-top-left-radius: 5px;
|
||||||
|
-webkit-border-top-right-radius: 5px;
|
||||||
|
-moz-border-radius-topleft: 5px;
|
||||||
|
-moz-border-radius-topright: 5px;
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panel-content.aloha-sidebar-panel-bottom {
|
||||||
|
padding-bottom: 0;
|
||||||
|
-webkit-border-bottom-left-radius: 5px;
|
||||||
|
-webkit-border-bottom-right-radius: 5px;
|
||||||
|
-moz-border-radius-bottomleft: 5px;
|
||||||
|
-moz-border-radius-bottomright: 5px;
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panel-title {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 8px 0 0 8px;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||||
|
border-bottom-width: 0;
|
||||||
|
background-color: #303539;
|
||||||
|
background-image: -webkit-gradient(
|
||||||
|
linear,
|
||||||
|
center top,
|
||||||
|
center bottom,
|
||||||
|
color-stop(0.00, #6c6f74),
|
||||||
|
color-stop(0.05, #4c4f54),
|
||||||
|
color-stop(0.10, #3f4448),
|
||||||
|
color-stop(0.45, #383d41),
|
||||||
|
color-stop(0.50, #303539),
|
||||||
|
color-stop(0.95, #33363b)
|
||||||
|
);
|
||||||
|
background-image: -moz-linear-gradient(
|
||||||
|
center top,
|
||||||
|
#6c6f74 0%,
|
||||||
|
#4c4f54 5%,
|
||||||
|
#3f4448 10%,
|
||||||
|
#383d41 45%,
|
||||||
|
#303539 50%,
|
||||||
|
#33363b 95%
|
||||||
|
);
|
||||||
|
|
||||||
|
color: #ccc;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1.5em;
|
||||||
|
text-shadow: 0 0 6px #23262b;
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panel-title:hover {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panel-title-arrow {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
left: 8px;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background: url(../img/arrow.png) no-repeat center center;
|
||||||
|
opacity: 0.3;
|
||||||
|
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panel-title:hover .aloha-sidebar-panel-title-arrow {
|
||||||
|
opacity: 0.9;
|
||||||
|
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panel-title .aloha-sidebar-panel-title-arrow.aloha-sidebar-panel-title-arrow-down {
|
||||||
|
background-image: url(../img/arrow-down.png);
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panel-title-text {
|
||||||
|
margin-left: 24px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panel-content {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 5px;
|
||||||
|
background: #303539;
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panel-content-inner {
|
||||||
|
margin: 5px 0 0;
|
||||||
|
padding: 0;
|
||||||
|
color: #888;
|
||||||
|
background: #fff;
|
||||||
|
background-image: -webkit-radial-gradient(
|
||||||
|
rgba(0, 0, 0, 0.0) 50%,
|
||||||
|
rgba(0, 0, 0, 0.1) 90%,
|
||||||
|
rgba(0, 0, 0, 0.2) 100%
|
||||||
|
);
|
||||||
|
background-image: -moz-radial-gradient(
|
||||||
|
rgba(0, 0, 0, 0.0) 50%,
|
||||||
|
rgba(0, 0, 0, 0.1) 90%,
|
||||||
|
rgba(0, 0, 0, 0.2) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panel-content input {
|
||||||
|
margin: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
.aloha-sidebar-handle {
|
||||||
|
position: absolute;
|
||||||
|
top: 30px;
|
||||||
|
right: -30px;
|
||||||
|
width: 40px;
|
||||||
|
height: 30px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
background-color: #303539;
|
||||||
|
background-image: -webkit-gradient(
|
||||||
|
linear,
|
||||||
|
center top,
|
||||||
|
center bottom,
|
||||||
|
color-stop(0.00, #6c6f74),
|
||||||
|
color-stop(0.05, #4c4f54),
|
||||||
|
color-stop(0.10, #3f4448),
|
||||||
|
color-stop(0.45, #383d41),
|
||||||
|
color-stop(0.50, #303539),
|
||||||
|
color-stop(0.95, #33363b)
|
||||||
|
);
|
||||||
|
background-image: -moz-linear-gradient(
|
||||||
|
center top,
|
||||||
|
#6c6f74 0%,
|
||||||
|
#4c4f54 5%,
|
||||||
|
#3f4448 10%,
|
||||||
|
#383d41 45%,
|
||||||
|
#303539 50%,
|
||||||
|
#33363b 95%
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
-moz-box-shadow: 0px 0px 10px rgba(0, 0, 0, .5);
|
||||||
|
-webkit-box-shadow: 0px 0px 10px rgba(0, 0, 0, .5);
|
||||||
|
box-shadow: 0px 0px 10px rgba(0, 0, 0, .5);
|
||||||
|
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
-moz-border-radius-topright: 5px;
|
||||||
|
-moz-border-radius-bottomright: 5px;
|
||||||
|
-webkit-border-top-right-radius: 5px;
|
||||||
|
-webkit-border-bottom-right-radius: 5px;
|
||||||
|
}
|
||||||
|
.aloha-sidebar-right .aloha-sidebar-handle {
|
||||||
|
left: -30px;
|
||||||
|
border-top-right-radius: 0px;
|
||||||
|
border-bottom-right-radius: 0px;
|
||||||
|
-moz-border-radius-topright: 0px;
|
||||||
|
-moz-border-radius-bottomright: 0px;
|
||||||
|
-webkit-border-top-right-radius: 0px;
|
||||||
|
-webkit-border-bottom-right-radius: 0px;
|
||||||
|
|
||||||
|
border-top-left-radius: 5px;
|
||||||
|
border-bottom-left-radius: 5px;
|
||||||
|
-moz-border-radius-topleft: 5px;
|
||||||
|
-moz-border-radius-bottomleft: 5px;
|
||||||
|
-webkit-border-top-left-radius: 5px;
|
||||||
|
-webkit-border-bottom-left-radius: 5px;
|
||||||
|
}
|
||||||
|
.aloha-sidebar-handle-icon {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
|
background: url(../img/arrow.png) no-repeat center center;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
opacity: 0.5;
|
||||||
|
|
||||||
|
}
|
||||||
|
.aloha-sidebar-right .aloha-sidebar-handle-icon {
|
||||||
|
left: 5px;
|
||||||
|
right: auto;
|
||||||
|
}
|
||||||
|
.aloha-sidebar-handle-icon.aloha-sidebar-handle-icon-left {
|
||||||
|
background-image: url(../img/arrow-left.png);
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panel-parent-path {
|
||||||
|
background-color: #303539;
|
||||||
|
background-image: -webkit-gradient(
|
||||||
|
linear,
|
||||||
|
center top,
|
||||||
|
center bottom,
|
||||||
|
color-stop(0.00, #6c6f74),
|
||||||
|
color-stop(0.05, #4c4f54),
|
||||||
|
color-stop(0.10, #3f4448),
|
||||||
|
color-stop(0.45, #383d41),
|
||||||
|
color-stop(0.50, #303539),
|
||||||
|
color-stop(0.95, #33363b)
|
||||||
|
);
|
||||||
|
background-image: -moz-linear-gradient(
|
||||||
|
center top,
|
||||||
|
#6c6f74 0%,
|
||||||
|
#4c4f54 5%,
|
||||||
|
#3f4448 10%,
|
||||||
|
#383d41 45%,
|
||||||
|
#303539 50%,
|
||||||
|
#33363b 95%
|
||||||
|
);
|
||||||
|
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
text-shadow: 0 0 6px #23262b;
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 1em;
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panel-parent-path:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panel-parent-path span {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 10px 0 5px;
|
||||||
|
background: url(../img/breadcrumb-divider.png) no-repeat right center;
|
||||||
|
opacity: 0.25;
|
||||||
|
|
||||||
|
line-height: 1.6em;
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panel-parent-path span:first-child {
|
||||||
|
font-weight: bold;
|
||||||
|
opacity: 0.8;
|
||||||
|
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panel-parent-path span:last-child {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
.aloha-sidebar-panel-parent-content {
|
||||||
|
padding: 4px;
|
||||||
|
background-image: -webkit-gradient(
|
||||||
|
linear,
|
||||||
|
center top,
|
||||||
|
center bottom,
|
||||||
|
color-stop(0.0, rgba(0, 0, 0, 0.25)),
|
||||||
|
color-stop(0.05, rgba(0, 0, 0, 0.0))
|
||||||
|
);
|
||||||
|
background-image: -moz-linear-gradient(
|
||||||
|
center top,
|
||||||
|
rgba(0, 0, 0, 0.25) 0%,
|
||||||
|
rgba(0, 0, 0, 0.0) 5%
|
||||||
|
);
|
||||||
|
}
|
7774
vendor/assets/javascripts/locomotive/aloha/css/aloha.css
vendored
Normal file
33
vendor/assets/javascripts/locomotive/aloha/css/ext-aloha-reset.css
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
.aloha-floatingmenu table, .aloha-floatingmenu tr, .aloha-floatingmenu td,
|
||||||
|
.ext-root table, .ext-root tr, .ext-root td,
|
||||||
|
table.x-layer, .x-layer tr, .x-layer td,
|
||||||
|
table.x-window, .x-window tr, .x-window td,
|
||||||
|
table.x-toolbar-ct, .x-toolbar-ct tr, .x-toolbar-ct td,
|
||||||
|
table.x-toolbar-left, .x-toolbar-left tr, .x-toolbar-left td,
|
||||||
|
table.x-table-layout, .x-table-layout tr, .x-table-layout td,
|
||||||
|
table.x-toolbar-ct table, .x-toolbar-cell td, .x-table-layout-cell td {
|
||||||
|
width: 0 !important;
|
||||||
|
height: 0 !important;
|
||||||
|
border: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
/* reset for floatingmenu */
|
||||||
|
div.aloha-floatingmenu, div.aloha-shadow {
|
||||||
|
min-width: 0px;
|
||||||
|
}
|
||||||
|
/* more specific for browsers that support nth-child. */
|
||||||
|
.ext-root tr:nth-child(2n), .ext-root tr:nth-child(3n), .ext-root tr:nth-child(4n), .ext-root tr:nth-child(5n) {
|
||||||
|
width: 0 !important;
|
||||||
|
height: 0 !important;
|
||||||
|
border: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
.ext-root ol, .ext-root ul, .x-menu ul, .x-menu ol {
|
||||||
|
list-style:none;
|
||||||
|
margin:0;
|
||||||
|
padding:0;
|
||||||
|
}
|
357
vendor/assets/javascripts/locomotive/aloha/css/ext-aloha.css
vendored
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
.aloha-floatingmenu {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
.aloha-floatingmenu.fixed {
|
||||||
|
position: fixed !important;
|
||||||
|
}
|
||||||
|
.aloha-floatingmenu .x-tab-panel-header {
|
||||||
|
border-width: 0px 0px 1px 0px !important;
|
||||||
|
background: none !important;
|
||||||
|
}
|
||||||
|
.aloha-floatingmenu .x-tab-panel-header .x-tab-strip-wrap, .aloha-floatingmenu ul.x-tab-strip-top {
|
||||||
|
background: none !important;
|
||||||
|
}
|
||||||
|
.x-tab-strip-wrap ul li a:hover {
|
||||||
|
border: none;
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
.aloha-floatingmenu .x-tab-panel-header {
|
||||||
|
padding-bottom: 0px !important;
|
||||||
|
}
|
||||||
|
.aloha-floatingmenu:hover {
|
||||||
|
background-color: #E0E0E0;
|
||||||
|
-moz-border-radius-topleft: 3px;
|
||||||
|
-moz-border-radius-topright: 3px;
|
||||||
|
-webkit-border-top-left-radius: 3px;
|
||||||
|
-webkit-border-top-right-radius: 3px;
|
||||||
|
border-top-left-radius: 3px;
|
||||||
|
border-top-right-radius: 3px;
|
||||||
|
background: url(../img/grabhandle.png) no-repeat scroll center 5px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
.aloha-floatingmenu:hover .aloha-floatingmenu-pin {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.aloha-floatingmenu-pin {
|
||||||
|
background: url("../img/pin.png") no-repeat scroll 0 6px transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 5px !important;
|
||||||
|
width: 16px;
|
||||||
|
position: absolute !important;
|
||||||
|
right: 10px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.aloha-floatingmenu-pinned {
|
||||||
|
background-position: -16px 6px;
|
||||||
|
}
|
||||||
|
#aloha-floatingmenu-shadow {
|
||||||
|
position: absolute;
|
||||||
|
top: -1000;
|
||||||
|
left: -1000;
|
||||||
|
display: none;
|
||||||
|
z-index: 8800;
|
||||||
|
}
|
||||||
|
#aloha-floatingmenu-shadow.fixed {
|
||||||
|
position: fixed !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.x-tree-root-ct {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
.x-tree-node {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
.x-tree-node-ct {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
.aloha-button a:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
button.aloha-button {
|
||||||
|
background: url('../img/base.png') no-repeat !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-big {
|
||||||
|
background: url('../img/base-big.png') no-repeat !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-b {
|
||||||
|
background-position: 0px 0px;
|
||||||
|
}
|
||||||
|
button.aloha-button-i {
|
||||||
|
background-position: -16px 0px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-u {
|
||||||
|
background-position: -32px 0px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-del {
|
||||||
|
background-position: -48px 0px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-sub {
|
||||||
|
background-position: -64px 0px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-sup {
|
||||||
|
background-position: -80px 0px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-ul {
|
||||||
|
background-position: -256px 0px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-ol {
|
||||||
|
background-position: -272px 0px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-indent-list {
|
||||||
|
background-image: url(../img/text_indent.png) !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-outdent-list {
|
||||||
|
background-image: url(../img/text_indent_remove.png) !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-a {
|
||||||
|
background-position: -288px 0px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-a-remove {
|
||||||
|
background-position: -304px 0px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-p {
|
||||||
|
background-position: 0px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-p-de {
|
||||||
|
background-position: 0px -42px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-h1 {
|
||||||
|
background-position: -52px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-h1-de {
|
||||||
|
background-position: -52px -42px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-h2 {
|
||||||
|
background-position: -104px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-h2-de {
|
||||||
|
background-position: -104px -42px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-h3 {
|
||||||
|
background-position: -156px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-h3-de {
|
||||||
|
background-position: -156px -42px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-h4 {
|
||||||
|
background-position: -208px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-h4-de {
|
||||||
|
background-position: -208px -42px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-h5 {
|
||||||
|
background-position: -260px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-h5-de {
|
||||||
|
background-position: -260px -42px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-h6 {
|
||||||
|
background-position: -312px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-h6-de {
|
||||||
|
background-position: -312px -42px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-pre {
|
||||||
|
background-position: -364px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-pre-de {
|
||||||
|
background-position: -364px -42px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-title {
|
||||||
|
background-position: -416px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-title-de {
|
||||||
|
background-position: -416px -42px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-table {
|
||||||
|
background-position: -96px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-addColumnLeft {
|
||||||
|
background-position: -112px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-addColumnRight {
|
||||||
|
background-position: -128px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-addRowBefore {
|
||||||
|
background-position: -144px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-addRowAfter {
|
||||||
|
background-position: -160px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-deleteRows {
|
||||||
|
background-position: -176px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-deleteColumns {
|
||||||
|
background-position: -192px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-addPerson {
|
||||||
|
background-position: -224px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-addEvent {
|
||||||
|
background-position: -208px 0 !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-abbr {
|
||||||
|
background-position: -336px 0px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-row-header {
|
||||||
|
background-position: -352px 0px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-col-header {
|
||||||
|
background-position: -368px 0px !important;
|
||||||
|
}
|
||||||
|
button.aloha-button-tree {
|
||||||
|
background-position: -0 0 !important;
|
||||||
|
}
|
||||||
|
div.aloha-button a:hover {
|
||||||
|
border: 1px solid #b2cbff;
|
||||||
|
}
|
||||||
|
.aloha-floatingmenu {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.aloha-floatingmenu .pressed a {
|
||||||
|
border:1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #aloha-ribbon {
|
||||||
|
z-index:90000;
|
||||||
|
position:fixed;
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
width:100%;
|
||||||
|
padding-left:0;
|
||||||
|
padding-right:0;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.x-menu-list-item {
|
||||||
|
text-align:left;
|
||||||
|
}
|
||||||
|
.aloha-shadow {
|
||||||
|
-moz-box-shadow: 3px 3px 5px rgba(0, 0, 0, 0.3);
|
||||||
|
-webkit-box-shadow: 3px 3px 5px rgba(0, 0, 0, 0.3);
|
||||||
|
box-shadow: 3px 3px 5px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
ul.aloha-multisplit button.aloha-button {
|
||||||
|
background-image: url(../img/base-multi.png) !important;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: 0 0;
|
||||||
|
|
||||||
|
width: 54px !important;
|
||||||
|
height: 44px !important;
|
||||||
|
-moz-border-radius: 3px;
|
||||||
|
-webkit-border-radius: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.aloha-multisplit {
|
||||||
|
float: left;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
ul.aloha-multisplit, div.aloha-multisplit-wrapper {
|
||||||
|
width: 232px;
|
||||||
|
height: 46px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
div.aloha-multisplit-wrapper {
|
||||||
|
width: 248px;
|
||||||
|
}
|
||||||
|
ul.aloha-multisplit li {
|
||||||
|
float: left;
|
||||||
|
padding: 0px 1px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
ul.aloha-multisplit button.aloha-multisplit-wide {
|
||||||
|
font: 11px tahoma,arial,helvetica;
|
||||||
|
height: 18px !important;
|
||||||
|
width: 234px !important;
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px solid #cccccc;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: white;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: 6px 1px !important;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: -2px;
|
||||||
|
padding-left: 26px;
|
||||||
|
}
|
||||||
|
button.aloha-multisplit-toggle {
|
||||||
|
float: right;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 15px;
|
||||||
|
height: 12px;
|
||||||
|
margin-top: 32px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
button.aloha-multisplit-toggle-open {
|
||||||
|
background-image: url(../img/multisplit-open.gif);
|
||||||
|
}
|
||||||
|
button.aloha-multisplit-toggle-close {
|
||||||
|
background-image: url(../img/multisplit-close.gif);
|
||||||
|
}
|
||||||
|
ul.aloha-multisplit button {
|
||||||
|
border: 1px solid #cccccc;
|
||||||
|
margin: 1px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
ul.aloha-multisplit button:hover {
|
||||||
|
border: 1px solid #666666;
|
||||||
|
}
|
||||||
|
ul.aloha-multisplit button.aloha-multisplit-wide:hover {
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px solid #cccccc;
|
||||||
|
}
|
||||||
|
ul.aloha-multisplit .aloha-multisplit-activeitem button {
|
||||||
|
border: 2px solid #3B73D7;
|
||||||
|
}
|
||||||
|
ul.aloha-multisplit-expanded {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
z-index: 20000;
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #afafaf;
|
||||||
|
-moz-border-radius: 3px;
|
||||||
|
-webkit-border-radius: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
ul.aloha-multisplit button.aloha-button-removeFormat {
|
||||||
|
background-image: url(../img/removeformat.png) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* combo-list box
|
||||||
|
*/
|
||||||
|
.x-combo-list {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.x-combo-list-inner {
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
.x-combo-list-inner .loading-indicator {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.x-combo-selected {
|
||||||
|
color: #fff !important;
|
||||||
|
background-color: #3B73D7 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ui-attribute field
|
||||||
|
*/
|
||||||
|
.x-form-field.x-form-text {
|
||||||
|
color: #333 !important;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
.x-form-field.x-form-text::selection {
|
||||||
|
color: #fff;
|
||||||
|
background: #3B73D7;
|
||||||
|
}
|
||||||
|
.x-form-field.x-form-text::-moz-selection {
|
||||||
|
color: #fff;
|
||||||
|
background: #3B73D7;
|
||||||
|
}
|
||||||
|
.x-form-field.x-form-text::-webkit-selection {
|
||||||
|
color: #fff;
|
||||||
|
background: #3B73D7;
|
||||||
|
}
|
BIN
vendor/assets/javascripts/locomotive/aloha/img/arrow-down.png
vendored
Normal file
After Width: | Height: | Size: 770 B |
BIN
vendor/assets/javascripts/locomotive/aloha/img/arrow-left.png
vendored
Normal file
After Width: | Height: | Size: 790 B |
BIN
vendor/assets/javascripts/locomotive/aloha/img/arrow.png
vendored
Normal file
After Width: | Height: | Size: 719 B |
BIN
vendor/assets/javascripts/locomotive/aloha/img/base-big.png
vendored
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
vendor/assets/javascripts/locomotive/aloha/img/base-multi.png
vendored
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
vendor/assets/javascripts/locomotive/aloha/img/base.png
vendored
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
vendor/assets/javascripts/locomotive/aloha/img/bg.png
vendored
Normal file
After Width: | Height: | Size: 509 KiB |
BIN
vendor/assets/javascripts/locomotive/aloha/img/breadcrumb-divider.png
vendored
Normal file
After Width: | Height: | Size: 330 B |
BIN
vendor/assets/javascripts/locomotive/aloha/img/fade-in.png
vendored
Normal file
After Width: | Height: | Size: 278 B |
BIN
vendor/assets/javascripts/locomotive/aloha/img/fade-out.png
vendored
Normal file
After Width: | Height: | Size: 292 B |
BIN
vendor/assets/javascripts/locomotive/aloha/img/gentics-logo.png
vendored
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
vendor/assets/javascripts/locomotive/aloha/img/grabhandle.png
vendored
Normal file
After Width: | Height: | Size: 112 B |
BIN
vendor/assets/javascripts/locomotive/aloha/img/maximize.png
vendored
Normal file
After Width: | Height: | Size: 123 B |
BIN
vendor/assets/javascripts/locomotive/aloha/img/multisplit-base.jpg
vendored
Normal file
After Width: | Height: | Size: 929 B |
BIN
vendor/assets/javascripts/locomotive/aloha/img/multisplit-close.gif
vendored
Normal file
After Width: | Height: | Size: 211 B |
BIN
vendor/assets/javascripts/locomotive/aloha/img/multisplit-open.gif
vendored
Normal file
After Width: | Height: | Size: 211 B |
BIN
vendor/assets/javascripts/locomotive/aloha/img/pin.png
vendored
Normal file
After Width: | Height: | Size: 564 B |
BIN
vendor/assets/javascripts/locomotive/aloha/img/removeformat.png
vendored
Normal file
After Width: | Height: | Size: 193 B |
BIN
vendor/assets/javascripts/locomotive/aloha/img/text_indent.png
vendored
Normal file
After Width: | Height: | Size: 353 B |
BIN
vendor/assets/javascripts/locomotive/aloha/img/text_indent_remove.png
vendored
Normal file
After Width: | Height: | Size: 351 B |
27566
vendor/assets/javascripts/locomotive/aloha/lib/aloha-bootstrap.js
vendored
Normal file
88671
vendor/assets/javascripts/locomotive/aloha/lib/aloha.js
vendored
Normal file
246
vendor/assets/javascripts/locomotive/aloha/lib/aloha/command.js
vendored
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
/*!
|
||||||
|
* CommandManager file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright (c) 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*//*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with CommandManager program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define( [ 'aloha/core', 'aloha/registry', 'aloha/engine', 'util/dom', 'aloha/contenthandlermanager' ],
|
||||||
|
function( Aloha, Registry, Engine, Dom, ContentHandlerManager ) {
|
||||||
|
|
||||||
|
// Action: What the command does when executed via execCommand(). Every command defined
|
||||||
|
// in CommandManager specification has an action defined for it in the relevant section. For example,
|
||||||
|
// the bold command's action generally makes the current selection bold, or removes bold if
|
||||||
|
// the selection is already bold. An editing toolbar might provide buttons that execute the
|
||||||
|
// action for a command if clicked, or a script might run an action without user interaction
|
||||||
|
// to achieve some particular effect.
|
||||||
|
//
|
||||||
|
// Indeterminate: A boolean value returned by queryCommandIndeterm(), depending on the
|
||||||
|
// current state of the document. Generally, a command that has a state defined will be
|
||||||
|
// indeterminate if the state is true for part but not all of the current selection, and a
|
||||||
|
// command that has a value defined will be indeterminate if different parts of the
|
||||||
|
// selection have different values. An editing toolbar might display a button or control
|
||||||
|
// in a special way if the command is indeterminate, like showing a "bold" button as
|
||||||
|
// partially depressed, or leaving a font size selector blank instead of showing the font
|
||||||
|
// size of the current selection. As a rule, a command can only be indeterminate if its
|
||||||
|
// state is false, supposing it has a state.
|
||||||
|
//
|
||||||
|
// State: A boolean value returned by queryCommandState(), depending on the current state
|
||||||
|
// of the document. The state of a command is true if it is already in effect, in some
|
||||||
|
// sense specific to the command. Most commands that have a state defined will take opposite
|
||||||
|
// actions depending on whether the state is true or false, such as making the selection
|
||||||
|
// bold if the state is false and removing bold if the state is true. Others will just
|
||||||
|
// have no effect if the state is true, like the justifyCenter command. Still others will
|
||||||
|
// have the same effect regardless, like the styleWithCss command. An editing toolbar might
|
||||||
|
// display a button or control differently depending on the state and indeterminacy of the
|
||||||
|
// command.
|
||||||
|
//
|
||||||
|
// Value: A string returned by queryCommandValue(), depending on the current state of the
|
||||||
|
// document. A command usually has a value instead of a state if the property it modifies
|
||||||
|
// can take more than two different values, like the foreColor command. If the command is
|
||||||
|
// indeterminate, its value is generally based on the start of the selection. Otherwise,
|
||||||
|
// in most cases the value holds true for the entire selection, but see the justifyCenter
|
||||||
|
// command and its three companions for an exception. An editing toolbar might display the
|
||||||
|
// value of a command as selected in a drop-down or filled in in a text box, if the command
|
||||||
|
// isn't indeterminate.
|
||||||
|
//
|
||||||
|
// Relevant CSS property: CommandManager is defined for certain inline formatting commands, and
|
||||||
|
// is used in algorithms specific to those commands. It is an implementation detail, and
|
||||||
|
// is not exposed to authors. If a command does not have a relevant CSS property
|
||||||
|
// specified, it defaults to null.
|
||||||
|
|
||||||
|
var CommandManager = {
|
||||||
|
|
||||||
|
execCommand: function( commandId, showUi, value, range ) {
|
||||||
|
|
||||||
|
// Read current selection if not passed
|
||||||
|
if ( !range ) {
|
||||||
|
if ( !Aloha.getSelection().getRangeCount() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
range = Aloha.getSelection().getRangeAt( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// For the insertHTML command we provide contenthandler API
|
||||||
|
if ( commandId == 'insertHTML' ) {
|
||||||
|
//if (typeof Aloha.settings.contentHandler.insertHtml === 'undefined') {
|
||||||
|
// use all registered content handler; used for copy & paste atm (or write log message)
|
||||||
|
// Aloha.settings.contentHandler.insertHtml = Aloha.defaults.contentHandler.insertHtml;
|
||||||
|
//}
|
||||||
|
value = ContentHandlerManager.handleContent( value, {
|
||||||
|
contenthandler: Aloha.settings.contentHandler.insertHtml
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Engine.execCommand( commandId, showUi, value, range );
|
||||||
|
|
||||||
|
// Read range after engine modification
|
||||||
|
range = Aloha.getSelection().getRangeAt( 0 );
|
||||||
|
|
||||||
|
// FIX: doCleanup should work with W3C range
|
||||||
|
var startnode = range.commonAncestorContainer.parentNode;
|
||||||
|
var rangeObject = new window.GENTICS.Utils.RangeObject();
|
||||||
|
rangeObject.startContainer = range.startContainer;
|
||||||
|
rangeObject.startOffset = range.startOffset;
|
||||||
|
rangeObject.endContainer = range.endContainer;
|
||||||
|
rangeObject.endOffset = range.endOffset;
|
||||||
|
Dom.doCleanup({merge:true, removeempty: false}, rangeObject, startnode);
|
||||||
|
rangeObject.select();
|
||||||
|
Aloha.trigger('aloha-command-executed', commandId);
|
||||||
|
},
|
||||||
|
|
||||||
|
// If command is available and not disabled or the active range is not null
|
||||||
|
// the command is enabled
|
||||||
|
queryCommandEnabled: function( commandId, range ) {
|
||||||
|
|
||||||
|
// Take current selection if not passed
|
||||||
|
if ( !range ) {
|
||||||
|
if ( !Aloha.getSelection().getRangeCount() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
range = Aloha.getSelection().getRangeAt(0);
|
||||||
|
}
|
||||||
|
return Engine.queryCommandEnabled( commandId, range );
|
||||||
|
},
|
||||||
|
|
||||||
|
// "Return true if command is indeterminate, otherwise false."
|
||||||
|
queryCommandIndeterm: function( commandId, range ) {
|
||||||
|
|
||||||
|
// Take current selection if not passed
|
||||||
|
if ( !range ) {
|
||||||
|
if ( !Aloha.getSelection().getRangeCount() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
range = Aloha.getSelection().getRangeAt(0);
|
||||||
|
}
|
||||||
|
return Engine.queryCommandIndeterm( commandId, range );
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
queryCommandState: function( commandId, range ) {
|
||||||
|
|
||||||
|
// Take current selection if not passed
|
||||||
|
if ( !range ) {
|
||||||
|
if ( !Aloha.getSelection().getRangeCount() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
range = Aloha.getSelection().getRangeAt(0);
|
||||||
|
}
|
||||||
|
return Engine.queryCommandState( commandId, range );
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
// "When the queryCommandSupported(command) method on the HTMLDocument
|
||||||
|
// interface is invoked, the user agent must return true if command is
|
||||||
|
// supported, and false otherwise."
|
||||||
|
queryCommandSupported: function( commandId ) {
|
||||||
|
|
||||||
|
return Engine.queryCommandSupported( commandId );
|
||||||
|
},
|
||||||
|
|
||||||
|
queryCommandValue: function( commandId, range ) {
|
||||||
|
|
||||||
|
// Take current selection if not passed
|
||||||
|
if ( !range ) {
|
||||||
|
if ( !Aloha.getSelection().getRangeCount() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
range = Aloha.getSelection().getRangeAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Return command's value."
|
||||||
|
return Engine.queryCommandValue( commandId, range );
|
||||||
|
},
|
||||||
|
querySupportedCommands: function() {
|
||||||
|
|
||||||
|
var
|
||||||
|
commands = [],
|
||||||
|
command;
|
||||||
|
|
||||||
|
for ( command in Engine.commands ) {
|
||||||
|
commands.push( command );
|
||||||
|
}
|
||||||
|
return commands;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// create an instance
|
||||||
|
CommandManager = new ( Registry.extend( CommandManager ) )();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a registered command.
|
||||||
|
* http://aryeh.name/spec/editing/editing.html#methods-of-the-htmldocument-interface
|
||||||
|
* @method
|
||||||
|
* @param command name of the command
|
||||||
|
* @param showUI has no effect for Aloha Editor and is only here because in spec...
|
||||||
|
* @param value depends on the used command and it impementation
|
||||||
|
* @range optional a range on which the command will be executed if not specified
|
||||||
|
* the current selection will be used as range
|
||||||
|
*/
|
||||||
|
Aloha.execCommand = CommandManager.execCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check wheater the command in enabled.
|
||||||
|
* If command is not supported, raise a NOT_SUPPORTED_ERR exception.
|
||||||
|
* @param command name of the command
|
||||||
|
* @return true if command is enabled, false otherwise.
|
||||||
|
*/
|
||||||
|
Aloha.queryCommandEnabled = CommandManager.queryCommandEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the command has an indetermed state.
|
||||||
|
* If command is not supported, a NOT_SUPPORTED_ERR exception is thrown
|
||||||
|
* If command has no indeterminacy, INVALID_ACCESS_ERR exception is thrown
|
||||||
|
* If command is not enabled, return false.
|
||||||
|
* @param command name of the command
|
||||||
|
* @range optional a range on which the command will be executed if not specified
|
||||||
|
* the current selection will be used as range
|
||||||
|
* @return true if command is indeterminate, otherwise false.
|
||||||
|
*/
|
||||||
|
Aloha.queryCommandIndeterm = CommandManager.queryCommandIndeterm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the state of a given command
|
||||||
|
* If command is not supported, a NOT_SUPPORTED_ERR exception is thrown
|
||||||
|
* If command has no state, an INVALID_ACCESS_ERR exception is thrown
|
||||||
|
* If command is not enabled, return false
|
||||||
|
* If the state override for command is set, it returns the state
|
||||||
|
* @param command name of the command
|
||||||
|
* @return state override or true if command's state is true, otherwise false.
|
||||||
|
*/
|
||||||
|
Aloha.queryCommandState = CommandManager.queryCommandState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a given command is supported
|
||||||
|
* @return true if command is supported, and false otherwise.
|
||||||
|
*/
|
||||||
|
Aloha.queryCommandSupported = CommandManager.queryCommandSupported;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Value of a given Command
|
||||||
|
* If command is not supported, a NOT_SUPPORTED_ERR exception is thrown
|
||||||
|
* If command is not enabled, returns an empty string
|
||||||
|
* If command is "fontSize" and its value override is set, an integer
|
||||||
|
* number of pixels is returned as font size for the result.
|
||||||
|
* If the value override for command is set, it returns that.
|
||||||
|
* @return command's value.
|
||||||
|
*/
|
||||||
|
Aloha.queryCommandValue = CommandManager.queryCommandValue;
|
||||||
|
|
||||||
|
Aloha.querySupportedCommands = CommandManager.querySupportedCommands;
|
||||||
|
|
||||||
|
return CommandManager;
|
||||||
|
});
|
330
vendor/assets/javascripts/locomotive/aloha/lib/aloha/console.js
vendored
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright © 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*//*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
['aloha/core', 'util/class', 'aloha/jquery'],
|
||||||
|
function(Aloha, Class, jQuery ) {
|
||||||
|
|
||||||
|
|
||||||
|
var
|
||||||
|
// $ = jQuery,
|
||||||
|
// Aloha = window.Aloha,
|
||||||
|
console = window.console;
|
||||||
|
// Class = window.Class
|
||||||
|
// GENTICS = window.GENTICS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the aloha Log
|
||||||
|
* @namespace Aloha
|
||||||
|
* @class Log
|
||||||
|
* @singleton
|
||||||
|
*/
|
||||||
|
var alohaConsole = Class.extend({
|
||||||
|
/**
|
||||||
|
* Initialize the logging
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
init: function() {
|
||||||
|
|
||||||
|
// initialize the logging settings (if not present)
|
||||||
|
if (typeof Aloha.settings.logLevels === 'undefined' || !Aloha.settings.logLevels) {
|
||||||
|
Aloha.settings.logLevels = {'error' : true, 'warn' : true};
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the logHistory settings (if not present)
|
||||||
|
if (typeof Aloha.settings.logHistory === 'undefined' || !Aloha.settings.logHistory) {
|
||||||
|
Aloha.settings.logHistory = {};
|
||||||
|
}
|
||||||
|
// set the default values for the loghistory
|
||||||
|
if (!Aloha.settings.logHistory.maxEntries) {
|
||||||
|
Aloha.settings.logHistory.maxEntries = 100;
|
||||||
|
}
|
||||||
|
if (!Aloha.settings.logHistory.highWaterMark) {
|
||||||
|
Aloha.settings.logHistory.highWaterMark = 90;
|
||||||
|
}
|
||||||
|
if (!Aloha.settings.logHistory.levels) {
|
||||||
|
Aloha.settings.logHistory.levels = {'error' : true, 'warn' : true};
|
||||||
|
}
|
||||||
|
this.flushLogHistory();
|
||||||
|
|
||||||
|
Aloha.trigger('aloha-logger-ready');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log History as array of Message Objects. Every object has the properties
|
||||||
|
* 'level', 'component' and 'message'
|
||||||
|
* @property
|
||||||
|
* @type Array
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
logHistory: [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag, which is set as soon as the highWaterMark for the log history is reached.
|
||||||
|
* This flag is reset on every call of flushLogHistory()
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
highWaterMarkReached: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs a message to the console
|
||||||
|
* @method
|
||||||
|
* @param {String} level Level of the log ('error', 'warn' or 'info', 'debug')
|
||||||
|
* @param {String} component Component that calls the log
|
||||||
|
* @param {String} message log message
|
||||||
|
*/
|
||||||
|
log: function(level, component, message) {
|
||||||
|
|
||||||
|
|
||||||
|
// log ('Logging message');
|
||||||
|
if ( typeof component === 'undefined' ) {
|
||||||
|
message = level;
|
||||||
|
}
|
||||||
|
if ( typeof component !== 'string' && component && component.toString ) {
|
||||||
|
component = component.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// log ('warn', 'Warning message');
|
||||||
|
if ( typeof message === 'undefined' ) {
|
||||||
|
message = component;
|
||||||
|
component = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof level === 'undefined' || !level) {
|
||||||
|
level = 'log';
|
||||||
|
}
|
||||||
|
|
||||||
|
level = level.toLowerCase();
|
||||||
|
|
||||||
|
if ( typeof Aloha.settings.logLevels === "undefined" ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now check whether the log level is activated
|
||||||
|
if ( !Aloha.settings.logLevels[ level ] ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
component = component || "Unkown Aloha Component";
|
||||||
|
|
||||||
|
this.addToLogHistory({'level' : level, 'component' : component, 'message' : message, 'date' : new Date()});
|
||||||
|
|
||||||
|
switch (level) {
|
||||||
|
case 'error':
|
||||||
|
if (window.console && console.error) {
|
||||||
|
// FIXME:
|
||||||
|
// Using console.error rather than throwing an error is very
|
||||||
|
// problematic because we get not stack.
|
||||||
|
// We ought to consider doing the following:
|
||||||
|
// throw component + ': ' + message;
|
||||||
|
if(!component && !message) {
|
||||||
|
console.error("Error occured without message and component");
|
||||||
|
} else {
|
||||||
|
console.error(component + ': ' + message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'warn':
|
||||||
|
if (window.console && console.warn) {
|
||||||
|
console.warn(component + ': ' + message);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'info':
|
||||||
|
if (window.console && console.info) {
|
||||||
|
console.info(component + ': ' + message);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'debug':
|
||||||
|
if (window.console && console.log) {
|
||||||
|
console.log(component + ' [' + level + ']: ' + message);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (window.console && console.log) {
|
||||||
|
console.log(component + ' [' + level + ']: ' + message);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a message of log level 'error'
|
||||||
|
* @method
|
||||||
|
* @param {String} component Component that calls the log
|
||||||
|
* @param {String} message log message
|
||||||
|
*/
|
||||||
|
error: function(component, message) {
|
||||||
|
this.log('error', component, message);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a message of log level 'warn'
|
||||||
|
* @method
|
||||||
|
* @param {String} component Component that calls the log
|
||||||
|
* @param {String} message log message
|
||||||
|
*/
|
||||||
|
warn: function(component, message) {
|
||||||
|
this.log('warn', component, message);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a message of log level 'info'
|
||||||
|
* @method
|
||||||
|
* @param {String} component Component that calls the log
|
||||||
|
* @param {String} message log message
|
||||||
|
*/
|
||||||
|
info: function(component, message) {
|
||||||
|
this.log('info', component, message);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a message of log level 'debug'
|
||||||
|
* @param {String} component Component that calls the log
|
||||||
|
* @param {String} message log message
|
||||||
|
*/
|
||||||
|
debug: function(component, message) {
|
||||||
|
this.log('debug', component, message);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methods to mark function as deprecated for developers.
|
||||||
|
* @param {String} component String that calls the log
|
||||||
|
* @param {String} message log message
|
||||||
|
*/
|
||||||
|
deprecated: function(component, message) {
|
||||||
|
this.log( 'warn', component, message );
|
||||||
|
// help the developer to locate the call.
|
||||||
|
if ( Aloha.settings.logLevels[ 'deprecated' ] ) {
|
||||||
|
throw new Error ( message );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the given log level is currently enabled
|
||||||
|
* @param {String} level
|
||||||
|
* @return true when log level is enabled, false if not
|
||||||
|
*/
|
||||||
|
isLogLevelEnabled: function(level) {
|
||||||
|
return Aloha.settings && Aloha.settings.logLevels && Aloha.settings.logLevels[level];
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether error logging is enabled
|
||||||
|
* @return true if error logging is enabled, false if not
|
||||||
|
*/
|
||||||
|
isErrorEnabled: function() {
|
||||||
|
return this.isLogLevelEnabled('error');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether warn logging is enabled
|
||||||
|
* @return true if warn logging is enabled, false if not
|
||||||
|
*/
|
||||||
|
isWarnEnabled: function() {
|
||||||
|
return this.isLogLevelEnabled('warn');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether info logging is enabled
|
||||||
|
* @return true if info logging is enabled, false if not
|
||||||
|
*/
|
||||||
|
isInfoEnabled: function() {
|
||||||
|
return this.isLogLevelEnabled('info');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether debug logging is enabled
|
||||||
|
* @return true if debug logging is enabled, false if not
|
||||||
|
*/
|
||||||
|
isDebugEnabled: function() {
|
||||||
|
return this.isLogLevelEnabled('debug');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the given entry to the log history. Check whether the highWaterMark has been reached, and fire an event if yes.
|
||||||
|
* @param {Object} entry entry to be added to the log history
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
addToLogHistory: function(entry) {
|
||||||
|
|
||||||
|
if ( !Aloha.settings.logHistory ) {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
// when maxEntries is set to something illegal, we do nothing (log history is disabled)
|
||||||
|
// check whether the level is one we like to have logged
|
||||||
|
if ( Aloha.settings.logHistory.maxEntries <= 0
|
||||||
|
|| !Aloha.settings.logHistory.levels[ entry.level ]
|
||||||
|
) {
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// first add the entry as last element to the history array
|
||||||
|
this.logHistory.push( entry );
|
||||||
|
|
||||||
|
// check whether the highWaterMark was reached, if so, fire an event
|
||||||
|
if ( !this.highWaterMarkReached ) {
|
||||||
|
|
||||||
|
if ( this.logHistory.length >= Aloha.settings.logHistory.maxEntries * Aloha.settings.logHistory.highWaterMark / 100 ) {
|
||||||
|
|
||||||
|
// fire the event
|
||||||
|
Aloha.trigger('aloha-log-full');
|
||||||
|
// set the flag (so we will not fire the event again until the logHistory is flushed)
|
||||||
|
this.highWaterMarkReached = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether the log is full and eventually remove the oldest entries
|
||||||
|
while ( this.logHistory.length > Aloha.settings.logHistory.maxEntries ) {
|
||||||
|
this.logHistory.shift();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the log history
|
||||||
|
* @return log history as array of objects
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
getLogHistory: function() {
|
||||||
|
return this.logHistory;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush the log history. Remove all log entries and reset the flag for the highWaterMark
|
||||||
|
* @return void
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
flushLogHistory: function() {
|
||||||
|
this.logHistory = [];
|
||||||
|
this.highWaterMarkReached = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the Log object
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
alohaConsole = new alohaConsole();
|
||||||
|
|
||||||
|
// add to log namespace for compatiblility.
|
||||||
|
return Aloha.Log = Aloha.Console = alohaConsole;
|
||||||
|
|
||||||
|
});
|
65
vendor/assets/javascripts/locomotive/aloha/lib/aloha/contenthandlermanager.js
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*!
|
||||||
|
* Aloha Editor
|
||||||
|
* Author & Copyright (c) 2010 Gentics Software GmbH
|
||||||
|
* aloha-sales@gentics.com
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.com/license.html
|
||||||
|
*/
|
||||||
|
define(
|
||||||
|
['aloha/jquery', 'aloha/registry'],
|
||||||
|
function( jQuery, Registry ) {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an contentHandler from the given definition. Acts as a factory method
|
||||||
|
* for contentHandler.
|
||||||
|
*
|
||||||
|
* @param {Object} definition
|
||||||
|
*/
|
||||||
|
return new ( Registry.extend({
|
||||||
|
|
||||||
|
createHandler: function( definition ) {
|
||||||
|
|
||||||
|
if ( typeof definition.handleContent != 'function' ) {
|
||||||
|
throw 'ContentHandler has no function handleContent().';
|
||||||
|
}
|
||||||
|
|
||||||
|
var AbstractContentHandler = Class.extend({
|
||||||
|
handleContent: function( content ) {
|
||||||
|
// Implement in subclass!
|
||||||
|
}
|
||||||
|
}, definition);
|
||||||
|
|
||||||
|
return new AbstractContentHandler();
|
||||||
|
},
|
||||||
|
|
||||||
|
handleContent: function ( content, options ) {
|
||||||
|
var handler,
|
||||||
|
handlers = this.getEntries();
|
||||||
|
|
||||||
|
if ( typeof options.contenthandler === 'undefined') {
|
||||||
|
options.contenthandler = [];
|
||||||
|
for ( handler in handlers ) {
|
||||||
|
if ( handlers.hasOwnProperty(handler) ) {
|
||||||
|
options.contenthandler.push(handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( handler in handlers ) {
|
||||||
|
if ( handlers.hasOwnProperty(handler) ) {
|
||||||
|
if (jQuery.inArray( handler, options.contenthandler ) < 0 ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( typeof handlers[handler].handleContent === 'function') {
|
||||||
|
content = handlers[handler].handleContent( content, options );
|
||||||
|
} else {
|
||||||
|
console.error( 'A valid content handler needs the method handleContent.' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
}))();
|
||||||
|
});
|
560
vendor/assets/javascripts/locomotive/aloha/lib/aloha/core.js
vendored
Normal file
@ -0,0 +1,560 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright © 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*//*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
|
||||||
|
[
|
||||||
|
'aloha/jquery',
|
||||||
|
'aloha/pluginmanager'
|
||||||
|
],
|
||||||
|
|
||||||
|
function ( jQuery, PluginManager ) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------
|
||||||
|
// Private variables
|
||||||
|
//----------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash table that will be populated through the loadPlugins method.
|
||||||
|
* Maps the names of plugins with their urls for easy assess in the getPluginsUrl method
|
||||||
|
*/
|
||||||
|
var pluginPaths = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base Aloha Object
|
||||||
|
* @namespace Aloha
|
||||||
|
* @class Aloha The Aloha base object, which contains all the core functionality
|
||||||
|
* @singleton
|
||||||
|
*/
|
||||||
|
jQuery.extend(true, Aloha, {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Aloha Editor Version we are using
|
||||||
|
* It should be set by us and updated for the particular branch
|
||||||
|
* @property
|
||||||
|
*/
|
||||||
|
version: '0.10.0',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of editables that are managed by Aloha
|
||||||
|
* @property
|
||||||
|
* @type Array
|
||||||
|
*/
|
||||||
|
editables: [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The currently active editable is referenced here
|
||||||
|
* @property
|
||||||
|
* @type Aloha.Editable
|
||||||
|
*/
|
||||||
|
activeEditable: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* settings object, which will contain all Aloha settings
|
||||||
|
* @cfg {Object} object Aloha's settings
|
||||||
|
*/
|
||||||
|
settings: {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* defaults object, which will contain all Aloha defaults
|
||||||
|
* @cfg {Object} object Aloha's settings
|
||||||
|
*/
|
||||||
|
defaults: {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespace for ui components
|
||||||
|
*/
|
||||||
|
ui: {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This represents the name of the users OS. Could be:
|
||||||
|
* 'Mac', 'Linux', 'Win', 'Unix', 'Unknown'
|
||||||
|
* @property
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
OSName: 'Unknown',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Which stage is the aloha init process at?
|
||||||
|
* @property
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
stage: 'loadingAloha',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of loaded plugin names. Available after the
|
||||||
|
* "loadPlugins" stage.
|
||||||
|
*
|
||||||
|
* @property
|
||||||
|
* @type array
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
loadedPlugins: [],
|
||||||
|
|
||||||
|
requirePaths: [],
|
||||||
|
/**
|
||||||
|
* Initialize the initialization process
|
||||||
|
*/
|
||||||
|
init: function () {
|
||||||
|
|
||||||
|
// merge defaults and settings and provide all in settings
|
||||||
|
Aloha.settings = jQuery.extendObjects( true, {}, Aloha.defaults, Aloha.settings );
|
||||||
|
|
||||||
|
// initialize rangy. This is probably necessary here,
|
||||||
|
// because due to the current loading mechanism, rangy
|
||||||
|
// doesn't initialize itself in all browsers
|
||||||
|
if (window.rangy) {
|
||||||
|
window.rangy.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load & Initialise
|
||||||
|
Aloha.stage = 'loadPlugins';
|
||||||
|
Aloha.loadPlugins(function(){
|
||||||
|
Aloha.stage = 'initAloha';
|
||||||
|
Aloha.initAloha(function(){
|
||||||
|
Aloha.stage = 'initPlugins';
|
||||||
|
Aloha.initPlugins(function(){
|
||||||
|
Aloha.stage = 'initGui';
|
||||||
|
Aloha.initGui(function(){
|
||||||
|
Aloha.stage = 'alohaReady';
|
||||||
|
Aloha.trigger('aloha-ready');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load Plugins
|
||||||
|
*/
|
||||||
|
loadPlugins: function (next) {
|
||||||
|
// contains an array like [common/format, common/block]
|
||||||
|
var configuredPluginsWithBundle = this.getPluginsToBeLoaded();
|
||||||
|
|
||||||
|
if (configuredPluginsWithBundle.length) {
|
||||||
|
var paths = {},
|
||||||
|
pluginNames = [],
|
||||||
|
requiredInitializers = [],
|
||||||
|
pathsToPlugins = {};
|
||||||
|
|
||||||
|
// Background: We do not use CommonJS packages for our Plugins
|
||||||
|
// as this breaks the loading order when these modules have
|
||||||
|
// other dependencies.
|
||||||
|
// We "emulate" the commonjs modules with the path mapping.
|
||||||
|
/* require(
|
||||||
|
* { paths: {
|
||||||
|
* 'format': 'plugins/common/format/lib',
|
||||||
|
* 'format/nls': 'plugins/common/format/nls',
|
||||||
|
* ... for every plugin ...
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
* ['format/format-plugin', ... for every plugin ...],
|
||||||
|
* next <-- when everything is loaded, we continue
|
||||||
|
*/
|
||||||
|
jQuery.each(configuredPluginsWithBundle, function (i, configuredPluginWithBundle) {
|
||||||
|
var tmp, bundleName, pluginName, bundlePath = '';
|
||||||
|
|
||||||
|
tmp = configuredPluginWithBundle.split('/');
|
||||||
|
bundleName = tmp[0];
|
||||||
|
pluginName = tmp[1];
|
||||||
|
|
||||||
|
// TODO assertion if pluginName or bundleName NULL _-> ERROR!!
|
||||||
|
|
||||||
|
if (Aloha.settings.basePath) {
|
||||||
|
bundlePath = Aloha.settings.basePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Aloha.settings.bundles && Aloha.settings.bundles[bundleName]) {
|
||||||
|
bundlePath += Aloha.settings.bundles[bundleName];
|
||||||
|
} else {
|
||||||
|
bundlePath += '../plugins/' + bundleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginNames.push(pluginName);
|
||||||
|
paths[pluginName] = bundlePath + '/' + pluginName + '/lib';
|
||||||
|
|
||||||
|
pathsToPlugins[pluginName] = bundlePath + '/' + pluginName;
|
||||||
|
|
||||||
|
// As the "nls" path lies NOT inside /lib/, but is a sibling to /lib/, we need
|
||||||
|
// to register it explicitely. The same goes for the "css" folder.
|
||||||
|
jQuery.each(['nls', 'css', 'vendor', 'res'], function() {
|
||||||
|
paths[pluginName + '/' + this] = bundlePath + '/' + pluginName + '/' + this;
|
||||||
|
});
|
||||||
|
|
||||||
|
requiredInitializers.push(pluginName + '/' + pluginName + '-plugin');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.loadedPlugins = pluginNames;
|
||||||
|
this.requirePaths = paths;
|
||||||
|
|
||||||
|
// Main Require.js loading call, which fetches all the plugins.
|
||||||
|
require(
|
||||||
|
{
|
||||||
|
context: 'aloha',
|
||||||
|
paths: paths,
|
||||||
|
locale: this.settings.locale || this.defaults.locale || 'en'
|
||||||
|
},
|
||||||
|
requiredInitializers,
|
||||||
|
next
|
||||||
|
);
|
||||||
|
|
||||||
|
pluginPaths = pathsToPlugins;
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches plugins the user wants to have loaded. Returns all plugins the user
|
||||||
|
* has specified with the data-plugins property as array, with the bundle
|
||||||
|
* name in front.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
getPluginsToBeLoaded: function() {
|
||||||
|
// look for data-aloha-plugins attributes and load values
|
||||||
|
var
|
||||||
|
plugins = jQuery('[data-aloha-plugins]').data('aloha-plugins');
|
||||||
|
|
||||||
|
// Determine Plugins
|
||||||
|
if ( typeof plugins === 'string' && plugins !== "") {
|
||||||
|
return plugins.replace(/\s+/g, '').split(',');
|
||||||
|
}
|
||||||
|
// Return
|
||||||
|
return [];
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns list of loaded plugins (without Bundle name)
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
getLoadedPlugins: function() {
|
||||||
|
return this.loadedPlugins;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if a certain plugin is loaded, false otherwise.
|
||||||
|
*/
|
||||||
|
isPluginLoaded: function(pluginName) {
|
||||||
|
var found = false;
|
||||||
|
jQuery.each(this.loadedPlugins, function() {
|
||||||
|
if (pluginName.toString() === this.toString()) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return found;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise Aloha
|
||||||
|
*/
|
||||||
|
initAloha: function(next){
|
||||||
|
// check browser version on init
|
||||||
|
// this has to be revamped, as
|
||||||
|
if (jQuery.browser.webkit && parseFloat(jQuery.browser.version) < 532.5 || // Chrome/Safari 4
|
||||||
|
jQuery.browser.mozilla && parseFloat(jQuery.browser.version) < 1.9 || // FF 3.5
|
||||||
|
jQuery.browser.msie && jQuery.browser.version < 7 || // IE 7
|
||||||
|
jQuery.browser.opera && jQuery.browser.version < 11 ) { // right now, Opera needs some work
|
||||||
|
if (window.console && window.console.log) {
|
||||||
|
window.console.log( 'Your browser is not supported.' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// register the body click event to blur editables
|
||||||
|
jQuery('html').mousedown(function(e) {
|
||||||
|
// if an Ext JS modal is visible, we don't want to loose the focus on
|
||||||
|
// the editable as we assume that the user must have clicked somewhere
|
||||||
|
// in the modal... where else could he click?
|
||||||
|
// loosing the editable focus in this case hinders correct table
|
||||||
|
// column/row deletion, as the table module will clean it's selection
|
||||||
|
// as soon as the editable is deactivated. Fusubscriberthermore you'd have to
|
||||||
|
// refocus the editable again, which is just strange UX
|
||||||
|
if (Aloha.activeEditable && !Aloha.isMessageVisible() && !Aloha.eventHandled) {
|
||||||
|
Aloha.activeEditable.blur();
|
||||||
|
Aloha.activeEditable = null;
|
||||||
|
}
|
||||||
|
}).mouseup(function(e) {
|
||||||
|
Aloha.eventHandled = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialise the base path to the aloha files
|
||||||
|
Aloha.settings.base = Aloha.getAlohaUrl();
|
||||||
|
|
||||||
|
// initialize the Log
|
||||||
|
Aloha.Log.init();
|
||||||
|
|
||||||
|
// initialize the error handler for general javascript errors
|
||||||
|
if ( Aloha.settings.errorhandling ) {
|
||||||
|
window.onerror = function (msg, url, linenumber) {
|
||||||
|
Aloha.Log.error(Aloha, 'Error message: ' + msg + '\nURL: ' + url + '\nLine Number: ' + linenumber);
|
||||||
|
// TODO eventually add a message to the message line?
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// OS detection
|
||||||
|
if (navigator.appVersion.indexOf('Win') != -1) {
|
||||||
|
Aloha.OSName = 'Win';
|
||||||
|
}
|
||||||
|
if (navigator.appVersion.indexOf('Mac') != -1) {
|
||||||
|
Aloha.OSName = 'Mac';
|
||||||
|
}
|
||||||
|
if (navigator.appVersion.indexOf('X11') != -1) {
|
||||||
|
Aloha.OSName = 'Unix';
|
||||||
|
}
|
||||||
|
if (navigator.appVersion.indexOf('Linux') != -1) {
|
||||||
|
Aloha.OSName = 'Linux';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// this will disable browsers image resizing facilities
|
||||||
|
// disable resize handles
|
||||||
|
var supported;
|
||||||
|
try {
|
||||||
|
supported = document.queryCommandSupported( 'enableObjectResizing' );
|
||||||
|
} catch ( e ) {
|
||||||
|
supported = false;
|
||||||
|
Aloha.Log.log( 'enableObjectResizing is not supported.' );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( supported ) {
|
||||||
|
document.execCommand( 'enableObjectResizing', false, false);
|
||||||
|
Aloha.Log.log( 'enableObjectResizing disabled.' );
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Aloha.Log.error( e, 'Could not disable enableObjectResizing' );
|
||||||
|
// this is just for others, who will not support disabling enableObjectResizing
|
||||||
|
}
|
||||||
|
// Forward
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads plugins Aloha
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
initPlugins: function (next) {
|
||||||
|
PluginManager.init(function(){
|
||||||
|
next();
|
||||||
|
}, this.getLoadedPlugins() );
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads GUI components
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
initGui: function (next) {
|
||||||
|
|
||||||
|
Aloha.RepositoryManager.init();
|
||||||
|
|
||||||
|
// activate registered editables
|
||||||
|
for (var i = 0, editablesLength = Aloha.editables.length; i < editablesLength; i++) {
|
||||||
|
if ( !Aloha.editables[i].ready ) {
|
||||||
|
Aloha.editables[i].init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activates editable and deactivates all other Editables
|
||||||
|
* @param {Editable} editable the Editable to be activated
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
activateEditable: function (editable) {
|
||||||
|
|
||||||
|
// blur all editables, which are currently active
|
||||||
|
for (var i = 0, editablesLength = Aloha.editables.length; i < editablesLength; i++) {
|
||||||
|
if (Aloha.editables[i] != editable && Aloha.editables[i].isActive) {
|
||||||
|
Aloha.editables[i].blur();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Aloha.activeEditable = editable;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current Editable
|
||||||
|
* @return {Editable} returns the active Editable
|
||||||
|
*/
|
||||||
|
getActiveEditable: function() {
|
||||||
|
return Aloha.activeEditable;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* deactivated the current Editable
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
deactivateEditable: function () {
|
||||||
|
|
||||||
|
if ( typeof Aloha.activeEditable === 'undefined' || Aloha.activeEditable === null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// blur the editable
|
||||||
|
Aloha.activeEditable.blur();
|
||||||
|
Aloha.activeEditable = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an editable by an ID or null if no Editable with that ID registered.
|
||||||
|
* @param {string} id the element id to look for.
|
||||||
|
* @return {Aloha.Editable} editable
|
||||||
|
*/
|
||||||
|
getEditableById: function (id) {
|
||||||
|
|
||||||
|
// if the element is a textarea than route to the editable div
|
||||||
|
if (jQuery('#'+id).get(0).nodeName.toLowerCase() === 'textarea' ) {
|
||||||
|
id = id + '-aloha';
|
||||||
|
}
|
||||||
|
|
||||||
|
// serach all editables for id
|
||||||
|
for (var i = 0, editablesLength = Aloha.editables.length; i < editablesLength; i++) {
|
||||||
|
if (Aloha.editables[i].getId() == id) {
|
||||||
|
return Aloha.editables[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks wheater an object is a registered Aloha Editable.
|
||||||
|
* @param {jQuery} obj the jQuery object to be checked.
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
isEditable: function (obj) {
|
||||||
|
for (var i=0, editablesLength = Aloha.editables.length; i < editablesLength; i++) {
|
||||||
|
if ( Aloha.editables[i].originalObj.get(0) === obj ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs a message to the console
|
||||||
|
* @param level Level of the log ("error", "warn" or "info", "debug")
|
||||||
|
* @param component Component that calls the log
|
||||||
|
* @param message log message
|
||||||
|
* @return void
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
log: function(level, component, message) {
|
||||||
|
if (typeof Aloha.Log !== "undefined")
|
||||||
|
Aloha.Log.log(level, component, message);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the given editable
|
||||||
|
* @param editable editable to register
|
||||||
|
* @return void
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
registerEditable: function (editable) {
|
||||||
|
Aloha.editables.push(editable);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister the given editable. It will be deactivated and removed from editables.
|
||||||
|
* @param editable editable to unregister
|
||||||
|
* @return void
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
unregisterEditable: function (editable) {
|
||||||
|
|
||||||
|
// Find the index
|
||||||
|
var id = Aloha.editables.indexOf( editable );
|
||||||
|
// Remove it if really found!
|
||||||
|
if (id != -1) {
|
||||||
|
Aloha.editables.splice(id, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String representation
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
toString: function () {
|
||||||
|
return 'Aloha';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether at least one editable was modified
|
||||||
|
* @method
|
||||||
|
* @return {boolean} true when at least one editable was modified, false if not
|
||||||
|
*/
|
||||||
|
isModified: function () {
|
||||||
|
// check if something needs top be saved
|
||||||
|
for (var i = 0; i < Aloha.editables.length; i++) {
|
||||||
|
if (Aloha.editables[i].isModified && Aloha.editables[i].isModified()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the Aloha Url
|
||||||
|
* @method
|
||||||
|
* @return {String} alohaUrl
|
||||||
|
*/
|
||||||
|
getAlohaUrl: function( suffix ) {
|
||||||
|
// aloha base path is defined by a script tag with 2 data attributes
|
||||||
|
var requireJs = jQuery('[data-aloha-plugins]'),
|
||||||
|
baseUrl = ( requireJs.length ) ? requireJs[0].src.replace( /\/?aloha.js$/ , '' ) : '';
|
||||||
|
|
||||||
|
return baseUrl;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Plugin Url
|
||||||
|
* @method
|
||||||
|
* @param {String} name
|
||||||
|
* @return {String} url
|
||||||
|
*/
|
||||||
|
getPluginUrl: function (name) {
|
||||||
|
var url;
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
url = pluginPaths[name];
|
||||||
|
if(url) {
|
||||||
|
//Check if url is absolute and attach base url if it is not
|
||||||
|
if(!url.match("^(\/|http[s]?:).*")) {
|
||||||
|
url = Aloha.getAlohaUrl() + '/' + url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return Aloha;
|
||||||
|
});
|
194
vendor/assets/javascripts/locomotive/aloha/lib/aloha/ecma5.js
vendored
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
//Add ECMA262-5 method binding if not supported natively
|
||||||
|
//
|
||||||
|
if (!('bind' in Function.prototype)) {
|
||||||
|
Function.prototype.bind= function(owner) {
|
||||||
|
var that= this;
|
||||||
|
if (arguments.length<=1) {
|
||||||
|
return function() {
|
||||||
|
return that.apply(owner, arguments);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
var args= Array.prototype.slice.call(arguments, 1);
|
||||||
|
return function() {
|
||||||
|
return that.apply(owner, arguments.length===0? args : args.concat(Array.prototype.slice.call(arguments)));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add ECMA262-5 string trim if not supported natively
|
||||||
|
//
|
||||||
|
if (!('trim' in String.prototype)) {
|
||||||
|
String.prototype.trim= function() {
|
||||||
|
return this.replace(/^\s+/, '').replace(/\s+$/, '');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add ECMA262-5 Array methods if not supported natively
|
||||||
|
//
|
||||||
|
if (!('indexOf' in Array.prototype)) {
|
||||||
|
Array.prototype.indexOf= function(find, i /*opt*/) {
|
||||||
|
if (i===undefined) i= 0;
|
||||||
|
if (i<0) i+= this.length;
|
||||||
|
if (i<0) i= 0;
|
||||||
|
for (var n= this.length; i<n; i++)
|
||||||
|
if (i in this && this[i]===find)
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!('lastIndexOf' in Array.prototype)) {
|
||||||
|
Array.prototype.lastIndexOf= function(find, i /*opt*/) {
|
||||||
|
if (i===undefined) i= this.length-1;
|
||||||
|
if (i<0) i+= this.length;
|
||||||
|
if (i>this.length-1) i= this.length-1;
|
||||||
|
for (i++; i-->0;) /* i++ because from-argument is sadly inclusive */
|
||||||
|
if (i in this && this[i]===find)
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!('forEach' in Array.prototype)) {
|
||||||
|
Array.prototype.forEach= function(action, that /*opt*/) {
|
||||||
|
for (var i= 0, n= this.length; i<n; i++)
|
||||||
|
if (i in this)
|
||||||
|
action.call(that, this[i], i, this);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!('map' in Array.prototype)) {
|
||||||
|
Array.prototype.map= function(mapper, that /*opt*/) {
|
||||||
|
var other= new Array(this.length);
|
||||||
|
for (var i= 0, n= this.length; i<n; i++)
|
||||||
|
if (i in this)
|
||||||
|
other[i]= mapper.call(that, this[i], i, this);
|
||||||
|
return other;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!('filter' in Array.prototype)) {
|
||||||
|
Array.prototype.filter= function(filter, that /*opt*/) {
|
||||||
|
var other= [], v;
|
||||||
|
for (var i=0, n= this.length; i<n; i++)
|
||||||
|
if (i in this && filter.call(that, v= this[i], i, this))
|
||||||
|
other.push(v);
|
||||||
|
return other;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!('every' in Array.prototype)) {
|
||||||
|
Array.prototype.every= function(tester, that /*opt*/) {
|
||||||
|
for (var i= 0, n= this.length; i<n; i++)
|
||||||
|
if (i in this && !tester.call(that, this[i], i, this))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!('some' in Array.prototype)) {
|
||||||
|
Array.prototype.some= function(tester, that /*opt*/) {
|
||||||
|
for (var i= 0, n= this.length; i<n; i++)
|
||||||
|
if (i in this && tester.call(that, this[i], i, this))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!Node) {
|
||||||
|
// http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1841493061
|
||||||
|
var Node = {
|
||||||
|
'ELEMENT_NODE' : 1,
|
||||||
|
'ATTRIBUTE_NODE': 2,
|
||||||
|
'TEXT_NODE': 3,
|
||||||
|
'CDATA_SECTION_NODE': 4,
|
||||||
|
'ENTITY_REFERENCE_NODE': 5,
|
||||||
|
'ENTITY_NODE': 6,
|
||||||
|
'PROCESSING_INSTRUCTION_NODE': 7,
|
||||||
|
'COMMENT_NODE': 8,
|
||||||
|
'DOCUMENT_NODE': 9,
|
||||||
|
'DOCUMENT_TYPE_NODE': 10,
|
||||||
|
'DOCUMENT_FRAGMENT_NODE': 11,
|
||||||
|
'NOTATION_NODE': 12,
|
||||||
|
//The two nodes are disconnected. Order between disconnected nodes is always implementation-specific.
|
||||||
|
'DOCUMENT_POSITION_DISCONNECTED': 0x01,
|
||||||
|
//The second node precedes the reference node.
|
||||||
|
'DOCUMENT_POSITION_PRECEDING': 0x02,
|
||||||
|
//The node follows the reference node.
|
||||||
|
'DOCUMENT_POSITION_FOLLOWING': 0x04,
|
||||||
|
//The node contains the reference node. A node which contains is always preceding, too.
|
||||||
|
'DOCUMENT_POSITION_CONTAINS': 0x08,
|
||||||
|
//The node is contained by the reference node. A node which is contained is always following, too.
|
||||||
|
'DOCUMENT_POSITION_CONTAINED_BY': 0x10,
|
||||||
|
//The determination of preceding versus following is implementation-specific.
|
||||||
|
'DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC': 0x20
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-compareDocumentPosition
|
||||||
|
// FIXME: Check if the DOMNode prototype can be set.
|
||||||
|
window.compareDocumentPosition = function(node1, node2) {
|
||||||
|
|
||||||
|
if ('compareDocumentPosition' in document.documentElement ) {
|
||||||
|
return node1.compareDocumentPosition(node2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!("contains" in document.documentElement)) {
|
||||||
|
throw 'compareDocumentPosition nor contains is not supported by this browser.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node1 == node2) return 0;
|
||||||
|
|
||||||
|
//if they don't have the same parent, there's a disconnect
|
||||||
|
if (getRootParent(node1) != getRootParent(node2)) return 1;
|
||||||
|
|
||||||
|
//use this if both nodes have a sourceIndex (text nodes don't)
|
||||||
|
if ("sourceIndex" in node1 && "sourceIndex" in node2) {
|
||||||
|
return comparePosition(node1, node2);
|
||||||
|
}
|
||||||
|
|
||||||
|
//document will definitely contain the other node
|
||||||
|
if (node1 == document) return 20;
|
||||||
|
else if (node2 == document) return 10;
|
||||||
|
|
||||||
|
//get sourceIndexes to use for both nodes
|
||||||
|
var useNode1 = getUseNode(node1), useNode2 = getUseNode(node2);
|
||||||
|
|
||||||
|
//call this function again to get the result
|
||||||
|
var result = comparePosition(useNode1, useNode2);
|
||||||
|
|
||||||
|
//clean up if needed
|
||||||
|
if (node1 != useNode1) useNode1.parentNode.removeChild(useNode1);
|
||||||
|
if (node2 != useNode2) useNode2.parentNode.removeChild(useNode2);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
|
||||||
|
//node.ownerDocument gives the document object, which isn't the right info for a disconnect
|
||||||
|
function getRootParent(node) {
|
||||||
|
do { var parent = node; }
|
||||||
|
while (node = node.parentNode);
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Compare Position - MIT Licensed, John Resig; http://ejohn.org/blog/comparing-document-position/
|
||||||
|
//Already checked for equality and disconnect
|
||||||
|
function comparePosition(node1, node2) {
|
||||||
|
return (node1.contains(node2) && 16) +
|
||||||
|
(node2.contains(node1) && 8) +
|
||||||
|
(node1.sourceIndex >= 0 && node2.sourceIndex >= 0 ?
|
||||||
|
(node1.sourceIndex < node2.sourceIndex && 4) +
|
||||||
|
(node1.sourceIndex > node2.sourceIndex && 2) :
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//get a node with a sourceIndex to use
|
||||||
|
function getUseNode(node) {
|
||||||
|
//if the node already has a sourceIndex, use that node
|
||||||
|
if ("sourceIndex" in node) return node;
|
||||||
|
//otherwise, insert a comment (which has a sourceIndex but minimal DOM impact) before the node and use that
|
||||||
|
return node.parentNode.insertBefore(document.createComment(""), node);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!('getComputedStyle' in window)) {
|
||||||
|
window.getComputedStyle = function (node, style) {
|
||||||
|
if( node.currentStyle ) {
|
||||||
|
return node.currentStyle;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
280
vendor/assets/javascripts/locomotive/aloha/lib/aloha/ecma5shims.js
vendored
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
|
||||||
|
define([],
|
||||||
|
function(){
|
||||||
|
|
||||||
|
var shims = {
|
||||||
|
// Function bind
|
||||||
|
bind: function(owner){
|
||||||
|
var obj = this.obj || this;
|
||||||
|
var native_method = Function.prototype.bind;
|
||||||
|
var args= Array.prototype.slice.call(arguments, 1);
|
||||||
|
|
||||||
|
if(native_method){
|
||||||
|
return native_method.apply(obj, arguments);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return function() {
|
||||||
|
return obj.apply(owner, arguments.length===0? args : args.concat(Array.prototype.slice.call(arguments)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// String trim
|
||||||
|
trim: function(){
|
||||||
|
var obj = this.obj || this;
|
||||||
|
var native_method = String.prototype.trim;
|
||||||
|
|
||||||
|
if(native_method){
|
||||||
|
return native_method.call(obj);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return obj.replace(/^\s+/, '').replace(/\s+$/, '');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Array methods
|
||||||
|
indexOf: function(find, i /*opt*/){
|
||||||
|
var obj = this.obj || this;
|
||||||
|
var native_method = Array.prototype.indexOf;
|
||||||
|
|
||||||
|
if(native_method){
|
||||||
|
return native_method.call(obj, find, i);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (i===undefined) i= 0;
|
||||||
|
if (i<0) i+= obj.length;
|
||||||
|
if (i<0) i= 0;
|
||||||
|
for (var n = obj.length; i<n; i++)
|
||||||
|
if (i in obj && obj[i]===find)
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
forEach: function(action, that /*opt*/){
|
||||||
|
var obj = this.obj || this;
|
||||||
|
var native_method = Array.prototype.forEach;
|
||||||
|
|
||||||
|
if(native_method){
|
||||||
|
return native_method.call(obj, action, that);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (var i= 0, n = obj.length; i<n; i++)
|
||||||
|
if (i in obj)
|
||||||
|
action.call(that, obj[i], i, obj);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
map: function(mapper, that /*opt*/, chain /*opt */){
|
||||||
|
var obj = this.obj || this;
|
||||||
|
var native_method = Array.prototype.map;
|
||||||
|
var returnWrapper = (typeof arguments[arguments.length - 1] == "boolean") ? Array.prototype.pop.call(arguments) : false;
|
||||||
|
var result = [];
|
||||||
|
|
||||||
|
if(native_method){
|
||||||
|
result = native_method.call(obj, mapper, that);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var other= new Array(obj.length);
|
||||||
|
for (var i= 0, n= obj.length; i<n; i++)
|
||||||
|
if (i in obj)
|
||||||
|
other[i]= mapper.call(that, obj[i], i, obj);
|
||||||
|
result = other;
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnWrapper ? $_(result) : result;
|
||||||
|
},
|
||||||
|
|
||||||
|
filter: function(filterFunc, that /*opt*/, chain /*opt */){
|
||||||
|
var obj = this.obj || this;
|
||||||
|
var native_method = Array.prototype.filter;
|
||||||
|
var returnWrapper = (typeof arguments[arguments.length - 1] == "boolean") ? Array.prototype.pop.call(arguments) : false;
|
||||||
|
var result = [];
|
||||||
|
|
||||||
|
if(native_method){
|
||||||
|
result = native_method.call(obj, filterFunc, that);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var other= [], v;
|
||||||
|
for (var i=0, n= obj.length; i<n; i++)
|
||||||
|
if (i in obj && filterFunc.call(that, v= obj[i], i, obj))
|
||||||
|
other.push(v);
|
||||||
|
result = other;
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnWrapper ? $_(result) : result;
|
||||||
|
},
|
||||||
|
|
||||||
|
every: function(tester, that /*opt*/) {
|
||||||
|
var obj = this.obj || this;
|
||||||
|
var native_method = Array.prototype.every;
|
||||||
|
|
||||||
|
if(native_method){
|
||||||
|
return native_method.call(obj, tester, that);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (var i= 0, n= obj.length; i<n; i++)
|
||||||
|
if (i in obj && !tester.call(that, obj[i], i, obj))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
some: function(tester, that /*opt*/){
|
||||||
|
var obj = this.obj || this;
|
||||||
|
var native_method = Array.prototype.some;
|
||||||
|
|
||||||
|
if(native_method){
|
||||||
|
return native_method.call(obj, tester, that);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (var i= 0, n= obj.length; i<n; i++)
|
||||||
|
if (i in obj && tester.call(that, obj[i], i, obj))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Since IE7 doesn't support 'hasAttribute' method on nodes
|
||||||
|
// TODO: raise an exception if the object is not an node
|
||||||
|
hasAttribute: function(attr){
|
||||||
|
var obj = this.obj || this;
|
||||||
|
var native_method = obj.hasAttribute;
|
||||||
|
|
||||||
|
if(native_method){
|
||||||
|
return obj.hasAttribute(attr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return (typeof obj.attributes[attr] != "undefined")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
var $_ = function(obj) {
|
||||||
|
var wrapper = function() {};
|
||||||
|
wrapper.prototype = shims;
|
||||||
|
|
||||||
|
var wrapper_instance = new wrapper();
|
||||||
|
wrapper_instance.obj = obj;
|
||||||
|
return wrapper_instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var shim in shims) {
|
||||||
|
$_[shim] = shims[shim];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Node constants
|
||||||
|
// http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1841493061
|
||||||
|
if(typeof Node != 'undefined'){
|
||||||
|
$_.Node = Node;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$_.Node = {
|
||||||
|
'ELEMENT_NODE' : 1,
|
||||||
|
'ATTRIBUTE_NODE': 2,
|
||||||
|
'TEXT_NODE': 3,
|
||||||
|
'CDATA_SECTION_NODE': 4,
|
||||||
|
'ENTITY_REFERENCE_NODE': 5,
|
||||||
|
'ENTITY_NODE': 6,
|
||||||
|
'PROCESSING_INSTRUCTION_NODE': 7,
|
||||||
|
'COMMENT_NODE': 8,
|
||||||
|
'DOCUMENT_NODE': 9,
|
||||||
|
'DOCUMENT_TYPE_NODE': 10,
|
||||||
|
'DOCUMENT_FRAGMENT_NODE': 11,
|
||||||
|
'NOTATION_NODE': 12,
|
||||||
|
//The two nodes are disconnected. Order between disconnected nodes is always implementation-specific.
|
||||||
|
'DOCUMENT_POSITION_DISCONNECTED': 0x01,
|
||||||
|
//The second node precedes the reference node.
|
||||||
|
'DOCUMENT_POSITION_PRECEDING': 0x02,
|
||||||
|
//The node follows the reference node.
|
||||||
|
'DOCUMENT_POSITION_FOLLOWING': 0x04,
|
||||||
|
//The node contains the reference node. A node which contains is always preceding, too.
|
||||||
|
'DOCUMENT_POSITION_CONTAINS': 0x08,
|
||||||
|
//The node is contained by the reference node. A node which is contained is always following, too.
|
||||||
|
'DOCUMENT_POSITION_CONTAINED_BY': 0x10,
|
||||||
|
//The determination of preceding versus following is implementation-specific.
|
||||||
|
'DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC': 0x20
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-compareDocumentPosition
|
||||||
|
// FIXME: Check if the DOMNode prototype can be set.
|
||||||
|
$_.compareDocumentPosition = function(node1, node2) {
|
||||||
|
|
||||||
|
if ('compareDocumentPosition' in document.documentElement ) {
|
||||||
|
return node1.compareDocumentPosition(node2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!("contains" in document.documentElement)) {
|
||||||
|
throw 'compareDocumentPosition nor contains is not supported by this browser.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node1 == node2) return 0;
|
||||||
|
|
||||||
|
//if they don't have the same parent, there's a disconnect
|
||||||
|
if (getRootParent(node1) != getRootParent(node2)) return 1;
|
||||||
|
|
||||||
|
//use this if both nodes have a sourceIndex (text nodes don't)
|
||||||
|
if ("sourceIndex" in node1 && "sourceIndex" in node2) {
|
||||||
|
return comparePosition(node1, node2);
|
||||||
|
}
|
||||||
|
|
||||||
|
//document will definitely contain the other node
|
||||||
|
if (node1 == document) return 20;
|
||||||
|
else if (node2 == document) return 10;
|
||||||
|
|
||||||
|
//get sourceIndexes to use for both nodes
|
||||||
|
var useNode1 = getUseNode(node1), useNode2 = getUseNode(node2);
|
||||||
|
|
||||||
|
//call this function again to get the result
|
||||||
|
var result = comparePosition(useNode1, useNode2);
|
||||||
|
|
||||||
|
//clean up if needed
|
||||||
|
if (node1 != useNode1) useNode1.parentNode.removeChild(useNode1);
|
||||||
|
if (node2 != useNode2) useNode2.parentNode.removeChild(useNode2);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
|
||||||
|
//node.ownerDocument gives the document object, which isn't the right info for a disconnect
|
||||||
|
function getRootParent(node) {
|
||||||
|
do { var parent = node; }
|
||||||
|
while (node = node.parentNode);
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Compare Position - MIT Licensed, John Resig; http://ejohn.org/blog/comparing-document-position/
|
||||||
|
//Already checked for equality and disconnect
|
||||||
|
function comparePosition(node1, node2) {
|
||||||
|
return (node1.contains(node2) && 16) +
|
||||||
|
(node2.contains(node1) && 8) +
|
||||||
|
(node1.sourceIndex >= 0 && node2.sourceIndex >= 0 ?
|
||||||
|
(node1.sourceIndex < node2.sourceIndex && 4) +
|
||||||
|
(node1.sourceIndex > node2.sourceIndex && 2) :
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//get a node with a sourceIndex to use
|
||||||
|
function getUseNode(node) {
|
||||||
|
//if the node already has a sourceIndex, use that node
|
||||||
|
if ("sourceIndex" in node) return node;
|
||||||
|
//otherwise, insert a comment (which has a sourceIndex but minimal DOM impact) before the node and use that
|
||||||
|
return node.parentNode.insertBefore(document.createComment(""), node);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$_.getComputedStyle = function(node, style){
|
||||||
|
if('getComputedStyle' in window) {
|
||||||
|
return window.getComputedStyle(node, style);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if( node.currentStyle ) {
|
||||||
|
return node.currentStyle;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return $_;
|
||||||
|
});
|
878
vendor/assets/javascripts/locomotive/aloha/lib/aloha/editable.js
vendored
Normal file
@ -0,0 +1,878 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright © 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* ( at your option ) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define( [
|
||||||
|
'aloha/core',
|
||||||
|
'util/class',
|
||||||
|
'aloha/jquery',
|
||||||
|
'aloha/pluginmanager',
|
||||||
|
'aloha/floatingmenu',
|
||||||
|
'aloha/selection',
|
||||||
|
'aloha/markup',
|
||||||
|
'aloha/contenthandlermanager',
|
||||||
|
'aloha/console'
|
||||||
|
], function( Aloha, Class, jQuery, PluginManager, FloatingMenu, Selection,
|
||||||
|
Markup, ContentHandlerManager, console ) {
|
||||||
|
|
||||||
|
|
||||||
|
var unescape = window.unescape,
|
||||||
|
GENTICS = window.GENTICS,
|
||||||
|
|
||||||
|
// True, if the next editable activate event should not be handled
|
||||||
|
ignoreNextActivateEvent = false;
|
||||||
|
|
||||||
|
// default supported and custom content handler settings
|
||||||
|
// @TODO move to new config when implemented in Aloha
|
||||||
|
Aloha.defaults.contentHandler = {};
|
||||||
|
Aloha.defaults.contentHandler.initEditable = [ 'sanitize' ];
|
||||||
|
Aloha.defaults.contentHandler.getContents = [ 'sanitize' ];
|
||||||
|
|
||||||
|
// The insertHtml contenthandler ( paste ) will, by default, use all
|
||||||
|
// registered content handlers.
|
||||||
|
//Aloha.defaults.contentHandler.insertHtml = void 0;
|
||||||
|
|
||||||
|
if ( typeof Aloha.settings.contentHandler === 'undefined' ) {
|
||||||
|
Aloha.settings.contentHandler = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultContentSerializer = function(editableElement){
|
||||||
|
return jQuery(editableElement).html();
|
||||||
|
};
|
||||||
|
|
||||||
|
var contentSerializer = defaultContentSerializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Editable object
|
||||||
|
* @namespace Aloha
|
||||||
|
* @class Editable
|
||||||
|
* @method
|
||||||
|
* @constructor
|
||||||
|
* @param {Object} obj jQuery object reference to the object
|
||||||
|
*/
|
||||||
|
Aloha.Editable = Class.extend( {
|
||||||
|
|
||||||
|
_constructor: function( obj ) {
|
||||||
|
// check wheter the object has an ID otherwise generate and set
|
||||||
|
// globally unique ID
|
||||||
|
if ( !obj.attr( 'id' ) ) {
|
||||||
|
obj.attr( 'id', GENTICS.Utils.guid() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// store object reference
|
||||||
|
this.obj = obj;
|
||||||
|
this.originalObj = obj;
|
||||||
|
this.ready = false;
|
||||||
|
|
||||||
|
// delimiters, timer and idle for smartContentChange
|
||||||
|
// smartContentChange triggers -- tab: '\u0009' - space: '\u0020' - enter: 'Enter'
|
||||||
|
this.sccDelimiters = [ ':', ';', '.', '!', '?', ',',
|
||||||
|
unescape( '%u0009' ), unescape( '%u0020' ), 'Enter' ];
|
||||||
|
this.sccIdle = 5000;
|
||||||
|
this.sccDelay = 500;
|
||||||
|
this.sccTimerIdle = false;
|
||||||
|
this.sccTimerDelay = false;
|
||||||
|
|
||||||
|
// see keyset http://www.w3.org/TR/2007/WD-DOM-Level-3-Events-20071221/keyset.html
|
||||||
|
this.keyCodeMap = {
|
||||||
|
93 : "Apps", // The Application key
|
||||||
|
18 : "Alt", // The Alt ( Menu ) key.
|
||||||
|
20 : "CapsLock", // The Caps Lock ( Capital ) key.
|
||||||
|
17 : "Control", // The Control ( Ctrl ) key.
|
||||||
|
40 : "Down", // The Down Arrow key.
|
||||||
|
35 : "End", // The End key.
|
||||||
|
13 : "Enter", // The Enter key.
|
||||||
|
112 : "F1", // The F1 key.
|
||||||
|
113 : "F2", // The F2 key.
|
||||||
|
114 : "F3", // The F3 key.
|
||||||
|
115 : "F4", // The F4 key.
|
||||||
|
116 : "F5", // The F5 key.
|
||||||
|
117 : "F6", // The F6 key.
|
||||||
|
118 : "F7", // The F7 key.
|
||||||
|
119 : "F8", // The F8 key.
|
||||||
|
120 : "F9", // The F9 key.
|
||||||
|
121 : "F10", // The F10 key.
|
||||||
|
122 : "F11", // The F11 key.
|
||||||
|
123 : "F12", // The F12 key.
|
||||||
|
|
||||||
|
// Anybody knows the keycode for F13-F24?
|
||||||
|
36 : "Home", // The Home key.
|
||||||
|
45 : "Insert", // The Insert ( Ins ) key.
|
||||||
|
37 : "Left", // The Left Arrow key.
|
||||||
|
224 : "Meta", // The Meta key.
|
||||||
|
34 : "PageDown", // The Page Down ( Next ) key.
|
||||||
|
33 : "PageUp", // The Page Up key.
|
||||||
|
19 : "Pause", // The Pause key.
|
||||||
|
44 : "PrintScreen", // The Print Screen ( PrintScrn, SnapShot ) key.
|
||||||
|
39 : "Right", // The Right Arrow key.
|
||||||
|
145 : "Scroll", // The scroll lock key
|
||||||
|
16 : "Shift", // The Shift key.
|
||||||
|
38 : "Up", // The Up Arrow key.
|
||||||
|
91 : "Win", // The left Windows Logo key.
|
||||||
|
92 : "Win" // The right Windows Logo key.
|
||||||
|
};
|
||||||
|
|
||||||
|
this.placeholderClass = 'aloha-placeholder';
|
||||||
|
|
||||||
|
Aloha.registerEditable( this );
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the editable
|
||||||
|
* @return void
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
init: function() {
|
||||||
|
var me = this;
|
||||||
|
|
||||||
|
// TODO make editables their own settings.
|
||||||
|
this.settings = Aloha.settings;
|
||||||
|
|
||||||
|
// smartContentChange settings
|
||||||
|
// @TODO move to new config when implemented in Aloha
|
||||||
|
if ( Aloha.settings && Aloha.settings.smartContentChange ) {
|
||||||
|
if ( Aloha.settings.smartContentChange.delimiters ) {
|
||||||
|
this.sccDelimiters = Aloha.settings.smartContentChange.delimiters;
|
||||||
|
} /* else {
|
||||||
|
this.sccDelimiters = this.sccDelimiters;
|
||||||
|
} */
|
||||||
|
|
||||||
|
if ( Aloha.settings.smartContentChange.idle ) {
|
||||||
|
this.sccIdle = Aloha.settings.smartContentChange.idle;
|
||||||
|
} /* else {
|
||||||
|
this.sccIdle = this.sccIdle;
|
||||||
|
} */
|
||||||
|
|
||||||
|
if ( Aloha.settings.smartContentChange.delay ) {
|
||||||
|
this.sccDelay = Aloha.settings.smartContentChange.delay;
|
||||||
|
} /* else {
|
||||||
|
this.sccDelay = this.sccDelay;
|
||||||
|
} */
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if Aloha can handle the obj as Editable
|
||||||
|
if ( !this.check( this.obj ) ) {
|
||||||
|
//Aloha.log( 'warn', this, 'Aloha cannot handle {' + this.obj[0].nodeName + '}' );
|
||||||
|
this.destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply content handler to clean up content
|
||||||
|
var content = me.obj.html();
|
||||||
|
if ( typeof Aloha.settings.contentHandler.initEditable === 'undefined' ) {
|
||||||
|
Aloha.settings.contentHandler.initEditable = Aloha.defaults.contentHandler.initEditable;
|
||||||
|
}
|
||||||
|
content = ContentHandlerManager.handleContent( content, {
|
||||||
|
contenthandler: Aloha.settings.contentHandler.initEditable
|
||||||
|
} );
|
||||||
|
me.obj.html( content );
|
||||||
|
|
||||||
|
// only initialize the editable when Aloha is fully ready ( including plugins )
|
||||||
|
Aloha.bind( 'aloha-ready', function() {
|
||||||
|
// initialize the object
|
||||||
|
me.obj.addClass( 'aloha-editable' ).contentEditable( true );
|
||||||
|
|
||||||
|
// add focus event to the object to activate
|
||||||
|
me.obj.mousedown( function( e ) {
|
||||||
|
// check whether the mousedown was already handled
|
||||||
|
if ( !Aloha.eventHandled ) {
|
||||||
|
Aloha.eventHandled = true;
|
||||||
|
return me.activate( e );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
me.obj.mouseup( function( e ) {
|
||||||
|
Aloha.eventHandled = false;
|
||||||
|
} );
|
||||||
|
|
||||||
|
me.obj.focus( function( e ) {
|
||||||
|
return me.activate( e );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// by catching the keydown we can prevent the browser from doing its own thing
|
||||||
|
// if it does not handle the keyStroke it returns true and therefore all other
|
||||||
|
// events ( incl. browser's ) continue
|
||||||
|
me.obj.keydown( function( event ) {
|
||||||
|
me.keyCode = event.which;
|
||||||
|
return Markup.preProcessKeyStrokes( event );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// handle keypress
|
||||||
|
me.obj.keypress( function( event ) {
|
||||||
|
// triggers a smartContentChange to get the right charcode
|
||||||
|
// To test try http://www.w3.org/2002/09/tests/keys.html
|
||||||
|
Aloha.activeEditable.smartContentChange( event );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// handle shortcut keys
|
||||||
|
me.obj.keyup( function( event ) {
|
||||||
|
if ( event.keyCode === 27 ) {
|
||||||
|
Aloha.deactivateEditable();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
// register the onSelectionChange Event with the Editable field
|
||||||
|
me.obj.contentEditableSelectionChange( function( event ) {
|
||||||
|
Selection.onChange( me.obj, event );
|
||||||
|
return me.obj;
|
||||||
|
} );
|
||||||
|
|
||||||
|
// mark the editable as unmodified
|
||||||
|
me.setUnmodified();
|
||||||
|
|
||||||
|
// we don't do the sanitizing on aloha ready, since some plugins add elements into the content and bind events to it.
|
||||||
|
// if we sanitize by replacing the html, all events would get lost. TODO: think about a better solution for the sanitizing, without
|
||||||
|
// destroying the events
|
||||||
|
// // apply content handler to clean up content
|
||||||
|
// var content = me.obj.html();
|
||||||
|
// if ( typeof Aloha.settings.contentHandler.initEditable === 'undefined' ) {
|
||||||
|
// Aloha.settings.contentHandler.initEditable = Aloha.defaults.contentHandler.initEditable;
|
||||||
|
// }
|
||||||
|
// content = ContentHandlerManager.handleContent( content, {
|
||||||
|
// contenthandler: Aloha.settings.contentHandler.initEditable
|
||||||
|
// } );
|
||||||
|
// me.obj.html( content );
|
||||||
|
|
||||||
|
me.snapshotContent = me.getContents();
|
||||||
|
|
||||||
|
// FF bug: check for empty editable contents ( no <br>; no whitespace )
|
||||||
|
if ( jQuery.browser.mozilla ) {
|
||||||
|
me.initEmptyEditable();
|
||||||
|
}
|
||||||
|
|
||||||
|
me.initPlaceholder();
|
||||||
|
|
||||||
|
me.ready = true;
|
||||||
|
|
||||||
|
// throw a new event when the editable has been created
|
||||||
|
/**
|
||||||
|
* @event editableCreated fires after a new editable has been created, eg. via $( '#editme' ).aloha()
|
||||||
|
* The event is triggered in Aloha's global scope Aloha
|
||||||
|
* @param {Event} e the event object
|
||||||
|
* @param {Array} a an array which contains a reference to the currently created editable on its first position
|
||||||
|
*/
|
||||||
|
Aloha.trigger( 'aloha-editable-created', [ me ] );
|
||||||
|
} );
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True, if this editable is active for editing
|
||||||
|
* @property
|
||||||
|
* @type boolean
|
||||||
|
*/
|
||||||
|
isActive: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stores the original content to determine if it has been modified
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
originalContent: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* every time a selection is made in the current editable the selection has to
|
||||||
|
* be saved for further use
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
range: undefined,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if object can be edited by Aloha Editor
|
||||||
|
* @return {boolean } editable true if Aloha Editor can handle else false
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
check: function() {
|
||||||
|
/* TODO check those elements
|
||||||
|
'map', 'meter', 'object', 'output', 'progress', 'samp',
|
||||||
|
'time', 'area', 'datalist', 'figure', 'kbd', 'keygen',
|
||||||
|
'mark', 'math', 'wbr', 'area',
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Extract El
|
||||||
|
var me = this,
|
||||||
|
obj = this.obj,
|
||||||
|
el = obj.get( 0 ),
|
||||||
|
nodeName = el.nodeName.toLowerCase(),
|
||||||
|
|
||||||
|
// supported elements
|
||||||
|
textElements = [ 'a', 'abbr', 'address', 'article', 'aside',
|
||||||
|
'b', 'bdo', 'blockquote', 'cite', 'code', 'command',
|
||||||
|
'del', 'details', 'dfn', 'div', 'dl', 'em', 'footer',
|
||||||
|
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'i',
|
||||||
|
'ins', 'menu', 'nav', 'p', 'pre', 'q', 'ruby',
|
||||||
|
'section', 'small', 'span', 'strong', 'sub', 'sup',
|
||||||
|
'var' ],
|
||||||
|
i, div;
|
||||||
|
|
||||||
|
for ( i = 0; i < textElements.length; i++ ) {
|
||||||
|
if ( nodeName === textElements[ i ] ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// special handled elements
|
||||||
|
switch ( nodeName ) {
|
||||||
|
case 'label':
|
||||||
|
case 'button':
|
||||||
|
// TODO need some special handling.
|
||||||
|
break;
|
||||||
|
case 'textarea':
|
||||||
|
// Create a div alongside the textarea
|
||||||
|
div = jQuery( '<div id="' + this.getId() +
|
||||||
|
'-aloha" class="aloha-textarea" />' )
|
||||||
|
.insertAfter( obj );
|
||||||
|
|
||||||
|
// Resize the div to the textarea and
|
||||||
|
// Populate the div with the value of the textarea
|
||||||
|
// Then, hide the textarea
|
||||||
|
div.height( obj.height() )
|
||||||
|
.width( obj.width() )
|
||||||
|
.html( obj.val() );
|
||||||
|
|
||||||
|
obj.hide();
|
||||||
|
|
||||||
|
// Attach a onsubmit to the form to place the HTML of the
|
||||||
|
// div back into the textarea
|
||||||
|
obj.parents( 'form:first' ).submit( function() {
|
||||||
|
obj.val( me.getContents() );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Swap textarea reference with the new div
|
||||||
|
this.obj = div;
|
||||||
|
|
||||||
|
// Supported
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the following elements are not supported
|
||||||
|
/*
|
||||||
|
'canvas', 'audio', 'br', 'embed', 'fieldset', 'hgroup', 'hr',
|
||||||
|
'iframe', 'img', 'input', 'map', 'script', 'select', 'style',
|
||||||
|
'svg', 'table', 'ul', 'video', 'ol', 'form', 'noscript',
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init Placeholder
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
initPlaceholder: function() {
|
||||||
|
if ( Aloha.settings.placeholder && this.isEmpty() ) {
|
||||||
|
this.addPlaceholder();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the conteneditable is empty.
|
||||||
|
*
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
isEmpty: function() {
|
||||||
|
var editableTrimedContent = jQuery.trim( this.getContents() ),
|
||||||
|
onlyBrTag = ( editableTrimedContent === '<br>' ) ? true : false;
|
||||||
|
return ( editableTrimedContent.length === 0 || onlyBrTag );
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the editable div is not empty. Fixes a FF browser bug
|
||||||
|
* see issue: https://github.com/alohaeditor/Aloha-Editor/issues/269
|
||||||
|
*
|
||||||
|
* @return {undefined}
|
||||||
|
*/
|
||||||
|
initEmptyEditable: function( ) {
|
||||||
|
var obj = this.obj;
|
||||||
|
if ( this.empty( this.getContents() ) ) {
|
||||||
|
jQuery( obj ).prepend( '<br class="aloha-cleanme" />' );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add placeholder in editable
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
addPlaceholder: function() {
|
||||||
|
var div = jQuery( '<div>' ),
|
||||||
|
span = jQuery( '<span>' ),
|
||||||
|
el,
|
||||||
|
obj = this.obj;
|
||||||
|
|
||||||
|
if ( GENTICS.Utils.Dom.allowsNesting( obj[0], div[0] ) ) {
|
||||||
|
el = div;
|
||||||
|
} else {
|
||||||
|
el = span;
|
||||||
|
}
|
||||||
|
|
||||||
|
jQuery( obj ).append( el.addClass( this.placeholderClass ) );
|
||||||
|
jQuery.each(
|
||||||
|
Aloha.settings.placeholder,
|
||||||
|
function( selector, selectorConfig ) {
|
||||||
|
if ( obj.is( selector ) ) {
|
||||||
|
el.html( selectorConfig );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// remove browser br
|
||||||
|
jQuery( 'br', obj ).remove();
|
||||||
|
|
||||||
|
// delete div, span, el;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove placeholder from contenteditable. If setCursor is true,
|
||||||
|
* will also set the cursor to the start of the selection. However,
|
||||||
|
* this will be ASYNCHRONOUS, so if you rely on the fact that
|
||||||
|
* the placeholder is removed after calling this method, setCursor
|
||||||
|
* should be false ( or not set )
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
removePlaceholder: function( obj, setCursor ) {
|
||||||
|
var placeholderClass = this.placeholderClass,
|
||||||
|
range;
|
||||||
|
|
||||||
|
// // remove browser br
|
||||||
|
// jQuery( 'br', obj ).remove();
|
||||||
|
|
||||||
|
// set the cursor // remove placeholder
|
||||||
|
if ( setCursor === true ) {
|
||||||
|
range = Selection.getRangeObject();
|
||||||
|
if ( !range.select ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
range.startContainer = range.endContainer = obj.get( 0 );
|
||||||
|
range.startOffset = range.endOffset = 0;
|
||||||
|
range.select();
|
||||||
|
|
||||||
|
window.setTimeout( function() {
|
||||||
|
jQuery( '.' + placeholderClass, obj ).remove();
|
||||||
|
}, 20 );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
jQuery( '.' + placeholderClass, obj ).remove();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* destroy the editable
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
destroy: function() {
|
||||||
|
// leave the element just to get sure
|
||||||
|
if ( this === Aloha.getActiveEditable() ) {
|
||||||
|
this.blur();
|
||||||
|
|
||||||
|
// also hide the floating menu if the current editable was active
|
||||||
|
FloatingMenu.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
// special handled elements
|
||||||
|
switch ( this.originalObj.get( 0 ).nodeName.toLowerCase() ) {
|
||||||
|
case 'label':
|
||||||
|
case 'button':
|
||||||
|
// TODO need some special handling.
|
||||||
|
break;
|
||||||
|
case 'textarea':
|
||||||
|
// restore content to original textarea
|
||||||
|
this.originalObj.val( this.getContents() );
|
||||||
|
this.obj.remove();
|
||||||
|
this.originalObj.show();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now the editable is not ready any more
|
||||||
|
this.ready = false;
|
||||||
|
|
||||||
|
// remove the placeholder if needed.
|
||||||
|
this.removePlaceholder( this.obj );
|
||||||
|
|
||||||
|
// initialize the object and disable contentEditable
|
||||||
|
// unbind all events
|
||||||
|
// TODO should only unbind the specific handlers.
|
||||||
|
this.obj.removeClass( 'aloha-editable' )
|
||||||
|
.contentEditable( false )
|
||||||
|
.unbind( 'mousedown click dblclick focus keydown keypress keyup' );
|
||||||
|
|
||||||
|
/* TODO remove this event, it should implemented as bind and unbind
|
||||||
|
// register the onSelectionChange Event with the Editable field
|
||||||
|
this.obj.contentEditableSelectionChange( function( event ) {
|
||||||
|
Aloha.Selection.onChange( me.obj, event );
|
||||||
|
return me.obj;
|
||||||
|
} );
|
||||||
|
*/
|
||||||
|
|
||||||
|
// throw a new event when the editable has been created
|
||||||
|
/**
|
||||||
|
* @event editableCreated fires after a new editable has been destroyes, eg. via $( '#editme' ).mahalo()
|
||||||
|
* The event is triggered in Aloha's global scope Aloha
|
||||||
|
* @param {Event} e the event object
|
||||||
|
* @param {Array} a an array which contains a reference to the currently created editable on its first position
|
||||||
|
*/
|
||||||
|
Aloha.trigger( 'aloha-editable-destroyed', [ this ] );
|
||||||
|
|
||||||
|
// finally register the editable with Aloha
|
||||||
|
Aloha.unregisterEditable( this );
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* marks the editables current state as unmodified. Use this method to inform the editable
|
||||||
|
* that it's contents have been saved
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
|
setUnmodified: function() {
|
||||||
|
this.originalContent = this.getContents();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if the editable has been modified during the edit process#
|
||||||
|
* @method
|
||||||
|
* @return boolean true if the editable has been modified, false otherwise
|
||||||
|
*/
|
||||||
|
isModified: function() {
|
||||||
|
return this.originalContent !== this.getContents();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String representation of the object
|
||||||
|
* @method
|
||||||
|
* @return Aloha.Editable
|
||||||
|
*/
|
||||||
|
toString: function() {
|
||||||
|
return 'Aloha.Editable';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check whether the editable has been disabled
|
||||||
|
*/
|
||||||
|
isDisabled: function() {
|
||||||
|
return !this.obj.contentEditable()
|
||||||
|
|| this.obj.contentEditable() === 'false';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* disable this editable
|
||||||
|
* a disabled editable cannot be written on by keyboard
|
||||||
|
*/
|
||||||
|
disable: function() {
|
||||||
|
return this.isDisabled() || this.obj.contentEditable( false );
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enable this editable
|
||||||
|
* reenables a disabled editable to be writteable again
|
||||||
|
*/
|
||||||
|
enable: function() {
|
||||||
|
return this.isDisabled() && this.obj.contentEditable( true );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* activates an Editable for editing
|
||||||
|
* disables all other active items
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
|
activate: function( e ) {
|
||||||
|
// get active Editable before setting the new one.
|
||||||
|
var oldActive = Aloha.getActiveEditable();
|
||||||
|
|
||||||
|
// We need to ommit this call when this flag is set to true.
|
||||||
|
// This flag will only be set to true before the removePlaceholder method
|
||||||
|
// is called since that method invokes a focus event which will again trigger
|
||||||
|
// this method. We want to avoid double invokation of this method.
|
||||||
|
if ( ignoreNextActivateEvent ) {
|
||||||
|
ignoreNextActivateEvent = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle special case in which a nested editable is focused by a click
|
||||||
|
// in this case the "focus" event would be triggered on the parent element
|
||||||
|
// which actually shifts the focus away to it's parent. this if is here to
|
||||||
|
// prevent this situation
|
||||||
|
if ( e && e.type === 'focus' && oldActive !== null
|
||||||
|
&& oldActive.obj.parent().get( 0 ) === e.currentTarget ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// leave immediately if this is already the active editable
|
||||||
|
if ( this.isActive || this.isDisabled() ) {
|
||||||
|
// we don't want parent editables to be triggered as well, so return false
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.obj.addClass( 'aloha-editable-active' );
|
||||||
|
|
||||||
|
Aloha.activateEditable( this );
|
||||||
|
|
||||||
|
ignoreNextActivateEvent = true;
|
||||||
|
this.removePlaceholder ( this.obj, true );
|
||||||
|
ignoreNextActivateEvent = false;
|
||||||
|
|
||||||
|
this.isActive = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @event editableActivated fires after the editable has been activated by clicking on it.
|
||||||
|
* This event is triggered in Aloha's global scope Aloha
|
||||||
|
* @param {Event} e the event object
|
||||||
|
* @param {Array} a an array which contains a reference to last active editable on its first position, as well
|
||||||
|
* as the currently active editable on it's second position
|
||||||
|
*/
|
||||||
|
// trigger a 'general' editableActivated event
|
||||||
|
Aloha.trigger( 'aloha-editable-activated', {
|
||||||
|
'oldActive' : oldActive,
|
||||||
|
'editable' : this
|
||||||
|
} );
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handle the blur event
|
||||||
|
* this must not be attached to the blur event, which will trigger far too often
|
||||||
|
* eg. when a table within an editable is selected
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
blur: function() {
|
||||||
|
this.obj.blur();
|
||||||
|
this.isActive = false;
|
||||||
|
this.initPlaceholder();
|
||||||
|
this.obj.removeClass( 'aloha-editable-active' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @event editableDeactivated fires after the editable has been activated by clicking on it.
|
||||||
|
* This event is triggered in Aloha's global scope Aloha
|
||||||
|
* @param {Event} e the event object
|
||||||
|
* @param {Array} a an array which contains a reference to this editable
|
||||||
|
*/
|
||||||
|
Aloha.trigger( 'aloha-editable-deactivated', { editable : this } );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @event smartContentChanged
|
||||||
|
*/
|
||||||
|
Aloha.activeEditable.smartContentChange( { type : 'blur' }, null );
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if the string is empty
|
||||||
|
* used for zerowidth check
|
||||||
|
* @return true if empty or string is null, false otherwise
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
empty: function( str ) {
|
||||||
|
// br is needed for chrome
|
||||||
|
return ( null === str )
|
||||||
|
|| ( jQuery.trim( str ) === '' || str === '<br/>' );
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the contents of this editable as a HTML string
|
||||||
|
* @method
|
||||||
|
* @return contents of the editable
|
||||||
|
*/
|
||||||
|
getContents: function( asObject ) {
|
||||||
|
var clonedObj = this.obj.clone( false );
|
||||||
|
|
||||||
|
// do core cleanup
|
||||||
|
clonedObj.find( '.aloha-cleanme' ).remove();
|
||||||
|
this.removePlaceholder( clonedObj );
|
||||||
|
PluginManager.makeClean( clonedObj );
|
||||||
|
|
||||||
|
return asObject ? clonedObj.contents() : contentSerializer(clonedObj[0]);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the id of this editable
|
||||||
|
* @method
|
||||||
|
* @return id of this editable
|
||||||
|
*/
|
||||||
|
getId: function() {
|
||||||
|
return this.obj.attr( 'id' );
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates and signals a smartContentChange event.
|
||||||
|
*
|
||||||
|
* A smart content change occurs when a special editing action, or a
|
||||||
|
* combination of interactions are performed by the user during the
|
||||||
|
* course of editing within an editable.
|
||||||
|
* The smart content change event would therefore signal to any
|
||||||
|
* component that is listening to this event, that content has been
|
||||||
|
* inserted into the editable that may need to be prococessed in a
|
||||||
|
* special way
|
||||||
|
* This is used for smart actions within the content/while editing.
|
||||||
|
* @param {Event} event
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
smartContentChange: function( event ) {
|
||||||
|
var me = this,
|
||||||
|
uniChar = null,
|
||||||
|
re,
|
||||||
|
match;
|
||||||
|
|
||||||
|
// ignore meta keys like crtl+v or crtl+l and so on
|
||||||
|
if ( event && ( event.metaKey || event.crtlKey || event.altKey ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( event && event.originalEvent ) {
|
||||||
|
// regex to strip unicode
|
||||||
|
re = new RegExp( "U\\+(\\w{4})" );
|
||||||
|
match = re.exec( event.originalEvent.keyIdentifier );
|
||||||
|
|
||||||
|
// Use keyIdentifier if available
|
||||||
|
if ( event.originalEvent.keyIdentifier && 1 === 2 ) {
|
||||||
|
// @fixme: Because of "&& 1 === 2" above, all the below is
|
||||||
|
// unreachable code
|
||||||
|
if ( match !== null ) {
|
||||||
|
uniChar = unescape( '%u' + match[1] );
|
||||||
|
}
|
||||||
|
if ( uniChar === null ) {
|
||||||
|
uniChar = event.originalEvent.keyIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FF & Opera don't support keyIdentifier
|
||||||
|
} else {
|
||||||
|
// Use among browsers reliable which http://api.jquery.com/keypress
|
||||||
|
uniChar = ( this.keyCodeMap[ this.keyCode ] ||
|
||||||
|
String.fromCharCode( event.which ) || 'unknown' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle "Enter" -- it's not "U+1234" -- when returned via "event.originalEvent.keyIdentifier"
|
||||||
|
// reference: http://www.w3.org/TR/2007/WD-DOM-Level-3-Events-20071221/keyset.html
|
||||||
|
if ( jQuery.inArray( uniChar, this.sccDelimiters ) >= 0 ) {
|
||||||
|
clearTimeout( this.sccTimerIdle );
|
||||||
|
clearTimeout( this.sccTimerDelay );
|
||||||
|
|
||||||
|
this.sccTimerDelay = setTimeout( function() {
|
||||||
|
Aloha.trigger( 'aloha-smart-content-changed', {
|
||||||
|
'editable' : me,
|
||||||
|
'keyIdentifier' : event.originalEvent.keyIdentifier,
|
||||||
|
'keyCode' : event.keyCode,
|
||||||
|
'char' : uniChar,
|
||||||
|
'triggerType' : 'keypress', // keypress, timer, blur, paste
|
||||||
|
'snapshotContent' : me.getSnapshotContent()
|
||||||
|
} );
|
||||||
|
|
||||||
|
console.debug( 'Aloha.Editable',
|
||||||
|
'smartContentChanged: event type keypress triggered' );
|
||||||
|
/*
|
||||||
|
var r = Aloha.Selection.rangeObject;
|
||||||
|
if ( r.isCollapsed()
|
||||||
|
&& r.startContainer.nodeType == 3 ) {
|
||||||
|
|
||||||
|
var posDummy = jQuery( '<span id="GENTICS-Aloha-PosDummy" />' );
|
||||||
|
|
||||||
|
GENTICS.Utils.Dom.insertIntoDOM(
|
||||||
|
posDummy,
|
||||||
|
r,
|
||||||
|
this.obj,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log( posDummy.offset().top, posDummy.offset().left );
|
||||||
|
|
||||||
|
GENTICS.Utils.Dom.removeFromDOM(
|
||||||
|
posDummy,
|
||||||
|
r,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
r.select();
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}, this.sccDelay );
|
||||||
|
|
||||||
|
} else if ( event && event.type === 'paste' ) {
|
||||||
|
Aloha.trigger( 'aloha-smart-content-changed', {
|
||||||
|
'editable' : me,
|
||||||
|
'keyIdentifier' : null,
|
||||||
|
'keyCode' : null,
|
||||||
|
'char' : null,
|
||||||
|
'triggerType' : 'paste',
|
||||||
|
'snapshotContent' : me.getSnapshotContent()
|
||||||
|
} );
|
||||||
|
|
||||||
|
} else if ( event && event.type === 'blur' ) {
|
||||||
|
Aloha.trigger( 'aloha-smart-content-changed', {
|
||||||
|
'editable' : me,
|
||||||
|
'keyIdentifier' : null,
|
||||||
|
'keyCode' : null,
|
||||||
|
'char' : null,
|
||||||
|
'triggerType' : 'blur',
|
||||||
|
'snapshotContent' : me.getSnapshotContent()
|
||||||
|
} );
|
||||||
|
|
||||||
|
} else if ( uniChar !== null ) {
|
||||||
|
// in the rare case idle time is lower then delay time
|
||||||
|
clearTimeout( this.sccTimerDelay );
|
||||||
|
clearTimeout( this.sccTimerIdle );
|
||||||
|
this.sccTimerIdle = setTimeout( function() {
|
||||||
|
Aloha.trigger( 'aloha-smart-content-changed', {
|
||||||
|
'editable' : me,
|
||||||
|
'keyIdentifier' : null,
|
||||||
|
'keyCode' : null,
|
||||||
|
'char' : null,
|
||||||
|
'triggerType' : 'idle',
|
||||||
|
'snapshotContent' : me.getSnapshotContent()
|
||||||
|
} );
|
||||||
|
}, this.sccIdle );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a snapshot of the active editable as a HTML string
|
||||||
|
* @hide
|
||||||
|
* @return snapshot of the editable
|
||||||
|
*/
|
||||||
|
getSnapshotContent: function() {
|
||||||
|
var ret = this.snapshotContent;
|
||||||
|
this.snapshotContent = this.getContents();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the serializer function to be used for the contents of all editables.
|
||||||
|
*
|
||||||
|
* The default content serializer will just call the jQuery.html()
|
||||||
|
* function on the editable element (which gets the innerHTML property).
|
||||||
|
*
|
||||||
|
* This method is a static class method and will affect the result
|
||||||
|
* of editable.getContents() for all editables that have been or
|
||||||
|
* will be constructed.
|
||||||
|
*
|
||||||
|
* @param serializerFunction
|
||||||
|
* A function that accepts a DOM element and returns the serialized
|
||||||
|
* XHTML of the element contents (excluding the start and end tag of
|
||||||
|
* the passed element).
|
||||||
|
*/
|
||||||
|
Aloha.Editable.setContentSerializer = function( serializerFunction ) {
|
||||||
|
contentSerializer = serializerFunction;
|
||||||
|
};
|
||||||
|
} );
|
8306
vendor/assets/javascripts/locomotive/aloha/lib/aloha/engine.js
vendored
Normal file
86
vendor/assets/javascripts/locomotive/aloha/lib/aloha/ext-alohaproxy.js
vendored
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright <EFBFBD> 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*//*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define( [
|
||||||
|
'aloha/jquery',
|
||||||
|
'aloha/ext',
|
||||||
|
'aloha/repositorymanager',
|
||||||
|
'aloha/console',
|
||||||
|
'i18n!aloha/nls/i18n'
|
||||||
|
], function ( jQuery, Ext, RepositoryManager, console ) {
|
||||||
|
|
||||||
|
|
||||||
|
Ext.data.AlohaProxy = function () {
|
||||||
|
// Must define a dummy api with "read" action to satisfy
|
||||||
|
// Ext.data.Api#prepare *before* calling super
|
||||||
|
var api = {};
|
||||||
|
api[ Ext.data.Api.actions.read ] = true;
|
||||||
|
Ext.data.AlohaProxy.superclass.constructor.call( this, { api: api } );
|
||||||
|
|
||||||
|
this.params = {
|
||||||
|
queryString : null,
|
||||||
|
objectTypeFilter : null,
|
||||||
|
filter : null,
|
||||||
|
inFolderId : null,
|
||||||
|
orderBy : null,
|
||||||
|
maxItems : null,
|
||||||
|
skipCount : null,
|
||||||
|
renditionFilter : null,
|
||||||
|
repositoryId : null
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var i18n = Aloha.require( 'i18n!aloha/nls/i18n' );
|
||||||
|
|
||||||
|
Ext.extend( Ext.data.AlohaProxy, Ext.data.DataProxy, {
|
||||||
|
|
||||||
|
doRequest: function ( action, rs, params, reader, cb, scope, arg ) {
|
||||||
|
jQuery.extend( this.params, params );
|
||||||
|
|
||||||
|
try {
|
||||||
|
RepositoryManager.query( this.params, function ( items ) {
|
||||||
|
cb.call( scope, reader.readRecords( items ), arg, true );
|
||||||
|
} );
|
||||||
|
} catch ( ex ) {
|
||||||
|
console.error( 'Ext.data.AlohaProxy',
|
||||||
|
'An error occured while querying repositories.' );
|
||||||
|
|
||||||
|
this.fireEvent( 'loadexception', this, null, arg, ex );
|
||||||
|
this.fireEvent( 'exception', this, 'response', action, arg, null, ex );
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setObjectTypeFilter: function ( otFilter ) {
|
||||||
|
this.params.objectTypeFilter = otFilter;
|
||||||
|
},
|
||||||
|
|
||||||
|
getObjectTypeFilter: function () {
|
||||||
|
return this.params.objectTypeFilter;
|
||||||
|
},
|
||||||
|
|
||||||
|
setParams: function ( p ) {
|
||||||
|
jQuery.extend( this.params, p );
|
||||||
|
}
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
} );
|
50
vendor/assets/javascripts/locomotive/aloha/lib/aloha/ext-alohareader.js
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright <EFBFBD> 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*//*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
['aloha/ext'],
|
||||||
|
function(Ext) {
|
||||||
|
|
||||||
|
|
||||||
|
Ext.data.AlohaObjectReader = function(meta, recordType) {
|
||||||
|
meta = {};
|
||||||
|
Ext.applyIf(meta, {
|
||||||
|
idProperty: 'id',
|
||||||
|
root: 'items',
|
||||||
|
totalProperty: 'results',
|
||||||
|
// TODO implement all defined optional attributes
|
||||||
|
fields: [
|
||||||
|
'id',
|
||||||
|
'url',
|
||||||
|
'name',
|
||||||
|
'type',
|
||||||
|
'weight',
|
||||||
|
'path',
|
||||||
|
'repositoryId'
|
||||||
|
]
|
||||||
|
});
|
||||||
|
Ext.data.JsonReader.superclass.constructor.call(this, meta, meta.fields);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ext.extend(Ext.data.AlohaObjectReader, Ext.data.JsonReader, {
|
||||||
|
// extend of necessary
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
77
vendor/assets/javascripts/locomotive/aloha/lib/aloha/ext-alohatreeloader.js
vendored
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright (c) 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*//*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
['aloha/ext', 'aloha/repositorymanager'],
|
||||||
|
function(Ext, RepositoryManager) {
|
||||||
|
|
||||||
|
|
||||||
|
Ext.tree.AlohaTreeLoader = function(config) {
|
||||||
|
Ext.apply(this, config);
|
||||||
|
Ext.tree.AlohaTreeLoader.superclass.constructor.call(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ext.extend( Ext.tree.AlohaTreeLoader, Ext.tree.TreeLoader, {
|
||||||
|
paramOrder: ['node', 'id'],
|
||||||
|
nodeParameter: 'id',
|
||||||
|
directFn : function(node, id, callback) {
|
||||||
|
var
|
||||||
|
params = {
|
||||||
|
inFolderId: node.id,
|
||||||
|
objectTypeFilter: this.objectTypeFilter,
|
||||||
|
repositoryId: node.repositoryId
|
||||||
|
};
|
||||||
|
|
||||||
|
RepositoryManager.getChildren ( params, function( items ) {
|
||||||
|
var response = {};
|
||||||
|
|
||||||
|
response = {
|
||||||
|
status: true,
|
||||||
|
scope: this,
|
||||||
|
argument: {callback: callback, node: node}
|
||||||
|
};
|
||||||
|
|
||||||
|
if(typeof callback === 'function'){
|
||||||
|
callback(items, response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
createNode: function(node) {
|
||||||
|
if ( node.name ) {
|
||||||
|
node.text = node.name;
|
||||||
|
}
|
||||||
|
if ( node.hasMoreItems ) {
|
||||||
|
node.leaf = !node.hasMoreItems;
|
||||||
|
}
|
||||||
|
if ( node.objectType ) {
|
||||||
|
node.cls = node.objectType;
|
||||||
|
}
|
||||||
|
return Ext.tree.TreeLoader.prototype.createNode.call(this, node);
|
||||||
|
},
|
||||||
|
objectTypeFilter : null,
|
||||||
|
setObjectTypeFilter : function (otFilter) {
|
||||||
|
this.objectTypeFilter = otFilter;
|
||||||
|
},
|
||||||
|
getObjectTypeFilter : function () {
|
||||||
|
return this.objectTypeFilter;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
32
vendor/assets/javascripts/locomotive/aloha/lib/aloha/ext.js
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright (c) 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*//*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define('aloha/ext',[], function() {
|
||||||
|
|
||||||
|
// Ext seems to have an onClick handler that uses
|
||||||
|
// QuickTips, but the handler doesn't initialize
|
||||||
|
// QuickTips and therefore causes an error.
|
||||||
|
// The bug occurred with the Gentics Content Node
|
||||||
|
// integration, but if it's really a bug in Ext, then
|
||||||
|
// it's a good idea to always initialize QuickTips here.
|
||||||
|
Ext.QuickTips.init();
|
||||||
|
|
||||||
|
return Ext;
|
||||||
|
});
|
1286
vendor/assets/javascripts/locomotive/aloha/lib/aloha/floatingmenu.js
vendored
Normal file
546
vendor/assets/javascripts/locomotive/aloha/lib/aloha/ierange-m2.js
vendored
Normal file
@ -0,0 +1,546 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor
|
||||||
|
* Author & Copyright (c) 2010 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.com/license.html
|
||||||
|
*/
|
||||||
|
(function(window, undefined) {
|
||||||
|
|
||||||
|
var
|
||||||
|
jQuery = window.alohaQuery || window.jQuery, $ = jQuery,
|
||||||
|
// GENTICS = window.GENTICS,
|
||||||
|
// Aloha = window.Aloha,
|
||||||
|
DOMUtils, TextRangeUtils, selection, DOMRange, RangeIterator, DOMSelection;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only execute the following code if we are in IE (check for
|
||||||
|
* document.attachEvent, this is a microsoft event and therefore only available
|
||||||
|
* in IE).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(document.attachEvent && document.selection) {
|
||||||
|
/*!
|
||||||
|
* DOM Ranges for Internet Explorer (m2)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Tim Cameron Ryan
|
||||||
|
* Released under the MIT/X License
|
||||||
|
* available at http://code.google.com/p/ierange/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Range reference:
|
||||||
|
http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html
|
||||||
|
http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsRange.cpp
|
||||||
|
https://developer.mozilla.org/En/DOM:Range
|
||||||
|
Selection reference:
|
||||||
|
http://trac.webkit.org/browser/trunk/WebCore/page/DOMSelection.cpp
|
||||||
|
TextRange reference:
|
||||||
|
http://msdn.microsoft.com/en-us/library/ms535872.aspx
|
||||||
|
Other links:
|
||||||
|
http://jorgenhorstink.nl/test/javascript/range/range.js
|
||||||
|
http://jorgenhorstink.nl/2006/07/05/dom-range-implementation-in-ecmascript-completed/
|
||||||
|
http://dylanschiemann.com/articles/dom2Range/dom2RangeExamples.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
//[TODO] better exception support
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
DOM functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
DOMUtils = {
|
||||||
|
findChildPosition: function (node) {
|
||||||
|
for (var i = 0; node = node.previousSibling; i++)
|
||||||
|
continue;
|
||||||
|
return i;
|
||||||
|
},
|
||||||
|
isDataNode: function (node) {
|
||||||
|
return node && node.nodeValue !== null && node.data !== null;
|
||||||
|
},
|
||||||
|
isAncestorOf: function (parent, node) {
|
||||||
|
return !DOMUtils.isDataNode(parent) &&
|
||||||
|
(parent.contains(DOMUtils.isDataNode(node) ? node.parentNode : node) ||
|
||||||
|
node.parentNode == parent);
|
||||||
|
},
|
||||||
|
isAncestorOrSelf: function (root, node) {
|
||||||
|
return DOMUtils.isAncestorOf(root, node) || root == node;
|
||||||
|
},
|
||||||
|
findClosestAncestor: function (root, node) {
|
||||||
|
if (DOMUtils.isAncestorOf(root, node))
|
||||||
|
while (node && node.parentNode != root)
|
||||||
|
node = node.parentNode;
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
getNodeLength: function (node) {
|
||||||
|
return DOMUtils.isDataNode(node) ? node.length : node.childNodes.length;
|
||||||
|
},
|
||||||
|
splitDataNode: function (node, offset) {
|
||||||
|
if (!DOMUtils.isDataNode(node))
|
||||||
|
return false;
|
||||||
|
var newNode = node.cloneNode(false);
|
||||||
|
node.deleteData(offset, node.length);
|
||||||
|
newNode.deleteData(0, offset);
|
||||||
|
node.parentNode.insertBefore(newNode, node.nextSibling);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Text Range utilities
|
||||||
|
functions to simplify text range manipulation in ie
|
||||||
|
*/
|
||||||
|
|
||||||
|
TextRangeUtils = {
|
||||||
|
convertToDOMRange: function (textRange, document) {
|
||||||
|
var domRange,adoptBoundary;
|
||||||
|
|
||||||
|
adoptBoundary = function(domRange, textRange, bStart) {
|
||||||
|
// iterate backwards through parent element to find anchor location
|
||||||
|
var cursorNode = document.createElement('a'),
|
||||||
|
cursor = textRange.duplicate(),
|
||||||
|
parent;
|
||||||
|
|
||||||
|
cursor.collapse(bStart);
|
||||||
|
parent = cursor.parentElement();
|
||||||
|
do {
|
||||||
|
parent.insertBefore(cursorNode, cursorNode.previousSibling);
|
||||||
|
cursor.moveToElementText(cursorNode);
|
||||||
|
} while (cursor.compareEndPoints(bStart ? 'StartToStart' : 'StartToEnd', textRange) > 0 && cursorNode.previousSibling);
|
||||||
|
|
||||||
|
// when we exceed or meet the cursor, we've found the node
|
||||||
|
if (cursor.compareEndPoints(bStart ? 'StartToStart' : 'StartToEnd', textRange) == -1 && cursorNode.nextSibling) {
|
||||||
|
// data node
|
||||||
|
cursor.setEndPoint(bStart ? 'EndToStart' : 'EndToEnd', textRange);
|
||||||
|
domRange[bStart ? 'setStart' : 'setEnd'](cursorNode.nextSibling, cursor.text.length);
|
||||||
|
} else {
|
||||||
|
// element
|
||||||
|
domRange[bStart ? 'setStartBefore' : 'setEndBefore'](cursorNode);
|
||||||
|
}
|
||||||
|
cursorNode.parentNode.removeChild(cursorNode);
|
||||||
|
};
|
||||||
|
|
||||||
|
// return a DOM range
|
||||||
|
domRange = new DOMRange(document);
|
||||||
|
adoptBoundary(domRange, textRange, true);
|
||||||
|
adoptBoundary(domRange, textRange, false);
|
||||||
|
return domRange;
|
||||||
|
},
|
||||||
|
|
||||||
|
convertFromDOMRange: function (domRange) {
|
||||||
|
function adoptEndPoint(textRange, domRange, bStart) {
|
||||||
|
// find anchor node and offset
|
||||||
|
var container = domRange[bStart ? 'startContainer' : 'endContainer'],
|
||||||
|
offset = domRange[bStart ? 'startOffset' : 'endOffset'], textOffset = 0,
|
||||||
|
anchorNode = DOMUtils.isDataNode(container) ? container : container.childNodes[offset],
|
||||||
|
anchorParent = DOMUtils.isDataNode(container) ? container.parentNode : container,
|
||||||
|
cursorNode, cursor;
|
||||||
|
|
||||||
|
// visible data nodes need a text offset
|
||||||
|
if (container.nodeType == 3 || container.nodeType == 4) {
|
||||||
|
textOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a cursor element node to position range (since we can't select text nodes)
|
||||||
|
cursorNode = domRange._document.createElement('a');
|
||||||
|
anchorParent.insertBefore(cursorNode, anchorNode);
|
||||||
|
cursor = domRange._document.body.createTextRange();
|
||||||
|
cursor.moveToElementText(cursorNode);
|
||||||
|
cursorNode.parentNode.removeChild(cursorNode);
|
||||||
|
// move range
|
||||||
|
textRange.setEndPoint(bStart ? 'StartToStart' : 'EndToStart', cursor);
|
||||||
|
textRange[bStart ? 'moveStart' : 'moveEnd']('character', textOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return an IE text range
|
||||||
|
var textRange = domRange._document.body.createTextRange();
|
||||||
|
adoptEndPoint(textRange, domRange, true);
|
||||||
|
adoptEndPoint(textRange, domRange, false);
|
||||||
|
return textRange;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
DOM Range
|
||||||
|
*/
|
||||||
|
DOMRange = function(document) {
|
||||||
|
// save document parameter
|
||||||
|
this._document = document;
|
||||||
|
|
||||||
|
// initialize range
|
||||||
|
//[TODO] this should be located at document[0], document[0]
|
||||||
|
this.startContainer = this.endContainer = document.body;
|
||||||
|
this.endOffset = DOMUtils.getNodeLength(document.body);
|
||||||
|
};
|
||||||
|
|
||||||
|
DOMRange.START_TO_START = 0;
|
||||||
|
DOMRange.START_TO_END = 1;
|
||||||
|
DOMRange.END_TO_END = 2;
|
||||||
|
DOMRange.END_TO_START = 3;
|
||||||
|
|
||||||
|
DOMRange.prototype = {
|
||||||
|
// public properties
|
||||||
|
startContainer: null,
|
||||||
|
startOffset: 0,
|
||||||
|
endContainer: null,
|
||||||
|
endOffset: 0,
|
||||||
|
commonAncestorContainer: null,
|
||||||
|
collapsed: false,
|
||||||
|
// private properties
|
||||||
|
_document: null,
|
||||||
|
|
||||||
|
// private methods
|
||||||
|
_refreshProperties: function () {
|
||||||
|
// collapsed attribute
|
||||||
|
this.collapsed = (this.startContainer == this.endContainer && this.startOffset == this.endOffset);
|
||||||
|
// find common ancestor
|
||||||
|
var node = this.startContainer;
|
||||||
|
while (node && node != this.endContainer && !DOMUtils.isAncestorOf(node, this.endContainer))
|
||||||
|
node = node.parentNode;
|
||||||
|
this.commonAncestorContainer = node;
|
||||||
|
},
|
||||||
|
|
||||||
|
// range methods
|
||||||
|
//[TODO] collapse if start is after end, end is before start
|
||||||
|
setStart: function(container, offset) {
|
||||||
|
this.startContainer = container;
|
||||||
|
this.startOffset = offset;
|
||||||
|
this._refreshProperties();
|
||||||
|
},
|
||||||
|
setEnd: function(container, offset) {
|
||||||
|
this.endContainer = container;
|
||||||
|
this.endOffset = offset;
|
||||||
|
this._refreshProperties();
|
||||||
|
},
|
||||||
|
setStartBefore: function (refNode) {
|
||||||
|
// set start to beore this node
|
||||||
|
this.setStart(refNode.parentNode, DOMUtils.findChildPosition(refNode));
|
||||||
|
},
|
||||||
|
setStartAfter: function (refNode) {
|
||||||
|
// select next sibling
|
||||||
|
this.setStart(refNode.parentNode, DOMUtils.findChildPosition(refNode) + 1);
|
||||||
|
},
|
||||||
|
setEndBefore: function (refNode) {
|
||||||
|
// set end to beore this node
|
||||||
|
this.setEnd(refNode.parentNode, DOMUtils.findChildPosition(refNode));
|
||||||
|
},
|
||||||
|
setEndAfter: function (refNode) {
|
||||||
|
// select next sibling
|
||||||
|
this.setEnd(refNode.parentNode, DOMUtils.findChildPosition(refNode) + 1);
|
||||||
|
},
|
||||||
|
selectNode: function (refNode) {
|
||||||
|
this.setStartBefore(refNode);
|
||||||
|
this.setEndAfter(refNode);
|
||||||
|
},
|
||||||
|
selectNodeContents: function (refNode) {
|
||||||
|
this.setStart(refNode, 0);
|
||||||
|
this.setEnd(refNode, DOMUtils.getNodeLength(refNode));
|
||||||
|
},
|
||||||
|
collapse: function (toStart) {
|
||||||
|
if (toStart)
|
||||||
|
this.setEnd(this.startContainer, this.startOffset);
|
||||||
|
else
|
||||||
|
this.setStart(this.endContainer, this.endOffset);
|
||||||
|
},
|
||||||
|
|
||||||
|
// editing methods
|
||||||
|
cloneContents: function () {
|
||||||
|
// clone subtree
|
||||||
|
return (function cloneSubtree(iterator) {
|
||||||
|
for (var node, frag = document.createDocumentFragment(); node = iterator.next(); ) {
|
||||||
|
node = node.cloneNode(!iterator.hasPartialSubtree());
|
||||||
|
if (iterator.hasPartialSubtree())
|
||||||
|
node.appendChild(cloneSubtree(iterator.getSubtreeIterator()));
|
||||||
|
frag.appendChild(node);
|
||||||
|
}
|
||||||
|
return frag;
|
||||||
|
})(new RangeIterator(this));
|
||||||
|
},
|
||||||
|
extractContents: function () {
|
||||||
|
// cache range and move anchor points
|
||||||
|
var range = this.cloneRange();
|
||||||
|
if (this.startContainer != this.commonAncestorContainer)
|
||||||
|
this.setStartAfter(DOMUtils.findClosestAncestor(this.commonAncestorContainer, this.startContainer));
|
||||||
|
this.collapse(true);
|
||||||
|
// extract range
|
||||||
|
return (function extractSubtree(iterator) {
|
||||||
|
for (var node, frag = document.createDocumentFragment(); node = iterator.next(); ) {
|
||||||
|
if ( iterator.hasPartialSubtree() ) {
|
||||||
|
node = node.cloneNode(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
if (iterator.hasPartialSubtree())
|
||||||
|
node.appendChild(extractSubtree(iterator.getSubtreeIterator()));
|
||||||
|
frag.appendChild(node);
|
||||||
|
}
|
||||||
|
return frag;
|
||||||
|
})(new RangeIterator(range));
|
||||||
|
},
|
||||||
|
deleteContents: function () {
|
||||||
|
// cache range and move anchor points
|
||||||
|
var range = this.cloneRange();
|
||||||
|
if (this.startContainer != this.commonAncestorContainer)
|
||||||
|
this.setStartAfter(DOMUtils.findClosestAncestor(this.commonAncestorContainer, this.startContainer));
|
||||||
|
this.collapse(true);
|
||||||
|
// delete range
|
||||||
|
(function deleteSubtree(iterator) {
|
||||||
|
while (iterator.next()) {
|
||||||
|
if ( iterator.hasPartialSubtree() ) {
|
||||||
|
deleteSubtree(iterator.getSubtreeIterator());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})(new RangeIterator(range));
|
||||||
|
},
|
||||||
|
insertNode: function (newNode) {
|
||||||
|
// set original anchor and insert node
|
||||||
|
if (DOMUtils.isDataNode(this.startContainer)) {
|
||||||
|
DOMUtils.splitDataNode(this.startContainer, this.startOffset);
|
||||||
|
this.startContainer.parentNode.insertBefore(newNode, this.startContainer.nextSibling);
|
||||||
|
} else {
|
||||||
|
this.startContainer.insertBefore(newNode, this.startContainer.childNodes[this.startOffset]);
|
||||||
|
}
|
||||||
|
// resync start anchor
|
||||||
|
this.setStart(this.startContainer, this.startOffset);
|
||||||
|
},
|
||||||
|
surroundContents: function (newNode) {
|
||||||
|
// extract and surround contents
|
||||||
|
var content = this.extractContents();
|
||||||
|
this.insertNode(newNode);
|
||||||
|
newNode.appendChild(content);
|
||||||
|
this.selectNode(newNode);
|
||||||
|
},
|
||||||
|
|
||||||
|
// other methods
|
||||||
|
compareBoundaryPoints: function (how, sourceRange) {
|
||||||
|
// get anchors
|
||||||
|
var containerA, offsetA, containerB, offsetB;
|
||||||
|
switch (how) {
|
||||||
|
case DOMRange.START_TO_START:
|
||||||
|
case DOMRange.START_TO_END:
|
||||||
|
containerA = this.startContainer;
|
||||||
|
offsetA = this.startOffset;
|
||||||
|
break;
|
||||||
|
case DOMRange.END_TO_END:
|
||||||
|
case DOMRange.END_TO_START:
|
||||||
|
containerA = this.endContainer;
|
||||||
|
offsetA = this.endOffset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (how) {
|
||||||
|
case DOMRange.START_TO_START:
|
||||||
|
case DOMRange.END_TO_START:
|
||||||
|
containerB = sourceRange.startContainer;
|
||||||
|
offsetB = sourceRange.startOffset;
|
||||||
|
break;
|
||||||
|
case DOMRange.START_TO_END:
|
||||||
|
case DOMRange.END_TO_END:
|
||||||
|
containerB = sourceRange.endContainer;
|
||||||
|
offsetB = sourceRange.endOffset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare
|
||||||
|
return containerA.sourceIndex < containerB.sourceIndex ? -1 :
|
||||||
|
containerA.sourceIndex == containerB.sourceIndex ?
|
||||||
|
offsetA < offsetB ? -1 : offsetA == offsetB ? 0 : 1
|
||||||
|
: 1;
|
||||||
|
},
|
||||||
|
cloneRange: function () {
|
||||||
|
// return cloned range
|
||||||
|
var range = new DOMRange(this._document);
|
||||||
|
range.setStart(this.startContainer, this.startOffset);
|
||||||
|
range.setEnd(this.endContainer, this.endOffset);
|
||||||
|
return range;
|
||||||
|
},
|
||||||
|
detach: function () {
|
||||||
|
//[TODO] Releases Range from use to improve performance.
|
||||||
|
},
|
||||||
|
toString: function () {
|
||||||
|
return TextRangeUtils.convertFromDOMRange(this).text;
|
||||||
|
},
|
||||||
|
createContextualFragment: function (tagString) {
|
||||||
|
// parse the tag string in a context node
|
||||||
|
var
|
||||||
|
content = (DOMUtils.isDataNode(this.startContainer) ? this.startContainer.parentNode : this.startContainer).cloneNode(false),
|
||||||
|
fragment;
|
||||||
|
|
||||||
|
content.innerHTML = tagString;
|
||||||
|
// return a document fragment from the created node
|
||||||
|
for (fragment = this._document.createDocumentFragment(); content.firstChild; )
|
||||||
|
fragment.appendChild(content.firstChild);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Range iterator
|
||||||
|
*/
|
||||||
|
RangeIterator = function(range) {
|
||||||
|
this.range = range;
|
||||||
|
if (range.collapsed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//[TODO] ensure this works
|
||||||
|
// get anchors
|
||||||
|
var root = range.commonAncestorContainer;
|
||||||
|
this._next = range.startContainer == root && !DOMUtils.isDataNode(range.startContainer) ?
|
||||||
|
range.startContainer.childNodes[range.startOffset] :
|
||||||
|
DOMUtils.findClosestAncestor(root, range.startContainer);
|
||||||
|
this._end = range.endContainer == root && !DOMUtils.isDataNode(range.endContainer) ?
|
||||||
|
range.endContainer.childNodes[range.endOffset] :
|
||||||
|
DOMUtils.findClosestAncestor(root, range.endContainer).nextSibling;
|
||||||
|
};
|
||||||
|
|
||||||
|
RangeIterator.prototype = {
|
||||||
|
// public properties
|
||||||
|
range: null,
|
||||||
|
// private properties
|
||||||
|
_current: null,
|
||||||
|
_next: null,
|
||||||
|
_end: null,
|
||||||
|
|
||||||
|
// public methods
|
||||||
|
hasNext: function () {
|
||||||
|
return !!this._next;
|
||||||
|
},
|
||||||
|
next: function () {
|
||||||
|
// move to next node
|
||||||
|
var current = this._current = this._next;
|
||||||
|
this._next = this._current && this._current.nextSibling != this._end ?
|
||||||
|
this._current.nextSibling : null;
|
||||||
|
|
||||||
|
// check for partial text nodes
|
||||||
|
if (DOMUtils.isDataNode(this._current)) {
|
||||||
|
if (this.range.endContainer == this._current)
|
||||||
|
(current = current.cloneNode(true)).deleteData(this.range.endOffset, current.length - this.range.endOffset);
|
||||||
|
if (this.range.startContainer == this._current)
|
||||||
|
(current = current.cloneNode(true)).deleteData(0, this.range.startOffset);
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
},
|
||||||
|
remove: function () {
|
||||||
|
var end, start;
|
||||||
|
// check for partial text nodes
|
||||||
|
if (DOMUtils.isDataNode(this._current) &&
|
||||||
|
(this.range.startContainer == this._current || this.range.endContainer == this._current)) {
|
||||||
|
start = this.range.startContainer == this._current ? this.range.startOffset : 0;
|
||||||
|
end = this.range.endContainer == this._current ? this.range.endOffset : this._current.length;
|
||||||
|
this._current.deleteData(start, end - start);
|
||||||
|
} else
|
||||||
|
this._current.parentNode.removeChild(this._current);
|
||||||
|
},
|
||||||
|
hasPartialSubtree: function () {
|
||||||
|
// check if this node be partially selected
|
||||||
|
return !DOMUtils.isDataNode(this._current) &&
|
||||||
|
(DOMUtils.isAncestorOrSelf(this._current, this.range.startContainer) ||
|
||||||
|
DOMUtils.isAncestorOrSelf(this._current, this.range.endContainer));
|
||||||
|
},
|
||||||
|
getSubtreeIterator: function () {
|
||||||
|
// create a new range
|
||||||
|
var subRange = new DOMRange(this.range._document);
|
||||||
|
subRange.selectNodeContents(this._current);
|
||||||
|
// handle anchor points
|
||||||
|
if (DOMUtils.isAncestorOrSelf(this._current, this.range.startContainer))
|
||||||
|
subRange.setStart(this.range.startContainer, this.range.startOffset);
|
||||||
|
if (DOMUtils.isAncestorOrSelf(this._current, this.range.endContainer))
|
||||||
|
subRange.setEnd(this.range.endContainer, this.range.endOffset);
|
||||||
|
// return iterator
|
||||||
|
return new RangeIterator(subRange);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
DOM Selection
|
||||||
|
*/
|
||||||
|
|
||||||
|
//[NOTE] This is a very shallow implementation of the Selection object, based on Webkit's
|
||||||
|
// implementation and without redundant features. Complete selection manipulation is still
|
||||||
|
// possible with just removeAllRanges/addRange/getRangeAt.
|
||||||
|
|
||||||
|
DOMSelection = function (document) {
|
||||||
|
// save document parameter
|
||||||
|
this._document = document;
|
||||||
|
|
||||||
|
// add DOM selection handler
|
||||||
|
var selection = this;
|
||||||
|
document.attachEvent('onselectionchange', function () { selection._selectionChangeHandler(); });
|
||||||
|
};
|
||||||
|
|
||||||
|
DOMSelection.prototype = {
|
||||||
|
// public properties
|
||||||
|
rangeCount: 0,
|
||||||
|
// private properties
|
||||||
|
_document: null,
|
||||||
|
|
||||||
|
// private methods
|
||||||
|
_selectionChangeHandler: function () {
|
||||||
|
// check if there exists a range
|
||||||
|
this.rangeCount = this._selectionExists(this._document.selection.createRange()) ? 1 : 0;
|
||||||
|
},
|
||||||
|
_selectionExists: function (textRange) {
|
||||||
|
// checks if a created text range exists or is an editable cursor
|
||||||
|
return textRange.compareEndPoints('StartToEnd', textRange) !== 0 ||
|
||||||
|
textRange.parentElement().isContentEditable;
|
||||||
|
},
|
||||||
|
|
||||||
|
// public methods
|
||||||
|
addRange: function (range) {
|
||||||
|
// add range or combine with existing range
|
||||||
|
var selection = this._document.selection.createRange(), textRange = TextRangeUtils.convertFromDOMRange(range);
|
||||||
|
if (!this._selectionExists(selection))
|
||||||
|
{
|
||||||
|
// select range
|
||||||
|
textRange.select();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// only modify range if it intersects with current range
|
||||||
|
if (textRange.compareEndPoints('StartToStart', selection) == -1)
|
||||||
|
if (textRange.compareEndPoints('StartToEnd', selection) > -1 &&
|
||||||
|
textRange.compareEndPoints('EndToEnd', selection) == -1)
|
||||||
|
selection.setEndPoint('StartToStart', textRange);
|
||||||
|
else
|
||||||
|
if (textRange.compareEndPoints('EndToStart', selection) < 1 &&
|
||||||
|
textRange.compareEndPoints('EndToEnd', selection) > -1)
|
||||||
|
selection.setEndPoint('EndToEnd', textRange);
|
||||||
|
selection.select();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeAllRanges: function () {
|
||||||
|
// remove all ranges
|
||||||
|
this._document.selection.empty();
|
||||||
|
},
|
||||||
|
getRangeAt: function (index) {
|
||||||
|
// return any existing selection, or a cursor position in content editable mode
|
||||||
|
var textRange = this._document.selection.createRange();
|
||||||
|
if (this._selectionExists(textRange))
|
||||||
|
return TextRangeUtils.convertToDOMRange(textRange, this._document);
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
toString: function () {
|
||||||
|
// get selection text
|
||||||
|
return this._document.selection.createRange().text;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
scripting hooks
|
||||||
|
*/
|
||||||
|
|
||||||
|
document.createRange = function () {
|
||||||
|
return new DOMRange(document);
|
||||||
|
};
|
||||||
|
|
||||||
|
selection = new DOMSelection(document);
|
||||||
|
window.getSelection = function () {
|
||||||
|
return selection;
|
||||||
|
};
|
||||||
|
|
||||||
|
//[TODO] expose DOMRange/DOMSelection to window.?
|
||||||
|
}
|
||||||
|
|
||||||
|
})(window);
|
324
vendor/assets/javascripts/locomotive/aloha/lib/aloha/jquery.aloha.js
vendored
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright © 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*//*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[ 'aloha/core', 'aloha/selection', 'aloha/jquery', 'aloha/console' ],
|
||||||
|
function( Aloha, Selection, jQuery, console ) {
|
||||||
|
|
||||||
|
|
||||||
|
var
|
||||||
|
// $ = jQuery,
|
||||||
|
// Aloha = window.Aloha,
|
||||||
|
// console = window.console,
|
||||||
|
XMLSerializer = window.XMLSerializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* jQuery between Extension
|
||||||
|
*
|
||||||
|
* insert either html code, a dom object OR a jQuery object inside of an existing text node.
|
||||||
|
* if the chained jQuery object is not a text node, nothing will happen.
|
||||||
|
*
|
||||||
|
* @param content HTML Code, DOM object or jQuery object to be inserted
|
||||||
|
* @param offset character offset from the start where the content should be inserted
|
||||||
|
*/
|
||||||
|
jQuery.fn.between = function(content, offset) {
|
||||||
|
var
|
||||||
|
offSize,
|
||||||
|
fullText;
|
||||||
|
|
||||||
|
if (this[0].nodeType !== 3) {
|
||||||
|
// we are not in a text node, just insert the element at the corresponding position
|
||||||
|
offSize = this.children().size();
|
||||||
|
if (offset > offSize) {
|
||||||
|
offset = offSize;
|
||||||
|
}
|
||||||
|
if (offset <= 0) {
|
||||||
|
this.prepend(content);
|
||||||
|
} else {
|
||||||
|
this.children().eq(offset -1).after(content);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we are in a text node so we have to split it at the correct position
|
||||||
|
if (offset <= 0) {
|
||||||
|
this.before(content);
|
||||||
|
} else if (offset >= this[0].length) {
|
||||||
|
this.after(content);
|
||||||
|
} else {
|
||||||
|
fullText = this[0].data;
|
||||||
|
this[0].data = fullText.substring(0, offset);
|
||||||
|
this.after(fullText.substring(offset, fullText.length));
|
||||||
|
this.after(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make the object contenteditable. Care about browser version (name of contenteditable attribute depends on it)
|
||||||
|
*/
|
||||||
|
jQuery.fn.contentEditable = function( b ) {
|
||||||
|
// ie does not understand contenteditable but contentEditable
|
||||||
|
// contentEditable is not xhtml compatible.
|
||||||
|
var $el = jQuery(this);
|
||||||
|
var ce = 'contenteditable';
|
||||||
|
|
||||||
|
// Check
|
||||||
|
if (jQuery.browser.msie && parseInt(jQuery.browser.version,10) == 7 ) {
|
||||||
|
ce = 'contentEditable';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof b === 'undefined' ) {
|
||||||
|
|
||||||
|
// For chrome use this specific attribute. The old ce will only
|
||||||
|
// return 'inherit' for nested elements of a contenteditable.
|
||||||
|
// The isContentEditable is a w3c standard compliant property which works in IE7,8,FF36+, Chrome 12+
|
||||||
|
if (typeof $el[0] === 'undefined' ) {
|
||||||
|
console.error('The jquery object did not contain any valid elements.');
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (typeof $el[0].isContentEditable === 'undefined') {
|
||||||
|
console.warn('Could not determine whether the is editable or not. I assume it is.');
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return $el[0].isContentEditable;
|
||||||
|
}
|
||||||
|
} else if (b === '') {
|
||||||
|
$el.removeAttr(ce);
|
||||||
|
} else {
|
||||||
|
if (b && b !== 'false') {
|
||||||
|
b = 'true';
|
||||||
|
} else {
|
||||||
|
b = 'false';
|
||||||
|
}
|
||||||
|
$el.attr(ce, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $el;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* jQuery Aloha Plugin
|
||||||
|
*
|
||||||
|
* turn all dom elements to continous text
|
||||||
|
* @return jQuery object for the matched elements
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
jQuery.fn.aloha = function() {
|
||||||
|
var $this = jQuery( this );
|
||||||
|
|
||||||
|
Aloha.bind( 'aloha-ready', function() {
|
||||||
|
$this.each( function() {
|
||||||
|
// create a new aloha editable object for each passed object
|
||||||
|
if ( !Aloha.isEditable( this ) ) {
|
||||||
|
new Aloha.Editable( jQuery( this ) );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Chain
|
||||||
|
return $this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* jQuery destroy elements as editable
|
||||||
|
*
|
||||||
|
* destroy all mached elements editable capabilities
|
||||||
|
* @return jQuery object for the matched elements
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
jQuery.fn.mahalo = function() {
|
||||||
|
return this.each(function() {
|
||||||
|
if (Aloha.isEditable(this)) {
|
||||||
|
Aloha.getEditableById(jQuery(this).attr('id')).destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* jQuery Extension
|
||||||
|
* new Event which is triggered whenever a selection (length >= 0) is made in
|
||||||
|
* an Aloha Editable element
|
||||||
|
*/
|
||||||
|
jQuery.fn.contentEditableSelectionChange = function(callback) {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
// update selection when keys are pressed
|
||||||
|
this.keyup(function(event){
|
||||||
|
var rangeObject = Selection.getRangeObject();
|
||||||
|
callback(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
// update selection on doubleclick (especially important for the first automatic selection, when the Editable is not active yet, but is at the same time activated as the selection occurs
|
||||||
|
this.dblclick(function(event) {
|
||||||
|
callback(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
// update selection when text is selected
|
||||||
|
this.mousedown(function(event){
|
||||||
|
// remember that a selection was started
|
||||||
|
that.selectionStarted = true;
|
||||||
|
});
|
||||||
|
jQuery(document).mouseup(function(event) {
|
||||||
|
Selection.eventOriginalTarget = that;
|
||||||
|
if (that.selectionStarted) {
|
||||||
|
callback(event);
|
||||||
|
}
|
||||||
|
Selection.eventOriginalTarget = false;
|
||||||
|
that.selectionStarted = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the outerHTML of an Element
|
||||||
|
* @version 1.0.0
|
||||||
|
* @date February 01, 2011
|
||||||
|
* @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
|
||||||
|
* @author Benjamin Arthur Lupton {@link http://balupton.com}
|
||||||
|
* @copyright 2011 Benjamin Arthur Lupton {@link http://balupton.com}
|
||||||
|
* @license MIT License {@link http://creativecommons.org/licenses/MIT/}
|
||||||
|
* @return {String} outerHtml
|
||||||
|
*/
|
||||||
|
jQuery.fn.outerHtml = jQuery.fn.outerHtml || function(){
|
||||||
|
var
|
||||||
|
$el = jQuery(this),
|
||||||
|
el = $el.get(0);
|
||||||
|
if (typeof el.outerHTML != 'undefined') {
|
||||||
|
return el.outerHTML;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
// Gecko-based browsers, Safari, Opera.
|
||||||
|
return (new XMLSerializer()).serializeToString(el);
|
||||||
|
} catch (e) {
|
||||||
|
try {
|
||||||
|
// Internet Explorer.
|
||||||
|
return el.xml;
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
jQuery.fn.zap = function () {
|
||||||
|
return this.each(function(){ jQuery(this.childNodes).insertBefore(this); }).remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
jQuery.fn.textNodes = function(excludeBreaks, includeEmptyTextNodes) {
|
||||||
|
var
|
||||||
|
ret = [],
|
||||||
|
doSomething = function(el){
|
||||||
|
if (
|
||||||
|
(el.nodeType === 3 && jQuery.trim(el.data) && !includeEmptyTextNodes) ||
|
||||||
|
(el.nodeType === 3 && includeEmptyTextNodes) ||
|
||||||
|
(el.nodeName =="BR" && !excludeBreaks)) {
|
||||||
|
ret.push(el);
|
||||||
|
} else {
|
||||||
|
for (var i=0, childLength = el.childNodes.length; i < childLength; ++i) {
|
||||||
|
doSomething(el.childNodes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
doSomething(this[0]);
|
||||||
|
|
||||||
|
return jQuery(ret);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* extendObjects is like jQuery.extend, but it does not extend arrays
|
||||||
|
*/
|
||||||
|
jQuery.extendObjects = jQuery.fn.extendObjects = function() {
|
||||||
|
var options, name, src, copy, copyIsArray, clone,
|
||||||
|
target = arguments[0] || {},
|
||||||
|
i = 1,
|
||||||
|
length = arguments.length,
|
||||||
|
deep = false;
|
||||||
|
|
||||||
|
// Handle a deep copy situation
|
||||||
|
if ( typeof target === "boolean" ) {
|
||||||
|
deep = target;
|
||||||
|
target = arguments[1] || {};
|
||||||
|
// skip the boolean and the target
|
||||||
|
i = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle case when target is a string or something (possible in deep copy)
|
||||||
|
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
|
||||||
|
target = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// extend jQuery itself if only one argument is passed
|
||||||
|
if ( length === i ) {
|
||||||
|
target = this;
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( ; i < length; i++ ) {
|
||||||
|
// Only deal with non-null/undefined values
|
||||||
|
if ( (options = arguments[ i ]) != null ) {
|
||||||
|
// Extend the base object
|
||||||
|
for ( name in options ) {
|
||||||
|
src = target[ name ];
|
||||||
|
copy = options[ name ];
|
||||||
|
|
||||||
|
// Prevent never-ending loop
|
||||||
|
if ( target === copy ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recurse if we're merging plain objects or arrays
|
||||||
|
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
|
||||||
|
if ( copyIsArray ) {
|
||||||
|
copyIsArray = false;
|
||||||
|
clone = src && jQuery.isArray(src) ? src : [];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
clone = src && jQuery.isPlainObject(src) ? src : {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Never move original objects, clone them
|
||||||
|
if (jQuery.isArray(copy)) {
|
||||||
|
// don't extend arrays
|
||||||
|
target[ name ] = copy;
|
||||||
|
} else {
|
||||||
|
target[ name ] = jQuery.extendObjects( deep, clone, copy );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't bring in undefined values
|
||||||
|
} else if ( copy !== undefined ) {
|
||||||
|
target[ name ] = copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the modified object
|
||||||
|
return target;
|
||||||
|
};
|
||||||
|
|
||||||
|
jQuery.isBoolean = function(b) {
|
||||||
|
return b === true || b === false;
|
||||||
|
},
|
||||||
|
|
||||||
|
jQuery.isNumeric = function(o) {
|
||||||
|
return ! isNaN (o-0);
|
||||||
|
}
|
||||||
|
});
|
24
vendor/assets/javascripts/locomotive/aloha/lib/aloha/jquery.js
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright (c) 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// define jquery and ext modules. They need to be available in global namespace
|
||||||
|
define('aloha/jquery',[], function() {
|
||||||
|
return Aloha.jQuery;
|
||||||
|
});
|
115
vendor/assets/javascripts/locomotive/aloha/lib/aloha/jquery.patch.js
vendored
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright © 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*//*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// use specified jQuery or load jQuery
|
||||||
|
|
||||||
|
define(
|
||||||
|
[ 'aloha/jquery' ],
|
||||||
|
function( jQuery ) {
|
||||||
|
|
||||||
|
//PATCH FOR A JQUERY BUG IN 1.6.1 & 1.6.2
|
||||||
|
//An additional sanity check was introduced to prevent IE from crashing when cache[id] does not exist
|
||||||
|
jQuery.data = ( function( jQuery ) {
|
||||||
|
return function( elem, name, data, pvt /* Internal Use Only */ ) {
|
||||||
|
if ( !jQuery.acceptData( elem ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache,
|
||||||
|
|
||||||
|
// We have to handle DOM nodes and JS objects differently because IE6-7
|
||||||
|
// can't GC object references properly across the DOM-JS boundary
|
||||||
|
isNode = elem.nodeType,
|
||||||
|
|
||||||
|
// Only DOM nodes need the global jQuery cache; JS object data is
|
||||||
|
// attached directly to the object so GC can occur automatically
|
||||||
|
cache = isNode ? jQuery.cache : elem,
|
||||||
|
|
||||||
|
// Only defining an ID for JS objects if its cache already exists allows
|
||||||
|
// the code to shortcut on the same path as a DOM node with no cache
|
||||||
|
id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando;
|
||||||
|
|
||||||
|
// Avoid doing any more work than we need to when trying to get data on an
|
||||||
|
// object that has no data at all
|
||||||
|
//if ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) {
|
||||||
|
if ( (!id || (pvt && id && (!cache[id] || !cache[ id ][ internalKey ]))) && getByName && data === undefined ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !id ) {
|
||||||
|
// Only DOM nodes need a new unique ID for each element since their data
|
||||||
|
// ends up in the global cache
|
||||||
|
if ( isNode ) {
|
||||||
|
elem[ jQuery.expando ] = id = ++jQuery.uuid;
|
||||||
|
} else {
|
||||||
|
id = jQuery.expando;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !cache[ id ] ) {
|
||||||
|
cache[ id ] = {};
|
||||||
|
|
||||||
|
// TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
|
||||||
|
// metadata on plain JS objects when the object is serialized using
|
||||||
|
// JSON.stringify
|
||||||
|
if ( !isNode ) {
|
||||||
|
cache[ id ].toJSON = jQuery.noop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An object can be passed to jQuery.data instead of a key/value pair; this gets
|
||||||
|
// shallow copied over onto the existing cache
|
||||||
|
if ( typeof name === "object" || typeof name === "function" ) {
|
||||||
|
if ( pvt ) {
|
||||||
|
cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name);
|
||||||
|
} else {
|
||||||
|
cache[ id ] = jQuery.extend(cache[ id ], name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thisCache = cache[ id ];
|
||||||
|
|
||||||
|
// Internal jQuery data is stored in a separate object inside the object's data
|
||||||
|
// cache in order to avoid key collisions between internal data and user-defined
|
||||||
|
// data
|
||||||
|
if ( pvt ) {
|
||||||
|
if ( !thisCache[ internalKey ] ) {
|
||||||
|
thisCache[ internalKey ] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
thisCache = thisCache[ internalKey ];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( data !== undefined ) {
|
||||||
|
thisCache[ jQuery.camelCase( name ) ] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should
|
||||||
|
// not attempt to inspect the internal events object using jQuery.data, as this
|
||||||
|
// internal data object is undocumented and subject to change.
|
||||||
|
if ( name === "events" && !thisCache[name] ) {
|
||||||
|
return thisCache[ internalKey ] && thisCache[ internalKey ].events;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getByName ? thisCache[ jQuery.camelCase( name ) ] : thisCache;
|
||||||
|
};
|
||||||
|
})( jQuery );
|
||||||
|
|
||||||
|
});
|
898
vendor/assets/javascripts/locomotive/aloha/lib/aloha/markup.js
vendored
Normal file
@ -0,0 +1,898 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright © 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*//*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[ 'aloha/core', 'util/class', 'aloha/jquery' ],
|
||||||
|
function( Aloha, Class, jQuery ) {
|
||||||
|
|
||||||
|
|
||||||
|
var
|
||||||
|
// Aloha = window.Aloha,
|
||||||
|
// Class = window.Class,
|
||||||
|
GENTICS = window.GENTICS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Markup object
|
||||||
|
*/
|
||||||
|
Aloha.Markup = Class.extend({
|
||||||
|
/**
|
||||||
|
* Key handlers for special key codes
|
||||||
|
*/
|
||||||
|
keyHandlers: {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a key handler for the given key code
|
||||||
|
* @param keyCode key code
|
||||||
|
* @param handler handler function
|
||||||
|
*/
|
||||||
|
addKeyHandler: function (keyCode, handler) {
|
||||||
|
if (!this.keyHandlers[keyCode]) {
|
||||||
|
this.keyHandlers[keyCode] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.keyHandlers[keyCode].push(handler);
|
||||||
|
},
|
||||||
|
|
||||||
|
insertBreak: function () {
|
||||||
|
var range = Aloha.Selection.rangeObject,nonWSIndex,nextTextNode,newBreak;
|
||||||
|
if (!range.isCollapsed()) {
|
||||||
|
this.removeSelectedMarkup();
|
||||||
|
}
|
||||||
|
|
||||||
|
newBreak = jQuery('<br/>');
|
||||||
|
GENTICS.Utils.Dom.insertIntoDOM(newBreak, range, Aloha.activeEditable.obj);
|
||||||
|
|
||||||
|
nextTextNode = GENTICS.Utils.Dom.searchAdjacentTextNode(newBreak.parent().get(0), GENTICS.Utils.Dom.getIndexInParent(newBreak.get(0)) + 1, false);
|
||||||
|
if (nextTextNode) {
|
||||||
|
// trim leading whitespace
|
||||||
|
nonWSIndex = nextTextNode.data.search(/\S/);
|
||||||
|
if (nonWSIndex > 0) {
|
||||||
|
nextTextNode.data = nextTextNode.data.substring(nonWSIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
range.startContainer = range.endContainer = newBreak.get(0).parentNode;
|
||||||
|
range.startOffset = range.endOffset = GENTICS.Utils.Dom.getIndexInParent(newBreak.get(0)) + 1;
|
||||||
|
range.correctRange();
|
||||||
|
range.clearCaches();
|
||||||
|
range.select();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* first method to handle key strokes
|
||||||
|
* @param event DOM event
|
||||||
|
* @param rangeObject as provided by Aloha.Selection.getRangeObject();
|
||||||
|
* @return "Aloha.Selection"
|
||||||
|
*/
|
||||||
|
preProcessKeyStrokes: function(event) {
|
||||||
|
if (event.type != 'keydown') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rangeObject = Aloha.Selection.rangeObject,
|
||||||
|
handlers,
|
||||||
|
i;
|
||||||
|
|
||||||
|
if (this.keyHandlers[event.keyCode]) {
|
||||||
|
handlers = this.keyHandlers[event.keyCode];
|
||||||
|
for ( i = 0; i < handlers.length; ++i) {
|
||||||
|
if (!handlers[i](event)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle left (37) and right (39) keys for block detection
|
||||||
|
if (event.keyCode === 37 || event.keyCode === 39) {
|
||||||
|
return this.processCursor(rangeObject, event.keyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BACKSPACE
|
||||||
|
if (event.keyCode === 8) {
|
||||||
|
event.preventDefault(); // prevent history.back() even on exception
|
||||||
|
Aloha.execCommand( 'delete', false );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
if (event.keyCode === 46) {
|
||||||
|
Aloha.execCommand( 'forwarddelete', false );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ENTER
|
||||||
|
if (event.keyCode === 13 ) {
|
||||||
|
if (event.shiftKey) {
|
||||||
|
Aloha.execCommand( 'insertlinebreak', false );
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
Aloha.execCommand( 'insertparagraph', false );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processing of cursor keys
|
||||||
|
* will currently detect blocks (elements with contenteditable=false)
|
||||||
|
* and selects them (normally the cursor would jump right past them)
|
||||||
|
*
|
||||||
|
* For each block an 'aloha-block-selected' event will be triggered.
|
||||||
|
*
|
||||||
|
* @param range the current range object
|
||||||
|
* @param keyCode keyCode of current keypress
|
||||||
|
* @return false if a block was found to prevent further events, true otherwise
|
||||||
|
*/
|
||||||
|
processCursor: function(range, keyCode) {
|
||||||
|
var rt = range.getRangeTree(), // RangeTree reference
|
||||||
|
i = 0,
|
||||||
|
cursorLeft = keyCode === 37,
|
||||||
|
cursorRight = keyCode === 39,
|
||||||
|
nextSiblingIsBlock = false, // check whether the next sibling is a block (contenteditable = false)
|
||||||
|
cursorIsWithinBlock = false, // check whether the cursor is positioned within a block (contenteditable = false)
|
||||||
|
cursorAtLastPos = false, // check if the cursor is within the last position of the currently active dom element
|
||||||
|
obj; // will contain references to dom objects
|
||||||
|
|
||||||
|
if ( !range.isCollapsed() ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;i < rt.length; i++) {
|
||||||
|
if ( typeof rt[i].domobj !== 'undefined' ) {
|
||||||
|
cursorAtLastPos = range.startOffset === rt[i].domobj.length;
|
||||||
|
if (cursorAtLastPos) {
|
||||||
|
nextSiblingIsBlock = jQuery( rt[i].domobj.nextSibling ).attr('contenteditable') === 'false';
|
||||||
|
cursorIsWithinBlock = jQuery( rt[i].domobj ).parents('[contenteditable=false]').length > 0;
|
||||||
|
|
||||||
|
if ( cursorRight && nextSiblingIsBlock ) {
|
||||||
|
obj = rt[i].domobj.nextSibling;
|
||||||
|
GENTICS.Utils.Dom.selectDomNode( obj );
|
||||||
|
Aloha.trigger( 'aloha-block-selected', obj );
|
||||||
|
Aloha.Selection.preventSelectionChanged();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( cursorLeft && cursorIsWithinBlock ) {
|
||||||
|
obj = jQuery( rt[i].domobj ).parents('[contenteditable=false]').get(0);
|
||||||
|
GENTICS.Utils.Dom.selectDomNode( obj );
|
||||||
|
Aloha.trigger( 'aloha-block-selected', obj );
|
||||||
|
Aloha.Selection.preventSelectionChanged();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* method handling shiftEnter
|
||||||
|
* @param Aloha.Selection.SelectionRange of the current selection
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
processShiftEnter: function(rangeObject) {
|
||||||
|
this.insertHTMLBreak(rangeObject.getSelectionTree(), rangeObject);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* method handling Enter
|
||||||
|
* @param Aloha.Selection.SelectionRange of the current selection
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
processEnter: function(rangeObject) {
|
||||||
|
if (rangeObject.splitObject) {
|
||||||
|
// now comes a very evil hack for ie, when the enter is pressed in a text node in an li element, we just append an empty text node
|
||||||
|
// if (jQuery.browser.msie
|
||||||
|
// && GENTICS.Utils.Dom
|
||||||
|
// .isListElement(rangeObject.splitObject)) {
|
||||||
|
// jQuery(rangeObject.splitObject).append(
|
||||||
|
// jQuery(document.createTextNode('')));
|
||||||
|
// }
|
||||||
|
|
||||||
|
this.splitRangeObject(rangeObject);
|
||||||
|
} else { // if there is no split object, the Editable is the paragraph type itself (e.g. a p or h2)
|
||||||
|
this.insertHTMLBreak(rangeObject.getSelectionTree(), rangeObject);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert the given html markup at the current selection
|
||||||
|
* @param html html markup to be inserted
|
||||||
|
*/
|
||||||
|
insertHTMLCode: function (html) {
|
||||||
|
var rangeObject = Aloha.Selection.rangeObject;
|
||||||
|
|
||||||
|
this.insertHTMLBreak(rangeObject.getSelectionTree(), rangeObject, jQuery(html));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* insert an HTML Break <br /> into current selection
|
||||||
|
* @param Aloha.Selection.SelectionRange of the current selection
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
insertHTMLBreak: function(selectionTree, rangeObject, inBetweenMarkup) {
|
||||||
|
var i, treeLength, el, jqEl, jqElBefore, jqElAfter, tmpObject, offset, checkObj;
|
||||||
|
|
||||||
|
inBetweenMarkup = inBetweenMarkup ? inBetweenMarkup: jQuery('<br/>');
|
||||||
|
for ( i = 0, treeLength = selectionTree.length; i < treeLength; i++) {
|
||||||
|
el = selectionTree[i];
|
||||||
|
jqEl = el.domobj ? jQuery(el.domobj) : undefined;
|
||||||
|
if (el.selection !== 'none') { // before cursor, leave this part inside the splitObject
|
||||||
|
if (el.selection == 'collapsed') {
|
||||||
|
// collapsed selection found (between nodes)
|
||||||
|
if (i > 0) {
|
||||||
|
// not at the start, so get the element to the left
|
||||||
|
jqElBefore = jQuery(selectionTree[i-1].domobj);
|
||||||
|
// and insert the break after it
|
||||||
|
jqElBefore.after(inBetweenMarkup);
|
||||||
|
} else {
|
||||||
|
// at the start, so get the element to the right
|
||||||
|
jqElAfter = jQuery(selectionTree[1].domobj);
|
||||||
|
// and insert the break before it
|
||||||
|
jqElAfter.before(inBetweenMarkup);
|
||||||
|
}
|
||||||
|
|
||||||
|
// now set the range
|
||||||
|
rangeObject.startContainer = rangeObject.endContainer = inBetweenMarkup[0].parentNode;
|
||||||
|
rangeObject.startOffset = rangeObject.endOffset = GENTICS.Utils.Dom.getIndexInParent(inBetweenMarkup[0]) + 1;
|
||||||
|
rangeObject.correctRange();
|
||||||
|
} else if (el.domobj && el.domobj.nodeType === 3) { // textNode
|
||||||
|
// when the textnode is immediately followed by a blocklevel element (like p, h1, ...) we need to add an additional br in between
|
||||||
|
if (el.domobj.nextSibling
|
||||||
|
&& el.domobj.nextSibling.nodeType == 1
|
||||||
|
&& Aloha.Selection.replacingElements[el.domobj.nextSibling.nodeName
|
||||||
|
.toLowerCase()]) {
|
||||||
|
// TODO check whether this depends on the browser
|
||||||
|
jqEl.after('<br/>');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.needEndingBreak()) {
|
||||||
|
// when the textnode is the last inside a blocklevel element
|
||||||
|
// (like p, h1, ...) we need to add an additional br as very
|
||||||
|
// last object in the blocklevel element
|
||||||
|
checkObj = el.domobj;
|
||||||
|
while (checkObj) {
|
||||||
|
if (checkObj.nextSibling) {
|
||||||
|
checkObj = false;
|
||||||
|
} else {
|
||||||
|
// go to the parent
|
||||||
|
checkObj = checkObj.parentNode;
|
||||||
|
// found a blocklevel or list element, we are done
|
||||||
|
if (GENTICS.Utils.Dom.isBlockLevelElement(checkObj) || GENTICS.Utils.Dom.isListElement(checkObj)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// reached the limit object, we are done
|
||||||
|
if (checkObj === rangeObject.limitObject) {
|
||||||
|
checkObj = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// when we found a blocklevel element, insert a break at the
|
||||||
|
// end. Mark the break so that it is cleaned when the
|
||||||
|
// content is fetched.
|
||||||
|
if (checkObj) {
|
||||||
|
jQuery(checkObj).append('<br class="aloha-cleanme"/>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert the break
|
||||||
|
jqEl.between(inBetweenMarkup, el.startOffset);
|
||||||
|
|
||||||
|
// correct the range
|
||||||
|
// count the number of previous siblings
|
||||||
|
offset = 0;
|
||||||
|
tmpObject = inBetweenMarkup[0];
|
||||||
|
while (tmpObject) {
|
||||||
|
tmpObject = tmpObject.previousSibling;
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
rangeObject.startContainer = inBetweenMarkup[0].parentNode;
|
||||||
|
rangeObject.endContainer = inBetweenMarkup[0].parentNode;
|
||||||
|
rangeObject.startOffset = offset;
|
||||||
|
rangeObject.endOffset = offset;
|
||||||
|
rangeObject.correctRange();
|
||||||
|
} else if (el.domobj && el.domobj.nodeType === 1) { // other node, normally a break
|
||||||
|
if (jqEl.parent().find('br.aloha-ephemera').length === 0) {
|
||||||
|
// but before putting it, remove all:
|
||||||
|
jQuery(rangeObject.limitObject).find('br.aloha-ephemera').remove();
|
||||||
|
// now put it:
|
||||||
|
jQuery(rangeObject.commonAncestorContainer).append(this.getFillUpElement(rangeObject.splitObject));
|
||||||
|
}
|
||||||
|
jqEl.after(inBetweenMarkup);
|
||||||
|
|
||||||
|
// now set the selection. Since we just added one break do the currect el
|
||||||
|
// the new position must be el's position + 1. el's position is the index
|
||||||
|
// of the el in the selection tree, which is i. then we must add
|
||||||
|
// another +1 because we want to be AFTER the object, not before. therefor +2
|
||||||
|
rangeObject.startContainer = rangeObject.commonAncestorContainer;
|
||||||
|
rangeObject.endContainer = rangeObject.startContainer;
|
||||||
|
rangeObject.startOffset = i+2;
|
||||||
|
rangeObject.endOffset = i+2;
|
||||||
|
rangeObject.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rangeObject.select();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether blocklevel elements need breaks at the end to visibly render a newline
|
||||||
|
* @return true if an ending break is necessary, false if not
|
||||||
|
*/
|
||||||
|
needEndingBreak: function () {
|
||||||
|
// currently, all browser except IE need ending breaks
|
||||||
|
return !jQuery.browser.msie;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the currently selected text or false if nothing is selected (or the selection is collapsed)
|
||||||
|
* @return selected text
|
||||||
|
*/
|
||||||
|
getSelectedText: function () {
|
||||||
|
var rangeObject = Aloha.Selection.rangeObject;
|
||||||
|
|
||||||
|
if (rangeObject.isCollapsed()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getFromSelectionTree(rangeObject.getSelectionTree(), true);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursive function to get the selected text from the selection tree starting at the given level
|
||||||
|
* @param selectionTree array of selectiontree elements
|
||||||
|
* @param astext true when the contents shall be fetched as text, false for getting as html markup
|
||||||
|
* @return selected text from that level (incluiding all sublevels)
|
||||||
|
*/
|
||||||
|
getFromSelectionTree: function (selectionTree, astext) {
|
||||||
|
var text = '', i, treeLength, el, clone;
|
||||||
|
for ( i = 0, treeLength = selectionTree.length; i < treeLength; i++) {
|
||||||
|
el = selectionTree[i];
|
||||||
|
if (el.selection == 'partial') {
|
||||||
|
if (el.domobj.nodeType === 3) {
|
||||||
|
// partial text node selected, get the selected part
|
||||||
|
text += el.domobj.data.substring(el.startOffset, el.endOffset);
|
||||||
|
} else if (el.domobj.nodeType === 1 && el.children) {
|
||||||
|
// partial element node selected, do the recursion into the children
|
||||||
|
if (astext) {
|
||||||
|
text += this.getFromSelectionTree(el.children, astext);
|
||||||
|
} else {
|
||||||
|
// when the html shall be fetched, we create a clone of the element and remove all the children
|
||||||
|
clone = jQuery(el.domobj).clone(false).empty();
|
||||||
|
// then we do the recursion and add the selection into the clone
|
||||||
|
clone.html(this.getFromSelectionTree(el.children, astext));
|
||||||
|
// finally we get the html of the clone
|
||||||
|
text += clone.outerHTML();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (el.selection == 'full') {
|
||||||
|
if (el.domobj.nodeType === 3) {
|
||||||
|
// full text node selected, get the text
|
||||||
|
text += jQuery(el.domobj).text();
|
||||||
|
} else if (el.domobj.nodeType === 1 && el.children) {
|
||||||
|
// full element node selected, get the html of the node and all children
|
||||||
|
text += astext ? jQuery(el.domobj).text() : jQuery(el.domobj).outerHTML();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the currently selected markup or false if nothing is selected (or the selection is collapsed)
|
||||||
|
* @return selected markup
|
||||||
|
*/
|
||||||
|
getSelectedMarkup: function () {
|
||||||
|
var rangeObject = Aloha.Selection.rangeObject;
|
||||||
|
|
||||||
|
if (rangeObject.isCollapsed()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getFromSelectionTree(rangeObject.getSelectionTree(), false);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the currently selected markup
|
||||||
|
|
||||||
|
*/
|
||||||
|
removeSelectedMarkup: function () {
|
||||||
|
var rangeObject = Aloha.Selection.rangeObject, newRange;
|
||||||
|
|
||||||
|
if (rangeObject.isCollapsed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
newRange = new Aloha.Selection.SelectionRange();
|
||||||
|
// remove the selection
|
||||||
|
this.removeFromSelectionTree(rangeObject.getSelectionTree(), newRange);
|
||||||
|
|
||||||
|
// do a cleanup now (starting with the commonancestorcontainer)
|
||||||
|
newRange.update();
|
||||||
|
GENTICS.Utils.Dom.doCleanup({'merge' : true, 'removeempty' : true}, Aloha.Selection.rangeObject);
|
||||||
|
Aloha.Selection.rangeObject = newRange;
|
||||||
|
|
||||||
|
// need to set the collapsed selection now
|
||||||
|
newRange.correctRange();
|
||||||
|
newRange.update();
|
||||||
|
newRange.select();
|
||||||
|
Aloha.Selection.updateSelection();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively remove the selected items, starting with the given level in the selectiontree
|
||||||
|
* @param selectionTree current level of the selectiontree
|
||||||
|
* @param newRange new collapsed range to be set after the removal
|
||||||
|
*/
|
||||||
|
removeFromSelectionTree: function (selectionTree, newRange) {
|
||||||
|
// remember the first found partially selected element node (in case we need
|
||||||
|
// to merge it with the last found partially selected element node)
|
||||||
|
var firstPartialElement, newdata, i, el, adjacentTextNode, treeLength;
|
||||||
|
|
||||||
|
// iterate through the selection tree
|
||||||
|
for (i = 0, treeLength = selectionTree.length; i < treeLength; i++) {
|
||||||
|
el = selectionTree[i];
|
||||||
|
// check the type of selection
|
||||||
|
if (el.selection == 'partial') {
|
||||||
|
if (el.domobj.nodeType === 3) {
|
||||||
|
// partial text node selected, so remove the selected portion
|
||||||
|
newdata = '';
|
||||||
|
if (el.startOffset > 0) {
|
||||||
|
newdata += el.domobj.data.substring(0, el.startOffset);
|
||||||
|
}
|
||||||
|
if (el.endOffset < el.domobj.data.length) {
|
||||||
|
newdata += el.domobj.data.substring(el.endOffset, el.domobj.data.length);
|
||||||
|
}
|
||||||
|
el.domobj.data = newdata;
|
||||||
|
|
||||||
|
// eventually set the new range (if not done before)
|
||||||
|
if (!newRange.startContainer) {
|
||||||
|
newRange.startContainer = newRange.endContainer = el.domobj;
|
||||||
|
newRange.startOffset = newRange.endOffset = el.startOffset;
|
||||||
|
}
|
||||||
|
} else if (el.domobj.nodeType === 1 && el.children) {
|
||||||
|
// partial element node selected, so do the recursion into the children
|
||||||
|
this.removeFromSelectionTree(el.children, newRange);
|
||||||
|
if (firstPartialElement) {
|
||||||
|
// when the first parially selected element is the same type
|
||||||
|
// of element, we need to merge them
|
||||||
|
if (firstPartialElement.nodeName == el.domobj.nodeName) {
|
||||||
|
// merge the nodes
|
||||||
|
jQuery(firstPartialElement).append(jQuery(el.domobj).contents());
|
||||||
|
// and remove the latter one
|
||||||
|
jQuery(el.domobj).remove();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// remember this element as first partially selected element
|
||||||
|
firstPartialElement = el.domobj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (el.selection == 'full') {
|
||||||
|
// eventually set the new range (if not done before)
|
||||||
|
if (!newRange.startContainer) {
|
||||||
|
adjacentTextNode = GENTICS.Utils.Dom.searchAdjacentTextNode(el.domobj.parentNode, GENTICS.Utils.Dom.getIndexInParent(el.domobj) + 1, false, {'blocklevel' : false});
|
||||||
|
if (adjacentTextNode) {
|
||||||
|
newRange.startContainer = newRange.endContainer = adjacentTextNode;
|
||||||
|
newRange.startOffset = newRange.endOffset = 0;
|
||||||
|
} else {
|
||||||
|
newRange.startContainer = newRange.endContainer = el.domobj.parentNode;
|
||||||
|
newRange.startOffset = newRange.endOffset = GENTICS.Utils.Dom.getIndexInParent(el.domobj) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// full node selected, so just remove it (will also remove all children)
|
||||||
|
jQuery(el.domobj).remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* split passed rangeObject without or with optional markup
|
||||||
|
* @param Aloha.Selection.SelectionRange of the current selection
|
||||||
|
* @param markup object (jQuery) to insert in between the split elements
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
splitRangeObject: function(rangeObject, markup) {
|
||||||
|
// UAAAA: first check where the markup can be inserted... *grrrrr*, then decide where to split
|
||||||
|
// object which is split up
|
||||||
|
var
|
||||||
|
splitObject = jQuery(rangeObject.splitObject),
|
||||||
|
selectionTree, insertAfterObject, followUpContainer;
|
||||||
|
|
||||||
|
// update the commonAncestor with the splitObject (so that the selectionTree is correct)
|
||||||
|
rangeObject.update(rangeObject.splitObject); // set the splitObject as new commonAncestorContainer and update the selectionTree
|
||||||
|
|
||||||
|
// calculate the selection tree. NOTE: it is necessary to do this before
|
||||||
|
// getting the followupcontainer, since getting the selection tree might
|
||||||
|
// possibly merge text nodes, which would lead to differences in the followupcontainer
|
||||||
|
selectionTree = rangeObject.getSelectionTree();
|
||||||
|
|
||||||
|
// object to be inserted after the splitObject
|
||||||
|
followUpContainer = this.getSplitFollowUpContainer(rangeObject);
|
||||||
|
|
||||||
|
// now split up the splitObject into itself AND the followUpContainer
|
||||||
|
this.splitRangeObjectHelper(selectionTree, rangeObject, followUpContainer); // split the current object into itself and the followUpContainer
|
||||||
|
|
||||||
|
// check whether the followupcontainer is still marked for removal
|
||||||
|
if (followUpContainer.hasClass('preparedForRemoval')) {
|
||||||
|
// TODO shall we just remove the class or shall we not use the followupcontainer?
|
||||||
|
followUpContainer.removeClass('preparedForRemoval');
|
||||||
|
}
|
||||||
|
|
||||||
|
// now let's find the place, where the followUp is inserted afterwards. normally that's the splitObject itself, but in
|
||||||
|
// some cases it might be their parent (e.g. inside a list, a <p> followUp must be inserted outside the list)
|
||||||
|
insertAfterObject = this.getInsertAfterObject(rangeObject, followUpContainer);
|
||||||
|
|
||||||
|
// now insert the followUpContainer
|
||||||
|
jQuery(followUpContainer).insertAfter(insertAfterObject); // attach the followUpContainer right after the insertAfterObject
|
||||||
|
|
||||||
|
// in some cases, we want to remove the "empty" splitObject (e.g. LIs, if enter was hit twice)
|
||||||
|
if (rangeObject.splitObject.nodeName.toLowerCase() === 'li' && !Aloha.Selection.standardTextLevelSemanticsComparator(rangeObject.splitObject, followUpContainer)) {
|
||||||
|
jQuery(rangeObject.splitObject).remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
rangeObject.startContainer = null;
|
||||||
|
// first check whether the followUpContainer starts with a <br/>
|
||||||
|
// if so, place the cursor right before the <br/>
|
||||||
|
var followContents = followUpContainer.contents();
|
||||||
|
if (followContents.length > 0
|
||||||
|
&& followContents.get(0).nodeType == 1
|
||||||
|
&& followContents.get(0).nodeName.toLowerCase() === 'br') {
|
||||||
|
rangeObject.startContainer = followUpContainer.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rangeObject.startContainer) {
|
||||||
|
// find a possible text node in the followUpContainer and set the selection to it
|
||||||
|
// if no textnode is available, set the selection to the followup container itself
|
||||||
|
rangeObject.startContainer = followUpContainer.textNodes(true, true).first().get(0);
|
||||||
|
}
|
||||||
|
if (!rangeObject.startContainer) { // if no text node was found, select the parent object of <br class="aloha-ephemera" />
|
||||||
|
rangeObject.startContainer = followUpContainer.textNodes(false).first().parent().get(0);
|
||||||
|
}
|
||||||
|
if (rangeObject.startContainer) {
|
||||||
|
// the cursor is always at the beginning of the followUp
|
||||||
|
rangeObject.endContainer = rangeObject.startContainer;
|
||||||
|
rangeObject.startOffset = 0;
|
||||||
|
rangeObject.endOffset = 0;
|
||||||
|
} else {
|
||||||
|
rangeObject.startContainer = rangeObject.endContainer = followUpContainer.parent().get(0);
|
||||||
|
rangeObject.startOffset = rangeObject.endOffset = GENTICS.Utils.Dom.getIndexInParent(followUpContainer.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally update the range object again
|
||||||
|
rangeObject.update();
|
||||||
|
|
||||||
|
// now set the selection
|
||||||
|
rangeObject.select();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* method to get the object after which the followUpContainer can be inserted during splitup
|
||||||
|
* this is a helper method, not needed anywhere else
|
||||||
|
* @param rangeObject Aloha.Selection.SelectionRange of the current selection
|
||||||
|
* @param followUpContainer optional jQuery object; if provided the rangeObject will be split and the second part will be insert inside of this object
|
||||||
|
* @return object after which the followUpContainer can be inserted
|
||||||
|
*/
|
||||||
|
getInsertAfterObject: function(rangeObject, followUpContainer) {
|
||||||
|
var passedSplitObject,i,el;
|
||||||
|
|
||||||
|
for (i = 0; i < rangeObject.markupEffectiveAtStart.length; i++) {
|
||||||
|
el = rangeObject.markupEffectiveAtStart[ i ];
|
||||||
|
// check if we have already passed the splitObject (some other markup might come before)
|
||||||
|
if (el === rangeObject.splitObject){
|
||||||
|
passedSplitObject = true;
|
||||||
|
}
|
||||||
|
// if not passed splitObject, skip this markup
|
||||||
|
if (!passedSplitObject) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// once we are passed, check if the followUpContainer is allowed to be inserted into the currents el's parent
|
||||||
|
if (Aloha.Selection.canTag1WrapTag2(jQuery(el).parent()[0].nodeName, followUpContainer[0].nodeName)) {
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* method to get the html code for a fillUpElement. this is needed for empty paragraphs etc., so that they take up their expected height
|
||||||
|
* @param splitObject split object (dom object)
|
||||||
|
* @return fillUpElement HTML Code
|
||||||
|
*/
|
||||||
|
getFillUpElement: function(splitObject) {
|
||||||
|
if (jQuery.browser.msie) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return jQuery('<br class="aloha-cleanme"/>');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* removes textNodes from passed array, which only contain contentWhiteSpace (e.g. a \n between two tags)
|
||||||
|
* @param domArray array of domObjects
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
removeElementContentWhitespaceObj: function(domArray) {
|
||||||
|
var correction = 0,
|
||||||
|
removeLater = [],
|
||||||
|
i,
|
||||||
|
el, removeIndex;
|
||||||
|
|
||||||
|
for (i = 0; i < domArray.length; i++) {
|
||||||
|
el = domArray[i];
|
||||||
|
if (el.isElementContentWhitespace) {
|
||||||
|
removeLater[removeLater.length] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < removeLater.length; i++) {
|
||||||
|
removeIndex = removeLater[i];
|
||||||
|
domArray.splice(removeIndex - correction, 1);
|
||||||
|
correction++;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* recursive method to parallelly walk through two dom subtrees, leave elements before startContainer in first subtree and move rest to other
|
||||||
|
* @param selectionTree tree to iterate over as contained in rangeObject. must be passed separately to allow recursion in the selection tree, but not in the rangeObject
|
||||||
|
* @param rangeObject Aloha.Selection.SelectionRange of the current selection
|
||||||
|
* @param followUpContainer optional jQuery object; if provided the rangeObject will be split and the second part will be insert inside of this object
|
||||||
|
* @param inBetweenMarkup jQuery object to be inserted between the two split parts. will be either a <br> (if no followUpContainer is passed) OR e.g. a table, which must be inserted between the splitobject AND the follow up
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
splitRangeObjectHelper: function(selectionTree, rangeObject, followUpContainer, inBetweenMarkup) {
|
||||||
|
if (!followUpContainer) {
|
||||||
|
Aloha.Log.warn(this, 'no followUpContainer, no inBetweenMarkup, nothing to do...');
|
||||||
|
}
|
||||||
|
|
||||||
|
var fillUpElement = this.getFillUpElement(rangeObject.splitObject),
|
||||||
|
splitObject = jQuery(rangeObject.splitObject),
|
||||||
|
startMoving = false,
|
||||||
|
el, i, completeText, jqObj, mirrorLevel, parent, treeLength;
|
||||||
|
|
||||||
|
if (selectionTree.length > 0) {
|
||||||
|
mirrorLevel = followUpContainer.contents();
|
||||||
|
|
||||||
|
// if length of mirrorLevel and selectionTree are not equal, the mirrorLevel must be corrected. this happens, when the mirrorLevel contains whitespace textNodes
|
||||||
|
if (mirrorLevel.length !== selectionTree.length) {
|
||||||
|
this.removeElementContentWhitespaceObj(mirrorLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0, treeLength = selectionTree.length; i < treeLength; i++) {
|
||||||
|
el = selectionTree[i];
|
||||||
|
// remove all objects in the mirrorLevel, which are BEFORE the cursor
|
||||||
|
// OR if the cursor is at the last position of the last Textnode (causing an empty followUpContainer to be appended)
|
||||||
|
if (
|
||||||
|
(el.selection === 'none' && startMoving === false) ||
|
||||||
|
(el.domobj && el.domobj.nodeType === 3 && el === selectionTree[ (selectionTree.length-1) ] && el.startOffset === el.domobj.data.length)
|
||||||
|
) {
|
||||||
|
// iteration is before cursor, leave this part inside the splitObject, remove from followUpContainer
|
||||||
|
// however if the object to remove is the last existing textNode within the followUpContainer, insert a BR instead
|
||||||
|
// otherwise the followUpContainer is invalid and takes up no vertical space
|
||||||
|
|
||||||
|
if (followUpContainer.textNodes().length > 1 || (el.domobj.nodeType === 1 && el.children.length === 0)) {
|
||||||
|
// note: the second part of the if (el.domobj.nodeType === 1 && el.children.length === 0) covers a very special condition,
|
||||||
|
// where an empty tag is located right before the cursor when pressing enter. In this case the empty tag would not be
|
||||||
|
// removed correctly otherwise
|
||||||
|
mirrorLevel.eq(i).remove();
|
||||||
|
} else if (GENTICS.Utils.Dom.isSplitObject(followUpContainer[0])) {
|
||||||
|
if (fillUpElement) {
|
||||||
|
followUpContainer.html(fillUpElement); // for your zoological german knowhow: ephemera = Eintagsfliege
|
||||||
|
} else {
|
||||||
|
followUpContainer.empty();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
followUpContainer.empty();
|
||||||
|
followUpContainer.addClass('preparedForRemoval');
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// split objects, which are AT the cursor Position or directly above
|
||||||
|
if (el.selection !== 'none') { // before cursor, leave this part inside the splitObject
|
||||||
|
// TODO better check for selection == 'partial' here?
|
||||||
|
if (el.domobj && el.domobj.nodeType === 3 && el.startOffset !== undefined) {
|
||||||
|
completeText = el.domobj.data;
|
||||||
|
if (el.startOffset > 0) {// first check, if there will be some text left in the splitObject
|
||||||
|
el.domobj.data = completeText.substr(0, el.startOffset);
|
||||||
|
} else if (selectionTree.length > 1) { // if not, check if the splitObject contains more than one node, because then it can be removed. this happens, when ENTER is pressed inside of a textnode, but not at the borders
|
||||||
|
jQuery(el.domobj).remove();
|
||||||
|
} else { // if the "empty" textnode is the last node left in the splitObject, replace it with a ephemera break
|
||||||
|
// if the parent is a blocklevel element, we insert the fillup element
|
||||||
|
parent = jQuery(el.domobj).parent();
|
||||||
|
if (GENTICS.Utils.Dom.isSplitObject(parent[0])) {
|
||||||
|
if (fillUpElement) {
|
||||||
|
parent.html(fillUpElement);
|
||||||
|
} else {
|
||||||
|
parent.empty();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if the parent is no blocklevel element and would be empty now, we completely remove it
|
||||||
|
parent.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (completeText.length - el.startOffset > 0) {
|
||||||
|
// first check if there is text left to put in the followUpContainer's textnode. this happens, when ENTER is pressed inside of a textnode, but not at the borders
|
||||||
|
mirrorLevel[i].data = completeText.substr(el.startOffset, completeText.length);
|
||||||
|
} else if (mirrorLevel.length > 1) {
|
||||||
|
// if not, check if the followUpContainer contains more than one node, because if yes, the "empty" textnode can be removed
|
||||||
|
mirrorLevel.eq( (i) ).remove();
|
||||||
|
} else if (GENTICS.Utils.Dom.isBlockLevelElement(followUpContainer[0])) {
|
||||||
|
// if the "empty" textnode is the last node left in the followUpContainer (which is a blocklevel element), replace it with a ephemera break
|
||||||
|
if (fillUpElement) {
|
||||||
|
followUpContainer.html(fillUpElement);
|
||||||
|
} else {
|
||||||
|
followUpContainer.empty();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if the "empty" textnode is the last node left in a non-blocklevel element, mark it for removal
|
||||||
|
followUpContainer.empty();
|
||||||
|
followUpContainer.addClass('preparedForRemoval');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startMoving = true;
|
||||||
|
if (el.children.length > 0) {
|
||||||
|
this.splitRangeObjectHelper(el.children, rangeObject, mirrorLevel.eq(i), inBetweenMarkup);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// remove all objects in the origin, which are AFTER the cursor
|
||||||
|
if (el.selection === 'none' && startMoving === true) {
|
||||||
|
// iteration is after cursor, remove from splitObject and leave this part inside the followUpContainer
|
||||||
|
jqObj = jQuery(el.domobj).remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Aloha.Log.error(this, 'can not split splitObject due to an empty selection tree');
|
||||||
|
}
|
||||||
|
|
||||||
|
// and finally cleanup: remove all fillUps > 1
|
||||||
|
splitObject.find('br.aloha-ephemera:gt(0)').remove(); // remove all elements greater than (gt) 0, that also means: leave one
|
||||||
|
followUpContainer.find('br.aloha-ephemera:gt(0)').remove(); // remove all elements greater than (gt) 0, that also means: leave one
|
||||||
|
|
||||||
|
// remove objects prepared for removal
|
||||||
|
splitObject.find('.preparedForRemoval').remove();
|
||||||
|
followUpContainer.find('.preparedForRemoval').remove();
|
||||||
|
|
||||||
|
// if splitObject / followUp are empty, place a fillUp inside
|
||||||
|
if (splitObject.contents().length === 0 && GENTICS.Utils.Dom.isSplitObject(splitObject[0]) && fillUpElement) {
|
||||||
|
splitObject.html(fillUpElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (followUpContainer.contents().length === 0 && GENTICS.Utils.Dom.isSplitObject(followUpContainer[0]) && fillUpElement) {
|
||||||
|
followUpContainer.html(fillUpElement);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns a jQuery object fitting the passed splitObject as follow up object
|
||||||
|
* examples,
|
||||||
|
* - when passed a p it will return an empty p (clone of the passed p)
|
||||||
|
* - when passed an h1, it will return either an h1 (clone of the passed one) or a new p (if the collapsed selection was at the end)
|
||||||
|
* @param rangeObject Aloha.RangeObject
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
getSplitFollowUpContainer: function(rangeObject) {
|
||||||
|
var
|
||||||
|
tagName = rangeObject.splitObject.nodeName.toLowerCase(),
|
||||||
|
returnObj, inside, lastObj;
|
||||||
|
|
||||||
|
switch(tagName) {
|
||||||
|
case 'h1':
|
||||||
|
case 'h2':
|
||||||
|
case 'h3':
|
||||||
|
case 'h4':
|
||||||
|
case 'h5':
|
||||||
|
case 'h6':
|
||||||
|
// get the last textnode in the splitobject, but don't consider aloha-cleanme elements
|
||||||
|
lastObj = jQuery(rangeObject.splitObject).textNodes(':not(.aloha-cleanme)').last()[0];
|
||||||
|
// special case: when enter is hit at the end of a heading, the followUp should be a <p>
|
||||||
|
if (lastObj && rangeObject.startContainer === lastObj && rangeObject.startOffset === lastObj.length) {
|
||||||
|
returnObj = jQuery('<p></p>');
|
||||||
|
inside = jQuery(rangeObject.splitObject).clone().contents();
|
||||||
|
returnObj.append(inside);
|
||||||
|
return returnObj;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'li':
|
||||||
|
// TODO check whether the li is the last one
|
||||||
|
// special case: if enter is hit twice inside a list, the next item should be a <p> (and inserted outside the list)
|
||||||
|
if (rangeObject.startContainer.nodeName.toLowerCase() === 'br' && jQuery(rangeObject.startContainer).hasClass('aloha-ephemera')) {
|
||||||
|
returnObj = jQuery('<p></p>');
|
||||||
|
inside = jQuery(rangeObject.splitObject).clone().contents();
|
||||||
|
returnObj.append(inside);
|
||||||
|
return returnObj;
|
||||||
|
}
|
||||||
|
// when the li is the last one and empty, we also just return a <p>
|
||||||
|
if (!rangeObject.splitObject.nextSibling && jQuery.trim(jQuery(rangeObject.splitObject).text()).length === 0) {
|
||||||
|
returnObj = jQuery('<p></p>');
|
||||||
|
return returnObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return jQuery(rangeObject.splitObject).clone();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the given domobj into an object with the given new nodeName.
|
||||||
|
* Preserves the content and all attributes. If a range object is given, also the range will be preserved
|
||||||
|
* @param domobj dom object to transform
|
||||||
|
* @param nodeName new node name
|
||||||
|
* @param range range object
|
||||||
|
* @api
|
||||||
|
* @return new object as jQuery object
|
||||||
|
*/
|
||||||
|
transformDomObject: function (domobj, nodeName, range) {
|
||||||
|
// first create the new element
|
||||||
|
var
|
||||||
|
jqOldObj = jQuery(domobj),
|
||||||
|
jqNewObj = jQuery('<' + nodeName + '></' + nodeName + '>'),
|
||||||
|
i;
|
||||||
|
|
||||||
|
// TODO what about events? css properties?
|
||||||
|
|
||||||
|
// copy attributes
|
||||||
|
if (jqOldObj[0].attributes) {
|
||||||
|
for (i = 0; i < jqOldObj[0].attributes.length; i++) {
|
||||||
|
jqNewObj.attr(jqOldObj[0].attributes[i].nodeName, jqOldObj[0].attributes[i].nodeValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy inline CSS
|
||||||
|
if (jqOldObj[0].style && jqOldObj[0].style.cssText) {
|
||||||
|
jqNewObj[0].style.cssText = jqOldObj[0].style.cssText;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now move the contents of the old dom object into the new dom object
|
||||||
|
jqOldObj.contents().appendTo(jqNewObj);
|
||||||
|
|
||||||
|
// finally replace the old object with the new one
|
||||||
|
jqOldObj.replaceWith(jqNewObj);
|
||||||
|
|
||||||
|
// preserve the range
|
||||||
|
if (range) {
|
||||||
|
if (range.startContainer == domobj) {
|
||||||
|
range.startContainer = jqNewObj.get(0);
|
||||||
|
}
|
||||||
|
if (range.endContainer == domobj) {
|
||||||
|
range.endContainer = jqNewObj.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jqNewObj;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String representation
|
||||||
|
* @return "Aloha.Selection"
|
||||||
|
*/
|
||||||
|
toString: function() {
|
||||||
|
return 'Aloha.Markup';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Aloha.Markup = new Aloha.Markup();
|
||||||
|
|
||||||
|
return Aloha.Markup;
|
||||||
|
|
||||||
|
});
|
115
vendor/assets/javascripts/locomotive/aloha/lib/aloha/message.js
vendored
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright © 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*//*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
['aloha/core', 'util/class', 'aloha/jquery'],
|
||||||
|
function( Aloha, Class, jQuery ) {
|
||||||
|
|
||||||
|
|
||||||
|
var
|
||||||
|
// Aloha = window.Aloha,
|
||||||
|
// Class = window.Class,
|
||||||
|
GENTICS = window.GENTICS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message Object
|
||||||
|
* @namespace Aloha
|
||||||
|
* @class Message
|
||||||
|
* @constructor
|
||||||
|
* @param {Object} data object which contains the parts of the message
|
||||||
|
* title: the title
|
||||||
|
* text: the message text to be displayed
|
||||||
|
* type: one of Aloha.Message.Type
|
||||||
|
* callback: callback function, which will be triggered after the message was confirmed, closed or accepted
|
||||||
|
*/
|
||||||
|
Aloha.Message = Class.extend({
|
||||||
|
_constructor: function (data) {
|
||||||
|
this.title = data.title;
|
||||||
|
this.text = data.text;
|
||||||
|
this.type = data.type;
|
||||||
|
this.callback = data.callback;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a textual representation of the message
|
||||||
|
* @return textual representation of the message
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
toString: function () {
|
||||||
|
return this.type + ': ' + this.message;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message types enum. Contains all allowed types of messages
|
||||||
|
* @property
|
||||||
|
*/
|
||||||
|
Aloha.Message.Type = {
|
||||||
|
// reserved for messages
|
||||||
|
// SUCCESS : 'success',
|
||||||
|
// INFO : 'info',
|
||||||
|
// WARN : 'warn',
|
||||||
|
// CRITICAL : 'critical',
|
||||||
|
CONFIRM : 'confirm', // confirm dialog, like js confirm()
|
||||||
|
ALERT : 'alert', // alert dialog like js alert()
|
||||||
|
WAIT : 'wait' // wait dialog with loading bar. has to be hidden via Aloha.hideMessage()
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the message line
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
Aloha.MessageLine = Class.extend({
|
||||||
|
messages: [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new message to the message line
|
||||||
|
* @param message message to add
|
||||||
|
* @return void
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
add: function(message) {
|
||||||
|
var messageline = '',
|
||||||
|
messagesLength = this.messages.length,
|
||||||
|
i;
|
||||||
|
|
||||||
|
// dummy implementation to add a message
|
||||||
|
this.messages[messagesLength] = message;
|
||||||
|
while(messagesLength > 4) {
|
||||||
|
this.messages.shift();
|
||||||
|
--messagesLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( i = 0; i < messagesLength; i++) {
|
||||||
|
messageline += this.messages[i].toString() + '<br/>';
|
||||||
|
}
|
||||||
|
jQuery('#gtx_aloha_messageline').html(messageline);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message Line Object
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
Aloha.MessageLine = new Aloha.MessageLine();
|
||||||
|
|
||||||
|
});
|
10
vendor/assets/javascripts/locomotive/aloha/lib/aloha/nls/de/i18n.js
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
define( {
|
||||||
|
'floatingmenu.tab.format': 'Formatieren',
|
||||||
|
'floatingmenu.tab.insert': 'Einf\u00fcgen',
|
||||||
|
'yes': 'Ja',
|
||||||
|
'no': 'Nein',
|
||||||
|
'cancel': 'Abbrechen',
|
||||||
|
'repository.no_item_found': 'Keinen Eintrag gefunden.',
|
||||||
|
'repository.loading': 'Es wird geladen',
|
||||||
|
'repository.no_items_found_yet': 'Noch keine Eintr\u00e4ge gefunden...'
|
||||||
|
} );
|
20
vendor/assets/javascripts/locomotive/aloha/lib/aloha/nls/i18n.js
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
define( {
|
||||||
|
'root': {
|
||||||
|
'plugin.abbr.floatingmenu.tab.abbr': 'Abbreviation',
|
||||||
|
'floatingmenu.tab.format': 'Format',
|
||||||
|
'floatingmenu.tab.insert': 'Insert',
|
||||||
|
'yes': 'Yes',
|
||||||
|
'no': 'No',
|
||||||
|
'cancel': 'Cancel',
|
||||||
|
'repository.no_item_found': 'No item found.',
|
||||||
|
'repository.loading': 'Loading',
|
||||||
|
'repository.no_items_found_yet': 'No items found yet...'
|
||||||
|
},
|
||||||
|
'de': true
|
||||||
|
/* 'eo': true,
|
||||||
|
'fi': true,
|
||||||
|
'fr': true,
|
||||||
|
'it': true,
|
||||||
|
'pl': true,
|
||||||
|
'ru': true*/
|
||||||
|
} );
|
106
vendor/assets/javascripts/locomotive/aloha/lib/aloha/observable.js
vendored
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright <EFBFBD> 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*//*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
['aloha/jquery'],
|
||||||
|
function(jQuery, undefined) {
|
||||||
|
|
||||||
|
|
||||||
|
var
|
||||||
|
$ = jQuery;
|
||||||
|
|
||||||
|
return {
|
||||||
|
_eventHandlers: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach a handler to an event
|
||||||
|
*
|
||||||
|
* @param {String} eventType A string containing the event name to bind to
|
||||||
|
* @param {Function} handler A function to execute each time the event is triggered
|
||||||
|
* @param {Object} scope Optional. Set the scope in which handler is executed
|
||||||
|
*/
|
||||||
|
bind: function(eventType, handler, scope) {
|
||||||
|
this._eventHandlers = this._eventHandlers || {};
|
||||||
|
if (!this._eventHandlers[eventType]) {
|
||||||
|
this._eventHandlers[eventType] = [];
|
||||||
|
}
|
||||||
|
this._eventHandlers[eventType].push({
|
||||||
|
handler: handler,
|
||||||
|
scope: (scope ? scope : window)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a previously-attached event handler
|
||||||
|
*
|
||||||
|
* @param {String} eventType A string containing the event name to unbind
|
||||||
|
* @param {Function} handler The function that is to be no longer executed. Optional. If not given, unregisters all functions for the given event.
|
||||||
|
*/
|
||||||
|
unbind: function(eventType, handler) {
|
||||||
|
this._eventHandlers = this._eventHandlers || {};
|
||||||
|
if (!this._eventHandlers[eventType]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!handler) {
|
||||||
|
// No handler function given, unbind all event handlers for the eventType
|
||||||
|
this._eventHandlers[eventType] = [];
|
||||||
|
} else {
|
||||||
|
this._eventHandlers[eventType] = $.grep(this._eventHandlers[eventType], function(element) {
|
||||||
|
if (element.handler === handler) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute all handlers attached to the given event type.
|
||||||
|
* All arguments except the eventType are directly passed to the callback function.
|
||||||
|
*
|
||||||
|
* @param (String} eventType A string containing the event name for which the event handlers should be invoked.
|
||||||
|
*/
|
||||||
|
trigger: function(eventType) {
|
||||||
|
this._eventHandlers = this._eventHandlers || {};
|
||||||
|
if (!this._eventHandlers[eventType]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// preparedArguments contains all arguments except the first one.
|
||||||
|
var preparedArguments = [];
|
||||||
|
$.each(arguments, function(i, argument) {
|
||||||
|
if (i>0) {
|
||||||
|
preparedArguments.push(argument);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$.each(this._eventHandlers[eventType], function(index, element) {
|
||||||
|
element.handler.apply(element.scope, preparedArguments);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all event handlers. Call this method when cleaning up.
|
||||||
|
*/
|
||||||
|
unbindAll: function() {
|
||||||
|
this._eventHandlers = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
276
vendor/assets/javascripts/locomotive/aloha/lib/aloha/plugin.js
vendored
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright © 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*//*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
['aloha/core', 'aloha/jquery', 'util/class', 'aloha/pluginmanager', 'aloha/console'],
|
||||||
|
function(Aloha, jQuery, Class, PluginManager, console ) {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract Plugin Object
|
||||||
|
* @namespace Aloha
|
||||||
|
* @class Plugin
|
||||||
|
* @constructor
|
||||||
|
* @param {String} pluginPrefix unique plugin prefix
|
||||||
|
*/
|
||||||
|
var Plugin = Class.extend({
|
||||||
|
|
||||||
|
name: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* contains the plugin's default settings object
|
||||||
|
* @cfg {Object} default settings for the plugin
|
||||||
|
*/
|
||||||
|
defaults: {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* contains the plugin's settings object
|
||||||
|
* @cfg {Object} settings the plugins settings stored in an object
|
||||||
|
*/
|
||||||
|
settings: {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Names of other plugins which must be loaded in order for this plugin to
|
||||||
|
* function.
|
||||||
|
* @cfg {Array}
|
||||||
|
*/
|
||||||
|
dependencies: [],
|
||||||
|
|
||||||
|
_constructor: function( name ) {
|
||||||
|
/**
|
||||||
|
* Settings of the plugin
|
||||||
|
*/
|
||||||
|
if (typeof name !== "string") {
|
||||||
|
console.error('Cannot initialise unnamed plugin, skipping');
|
||||||
|
} else {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if dependencies satisfied, false otherwise
|
||||||
|
*/
|
||||||
|
checkDependencies: function() {
|
||||||
|
var
|
||||||
|
dependenciesSatisfied = true,
|
||||||
|
that = this;
|
||||||
|
|
||||||
|
jQuery.each(this.dependencies, function() {
|
||||||
|
|
||||||
|
if (!Aloha.isPluginLoaded(this)) {
|
||||||
|
dependenciesSatisfied = false;
|
||||||
|
console.error('plugin.' + that.name, 'Required plugin "' + this + '" not found.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return dependenciesSatisfied;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init method of the plugin. Called from Aloha Core to initialize this plugin
|
||||||
|
* @return void
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
init: function() {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the configuration settings for an editable obj.
|
||||||
|
* Handles both conf arrays or conf objects
|
||||||
|
* <ul>
|
||||||
|
* <li>Array configuration parameters are:
|
||||||
|
* <pre>
|
||||||
|
* "list": {
|
||||||
|
* config : [ 'b', 'h1' ],
|
||||||
|
* editables : {
|
||||||
|
* '#title' : [ ],
|
||||||
|
* 'div' : [ 'b', 'i' ],
|
||||||
|
* '.article' : [ 'h1' ]
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The hash keys of the editables are css selectors. For a
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <div class="article">content</div>
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* the selectors 'div' and '.article' match and the returned configuration is
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* [ 'b', 'i', 'h1']
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The '#title' object would return an empty configuration.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* [ ]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* All other objects would get the 'config' configuration. If config is not set
|
||||||
|
* the plugin default configuration is returned.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* [ 'b', 'h1']
|
||||||
|
* </pre></li>
|
||||||
|
* <li>Object configuration parameters are :
|
||||||
|
* <pre>
|
||||||
|
* "image": {
|
||||||
|
* config : { 'img': { 'max_width': '50px',
|
||||||
|
* 'max_height': '50px' }},
|
||||||
|
* editables : {
|
||||||
|
* '#title': {},
|
||||||
|
* 'div': {'img': {}},
|
||||||
|
* '.article': {'img': { 'max_width': '150px',
|
||||||
|
* 'max_height': '150px' }}
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* The '#title' object would return an empty configuration.<br/>
|
||||||
|
* The 'div' object would return the default configuration.<br/>
|
||||||
|
* the '.article' would return :
|
||||||
|
* <pre>
|
||||||
|
* {'img': { 'max_width': '150px',
|
||||||
|
* 'max_height': '150px' }}
|
||||||
|
* </pre>
|
||||||
|
* </li>
|
||||||
|
*
|
||||||
|
* @param {jQuery} obj jQuery object of an Editable Object
|
||||||
|
* @return {Array} config A Array with configuration entries
|
||||||
|
*/
|
||||||
|
getEditableConfig: function (obj) {
|
||||||
|
var configObj = null,
|
||||||
|
configSpecified = false,
|
||||||
|
that = this;
|
||||||
|
|
||||||
|
if ( this.settings.editables ) {
|
||||||
|
// check if the editable's selector matches and if so add its configuration to object configuration
|
||||||
|
jQuery.each( this.settings.editables, function (selector, selectorConfig) {
|
||||||
|
if ( obj.is(selector) ) {
|
||||||
|
configSpecified = true;
|
||||||
|
if (selectorConfig instanceof Array) {
|
||||||
|
configObj = [];
|
||||||
|
configObj = jQuery.merge(configObj, selectorConfig);
|
||||||
|
} else if (typeof selectorConfig === "object") {
|
||||||
|
configObj = {};
|
||||||
|
for (var k in selectorConfig) {
|
||||||
|
if ( selectorConfig.hasOwnProperty(k) ) {
|
||||||
|
if (selectorConfig[k] instanceof Array) {
|
||||||
|
|
||||||
|
} else if (typeof selectorConfig[k] === "object") {
|
||||||
|
configObj[k] = {};
|
||||||
|
configObj[k] = jQuery.extend(true, configObj[k], that.config[k], selectorConfig[k]);
|
||||||
|
} else {
|
||||||
|
configObj[k] = selectorConfig[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
configObj = selectorConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// fall back to default configuration
|
||||||
|
if ( !configSpecified ) {
|
||||||
|
if ( typeof this.settings.config === 'undefined' || !this.settings.config ) {
|
||||||
|
configObj = this.config;
|
||||||
|
} else {
|
||||||
|
configObj = this.settings.config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return configObj;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make the given jQuery object (representing an editable) clean for saving
|
||||||
|
* @param obj jQuery object to make clean
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
makeClean: function ( obj ) {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a system-wide unique id out of a plugin-wide unique id by prefixing it with the plugin prefix
|
||||||
|
* @param id plugin-wide unique id
|
||||||
|
* @return system-wide unique id
|
||||||
|
* @hide
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
getUID: function(id) {
|
||||||
|
console.deprecated ('plugin', 'getUID() is deprecated. Use plugin.name instead.');
|
||||||
|
return this.name;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localize the given key for the plugin.
|
||||||
|
* @param key key to be localized
|
||||||
|
* @param replacements array of replacement strings
|
||||||
|
* @return localized string
|
||||||
|
* @hide
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
i18n: function(key, replacements) {
|
||||||
|
console.deprecated ('plugin', 'i18n() is deprecated. Use plugin.t() instead.');
|
||||||
|
return Aloha.i18n(this, key, replacements);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return string representation of the plugin, which is the prefix
|
||||||
|
* @return name
|
||||||
|
* @hide
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
toString: function() {
|
||||||
|
return this.name;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a plugin message to the logger
|
||||||
|
* @param level log level
|
||||||
|
* @param message log message
|
||||||
|
* @return void
|
||||||
|
* @hide
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
log: function (level, message) {
|
||||||
|
console.deprecated ('plugin', 'log() is deprecated. Use Aloha.console instead.');
|
||||||
|
console.log(level, this, message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static method used as factory to create plugins.
|
||||||
|
*
|
||||||
|
* @param {String} pluginName name of the plugin
|
||||||
|
* @param {Object} definition definition of the plugin, should have at least an "init" and "destroy" method.
|
||||||
|
*/
|
||||||
|
Plugin.create = function(pluginName, definition) {
|
||||||
|
|
||||||
|
var pluginInstance = new ( Plugin.extend( definition ) )( pluginName );
|
||||||
|
pluginInstance.settings = jQuery.extendObjects( true, pluginInstance.defaults, Aloha.settings[pluginName] );
|
||||||
|
PluginManager.register( pluginInstance );
|
||||||
|
|
||||||
|
return pluginInstance;
|
||||||
|
};
|
||||||
|
|
||||||
|
return Plugin;
|
||||||
|
});
|
146
vendor/assets/javascripts/locomotive/aloha/lib/aloha/pluginmanager.js
vendored
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright © 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*//*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Do not add dependencies that require depend on aloha/core
|
||||||
|
define(
|
||||||
|
[ 'aloha/jquery', 'util/class' ],
|
||||||
|
function( jQuery, Class ) {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Plugin Manager controls the lifecycle of all Aloha Plugins.
|
||||||
|
*
|
||||||
|
* @namespace Aloha
|
||||||
|
* @class PluginManager
|
||||||
|
* @singleton
|
||||||
|
*/
|
||||||
|
return new (Class.extend({
|
||||||
|
plugins: {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all registered plugins
|
||||||
|
* @return void
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
init: function(next, userPlugins) {
|
||||||
|
|
||||||
|
var
|
||||||
|
me = this,
|
||||||
|
globalSettings = ( Aloha && Aloha.settings ) ? Aloha.settings.plugins||{}: {},
|
||||||
|
i,
|
||||||
|
plugin,
|
||||||
|
pluginName;
|
||||||
|
|
||||||
|
// Global to local settings
|
||||||
|
for ( pluginName in globalSettings ) {
|
||||||
|
|
||||||
|
if ( globalSettings.hasOwnProperty( pluginName ) ) {
|
||||||
|
|
||||||
|
plugin = this.plugins[pluginName] || false;
|
||||||
|
|
||||||
|
if ( plugin ) {
|
||||||
|
plugin.settings = globalSettings[ pluginName ] || {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default: All loaded plugins are enabled
|
||||||
|
if ( !userPlugins.length ) {
|
||||||
|
|
||||||
|
for ( pluginName in this.plugins ) {
|
||||||
|
|
||||||
|
if ( this.plugins.hasOwnProperty( pluginName ) ) {
|
||||||
|
userPlugins.push( pluginName );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable Plugins specified by User
|
||||||
|
for ( i=0; i < userPlugins.length; ++i ) {
|
||||||
|
|
||||||
|
pluginName = userPlugins[ i ];
|
||||||
|
plugin = this.plugins[ pluginName ]||false;
|
||||||
|
|
||||||
|
if ( plugin ) {
|
||||||
|
|
||||||
|
plugin.settings = plugin.settings || {};
|
||||||
|
|
||||||
|
if ( typeof plugin.settings.enabled === 'undefined' ) {
|
||||||
|
plugin.settings.enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( plugin.settings.enabled ) {
|
||||||
|
if ( plugin.checkDependencies() ) {
|
||||||
|
plugin.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a plugin
|
||||||
|
* @param {Plugin} plugin plugin to register
|
||||||
|
*/
|
||||||
|
register: function( plugin ) {
|
||||||
|
|
||||||
|
if ( !plugin.name ) {
|
||||||
|
throw new Error( 'Plugin does not have an name.' );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this.plugins[ plugin.name ]) {
|
||||||
|
throw new Error( 'Already registered the plugin "' + plugin.name + '"!' );
|
||||||
|
}
|
||||||
|
|
||||||
|
this.plugins[ plugin.name ] = plugin;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass the given jQuery object, which represents an editable to all plugins, so that they can make the content clean (prepare for saving)
|
||||||
|
* @param obj jQuery object representing an editable
|
||||||
|
* @return void
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
makeClean: function(obj) {
|
||||||
|
var i, plugin;
|
||||||
|
// iterate through all registered plugins
|
||||||
|
for ( plugin in this.plugins ) {
|
||||||
|
if ( this.plugins.hasOwnProperty( plugin ) ) {
|
||||||
|
if (Aloha.Log.isDebugEnabled()) {
|
||||||
|
Aloha.Log.debug(this, 'Passing contents of HTML Element with id { ' + obj.attr('id') +
|
||||||
|
' } for cleaning to plugin { ' + plugin + ' }');
|
||||||
|
}
|
||||||
|
this.plugins[plugin].makeClean(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose a nice name for the Plugin Manager
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
toString: function() {
|
||||||
|
return 'pluginmanager';
|
||||||
|
}
|
||||||
|
|
||||||
|
}))();
|
||||||
|
});
|
3242
vendor/assets/javascripts/locomotive/aloha/lib/aloha/rangy-core.js
vendored
Normal file
59
vendor/assets/javascripts/locomotive/aloha/lib/aloha/registry.js
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor
|
||||||
|
* Author & Copyright (c) 2010 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.com/license.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registry base class.
|
||||||
|
* TODO: document that it also contains Observable.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
define(
|
||||||
|
['aloha/jquery', 'aloha/observable'],
|
||||||
|
function(jQuery, Observable) {
|
||||||
|
|
||||||
|
|
||||||
|
return Class.extend(Observable, {
|
||||||
|
|
||||||
|
_entries: null,
|
||||||
|
|
||||||
|
_constructor: function() {
|
||||||
|
this._entries = {};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @event register
|
||||||
|
* @param entry
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
register: function(id, entry) {
|
||||||
|
this._entries[id] = entry;
|
||||||
|
this.trigger('register', entry, id);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @event unregister
|
||||||
|
* @param odEntry
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
unregister: function(id) {
|
||||||
|
var oldEntry = this._entries[id];
|
||||||
|
delete this._entries[id];
|
||||||
|
this.trigger('unregister', oldEntry, id);
|
||||||
|
},
|
||||||
|
|
||||||
|
get: function(id) {
|
||||||
|
return this._entries[id];
|
||||||
|
},
|
||||||
|
|
||||||
|
has: function(id) {
|
||||||
|
return (this._entries[id] ? true : false);
|
||||||
|
},
|
||||||
|
|
||||||
|
getEntries: function() {
|
||||||
|
// clone the entries so the user does not accidentally modify our _entries object.
|
||||||
|
return jQuery.extend({}, this._entries);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
241
vendor/assets/javascripts/locomotive/aloha/lib/aloha/repository.js
vendored
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright (c) 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[ 'aloha/core', 'util/class', 'aloha/repositorymanager' ],
|
||||||
|
function( Aloha, Class, RepositoryManager ) {
|
||||||
|
|
||||||
|
|
||||||
|
// var
|
||||||
|
// $ = jQuery,
|
||||||
|
// GENTICS = window.GENTICS,
|
||||||
|
// Aloha = window.Aloha,
|
||||||
|
// Class = window.Class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract Repository Class. Implement that class for your own repository.
|
||||||
|
* @namespace Aloha.Repository
|
||||||
|
* @class Repository
|
||||||
|
* @constructor
|
||||||
|
* @param {String} repositoryId unique repository identifier
|
||||||
|
* @param {String} repositoryName (optional) is the displyed name for this Repository instance
|
||||||
|
*/
|
||||||
|
var AbstractRepository = Class.extend({
|
||||||
|
_constructor: function(repositoryId, repositoryName) {
|
||||||
|
/**
|
||||||
|
* @property repositoryId is the unique Id for this Repository instance
|
||||||
|
*/
|
||||||
|
this.repositoryId = repositoryId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* contains the repository's settings object
|
||||||
|
* @property settings {Object} the repository's settings stored in an object
|
||||||
|
*/
|
||||||
|
this.settings = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property repositoryName is the name for this Repository instance
|
||||||
|
*/
|
||||||
|
this.repositoryName = (repositoryName) ? repositoryName : repositoryId;
|
||||||
|
|
||||||
|
RepositoryManager.register(this);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init method of the repository. Called from Aloha Core to initialize this repository
|
||||||
|
* @return void
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
init: function() {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches a repository for object items matching queryString if none found returns null.
|
||||||
|
* The returned object items must be an array of Aloha.Repository.Object
|
||||||
|
*
|
||||||
|
<pre><code>
|
||||||
|
// simple delicious implementation
|
||||||
|
Aloha.Repositories.myRepository.query = function (params, callback) {
|
||||||
|
|
||||||
|
// make local var of this to use in ajax function
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
// handle each word as tag
|
||||||
|
var tags = p.queryString.split(' ');
|
||||||
|
|
||||||
|
// if we have a query and no tag matching return
|
||||||
|
if ( p.queryString && tags.length == 0 ) {
|
||||||
|
callback.call( that, []);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no handling of objectTypeFilter, filter, inFolderId, etc...
|
||||||
|
// in real implementation you should handle all parameters
|
||||||
|
|
||||||
|
jQuery.ajax({ type: "GET",
|
||||||
|
dataType: "jsonp",
|
||||||
|
url: 'http://feeds.delicious.com/v2/json/' + tags.join('+'),
|
||||||
|
success: function(data) {
|
||||||
|
var items = [];
|
||||||
|
// convert data to Aloha objects
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
if (typeof data[i] != 'function' ) {
|
||||||
|
items.push(new Aloha.Repository.Document ({
|
||||||
|
id: data[i].u,
|
||||||
|
name: data[i].d,
|
||||||
|
repositoryId: that.repositoryId,
|
||||||
|
type: 'website',
|
||||||
|
url: data[i].u
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback.call( that, items);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</code></pre>
|
||||||
|
*
|
||||||
|
* @param {object} params object with properties
|
||||||
|
* <div class="mdetail-params"><ul>
|
||||||
|
* <li><code> queryString</code> : String <div class="sub-desc">The query string for full text search</div></li>
|
||||||
|
* <li><code> objectTypeFilter</code> : array (optional) <div class="sub-desc">Object types that will be returned.</div></li>
|
||||||
|
* <li><code> filter</code> : array (optional) <div class="sub-desc">Attributes that will be returned.</div></li>
|
||||||
|
* <li><code> inFolderId</code> : boolean (optional) <div class="sub-desc">This is indicates whether or not a candidate object is a child-object of the folder object identified by the given inFolderId (objectId).</div></li>
|
||||||
|
* <li><code> inTreeId</code> : boolean (optional) <div class="sub-desc">This indicates whether or not a candidate object is a descendant-object of the folder object identified by the given inTreeId (objectId).</div></li>
|
||||||
|
* <li><code> orderBy</code> : array (optional) <div class="sub-desc">ex. [{lastModificationDate:’DESC’, name:’ASC’}]</div></li>
|
||||||
|
* <li><code> maxItems</code> : Integer (optional) <div class="sub-desc">number items to return as result</div></li>
|
||||||
|
* <li><code> skipCount</code> : Integer (optional) <div class="sub-desc">This is tricky in a merged multi repository scenario</div></li>
|
||||||
|
* <li><code> renditionFilter</code> : array (optional) <div class="sub-desc">Instead of termlist an array of kind or mimetype is expected. If null or array.length == 0 all renditions are returned. See http://docs.oasis-open.org/cmis/CMIS/v1.0/cd04/cmis-spec-v1.0.html#_Ref237323310 for renditionFilter</div></li>
|
||||||
|
* </ul></div>
|
||||||
|
* @param {function} callback this method must be called with all result items</div></li>
|
||||||
|
*/
|
||||||
|
query: null,
|
||||||
|
/*
|
||||||
|
query: function( params, callback ) {
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback([]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all children of a given motherId.
|
||||||
|
*
|
||||||
|
* @param {object} params object with properties
|
||||||
|
* <div class="mdetail-params"><ul>
|
||||||
|
* <li><code> objectTypeFilter</code> : array (optional) <div class="sub-desc">Object types that will be returned.</div></li>
|
||||||
|
* <li><code> filter</code> : array (optional) <div class="sub-desc">Attributes that will be returned.</div></li>
|
||||||
|
* <li><code> inFolderId</code> : boolean (optional) <div class="sub-desc">This indicates whether or not a candidate object is a child-object of the folder object identified by the given inFolderId (objectId).</div></li>
|
||||||
|
* <li><code> orderBy</code> : array (optional) <div class="sub-desc">ex. [{lastModificationDate:’DESC’, name:’ASC’}]</div></li>
|
||||||
|
* <li><code> maxItems</code> : Integer (optional) <div class="sub-desc">number items to return as result</div></li>
|
||||||
|
* <li><code> skipCount</code> : Integer (optional) <div class="sub-desc">This is tricky in a merged multi repository scenario</div></li>
|
||||||
|
* <li><code> renditionFilter</code> : array (optional) <div class="sub-desc">Instead of termlist an array of kind or mimetype is expected. If null or array.length == 0 all renditions are returned. See http://docs.oasis-open.org/cmis/CMIS/v1.0/cd04/cmis-spec-v1.0.html#_Ref237323310 for renditionFilter</div></li>
|
||||||
|
* </ul></div>
|
||||||
|
* @param {function} callback this method must be called with all result items
|
||||||
|
*/
|
||||||
|
getChildren: null,
|
||||||
|
/*
|
||||||
|
getChildren: function( params, callback ) {
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
callback([]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make the given jQuery object (representing an object marked as object of this type)
|
||||||
|
* clean. All attributes needed for handling should be removed.
|
||||||
|
*
|
||||||
|
<pre><code>
|
||||||
|
Aloha.Repositories.myRepository.makeClean = function (obj) {
|
||||||
|
obj.removeAttr('data-myRepository-name');
|
||||||
|
};
|
||||||
|
</code></pre>
|
||||||
|
* @param {jQuery} obj jQuery object to make clean
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
makeClean: function (obj) {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will be called when a user chooses an item from a repository and wants
|
||||||
|
* to insert this item in his content.
|
||||||
|
* Mark or modify an object as needed by that repository for handling, processing or identification.
|
||||||
|
* Objects can be any DOM object as A, SPAN, ABBR, etc. or
|
||||||
|
* special objects such as aloha-aloha_block elements.
|
||||||
|
* (see http://dev.w3.org/html5/spec/elements.html#embedding-custom-non-visible-data)
|
||||||
|
*
|
||||||
|
<pre><code>
|
||||||
|
Aloha.Repositories.myRepository.markObject = function (obj, resourceItem) {
|
||||||
|
obj.attr('data-myRepository-name').text(resourceItem.name);
|
||||||
|
};
|
||||||
|
</code></pre>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param obj jQuery target object to which the repositoryItem will be applied
|
||||||
|
* @param repositoryItem The selected item. A class constructed from Document or Folder.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
markObject: function (obj, repositoryItem) {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a template for rendering objects of this repository
|
||||||
|
* @param {String} template
|
||||||
|
* @return void
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
|
setTemplate: function (template) {
|
||||||
|
if (template) {
|
||||||
|
this.template = template;
|
||||||
|
} else {
|
||||||
|
this.template = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the repository has a template
|
||||||
|
* @return {boolean} true when the repository has a template, false if not
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
|
hasTemplate: function () {
|
||||||
|
return this.template ? true : false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the parsed template
|
||||||
|
* @return {Object} parsed template
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
|
getTemplate: function () {
|
||||||
|
return this.template;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the repositoryItem with given id
|
||||||
|
* @param itemId {String} id of the repository item to fetch
|
||||||
|
* @param callback {function} callback function
|
||||||
|
* @return {Aloha.Repository.Object} item with given id
|
||||||
|
*/
|
||||||
|
getObjectById: function ( itemId, callback ) { return true; }
|
||||||
|
});
|
||||||
|
|
||||||
|
// expose the AbstractRepository
|
||||||
|
Aloha.AbstractRepository = AbstractRepository;
|
||||||
|
|
||||||
|
return AbstractRepository;
|
||||||
|
});
|
571
vendor/assets/javascripts/locomotive/aloha/lib/aloha/repositorymanager.js
vendored
Normal file
@ -0,0 +1,571 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor Project http://aloha-editor.org
|
||||||
|
* Copyright (c) 2010-2011 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Contributors http://aloha-editor.org/contribution.php
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.org/license.html
|
||||||
|
*//*
|
||||||
|
* Aloha Editor is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.*
|
||||||
|
*
|
||||||
|
* Aloha Editor is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define( [
|
||||||
|
'aloha/core',
|
||||||
|
'util/class',
|
||||||
|
'aloha/jquery',
|
||||||
|
'aloha/console'
|
||||||
|
], function ( Aloha, Class, jQuery, console ) {
|
||||||
|
|
||||||
|
|
||||||
|
var undefined = void 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repository Manager
|
||||||
|
* @namespace Aloha
|
||||||
|
* @class RepositoryManager
|
||||||
|
* @singleton
|
||||||
|
*/
|
||||||
|
Aloha.RepositoryManager = Class.extend( {
|
||||||
|
|
||||||
|
repositories : [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all registered repositories
|
||||||
|
* Before we invoke each repositories init method, we merge the global
|
||||||
|
* repository settings into each repository's custom settings
|
||||||
|
*
|
||||||
|
* @todo: Write unit tests to check that global and custom settings are
|
||||||
|
* applied correctly
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
init: function () {
|
||||||
|
var repositories = this.repositories,
|
||||||
|
i = 0,
|
||||||
|
j = repositories.length,
|
||||||
|
repository,
|
||||||
|
settings;
|
||||||
|
|
||||||
|
if ( Aloha.settings && Aloha.settings.repositories ) {
|
||||||
|
settings = Aloha.settings.repositories;
|
||||||
|
} else {
|
||||||
|
settings = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( ; i < j; ++i ) {
|
||||||
|
repository = repositories[ i ];
|
||||||
|
|
||||||
|
if ( !repository.settings ) {
|
||||||
|
repository.settings = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( settings[ repository.repositoryId ] ) {
|
||||||
|
jQuery.extend(
|
||||||
|
repository.settings,
|
||||||
|
settings[ repository.repositoryId ]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
repository.init();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a Repository.
|
||||||
|
*
|
||||||
|
* @param {Aloha.Repository} repository Repository to register
|
||||||
|
*/
|
||||||
|
register: function ( repository ) {
|
||||||
|
if ( !this.getRepository( repository.repositoryId ) ) {
|
||||||
|
this.repositories.push( repository );
|
||||||
|
} else {
|
||||||
|
console.warn( this, 'A repository with name { ' +
|
||||||
|
repository.repositoryId +
|
||||||
|
' } already registerd. Ignoring this.' );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the repository object identified by repositoryId.
|
||||||
|
*
|
||||||
|
* @param {String} repositoryId - the name of the repository
|
||||||
|
* @return {?Aloha.Repository} a repository or null if name not found
|
||||||
|
*/
|
||||||
|
getRepository: function ( repositoryId ) {
|
||||||
|
var repositories = this.repositories,
|
||||||
|
i = 0,
|
||||||
|
j = repositories.length;
|
||||||
|
|
||||||
|
for ( ; i < j; ++i ) {
|
||||||
|
if ( repositories[ i ].repositoryId == repositoryId ) {
|
||||||
|
return repositories[ i ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches a all repositories for repositoryObjects matching query and
|
||||||
|
* repositoryObjectType.
|
||||||
|
*
|
||||||
|
<pre><code>
|
||||||
|
var params = {
|
||||||
|
queryString: 'hello',
|
||||||
|
objectTypeFilter: ['website'],
|
||||||
|
filter: null,
|
||||||
|
inFolderId: null,
|
||||||
|
orderBy: null,
|
||||||
|
maxItems: null,
|
||||||
|
skipCount: null,
|
||||||
|
renditionFilter: null,
|
||||||
|
repositoryId: null
|
||||||
|
};
|
||||||
|
Aloha.RepositoryManager.query( params, function( items ) {
|
||||||
|
// do something with the result items
|
||||||
|
console.log(items);
|
||||||
|
});
|
||||||
|
</code></pre>
|
||||||
|
*
|
||||||
|
* @param {Object <String,Mixed>} params object with properties
|
||||||
|
* <div class="mdetail-params"><ul>
|
||||||
|
* <li><code> queryString</code> : String <div class="sub-desc">The query string for full text search</div></li>
|
||||||
|
* <li><code> objectTypeFilter</code> : array (optional) <div class="sub-desc">Object types that will be returned.</div></li>
|
||||||
|
* <li><code> filter</code> : array (optional) <div class="sub-desc">Attributes that will be returned.</div></li>
|
||||||
|
* <li><code> inFolderId</code> : boolean (optional) <div class="sub-desc">This is indicates whether or not a candidate object is a child-object of the folder object identified by the given inFolderId (objectId).</div></li>
|
||||||
|
* <li><code> inTreeId</code> : boolean (optional) <div class="sub-desc">This indicates whether or not a candidate object is a descendant-object of the folder object identified by the given inTreeId (objectId).</div></li>
|
||||||
|
* <li><code> orderBy</code> : array (optional) <div class="sub-desc">ex. [{lastModificationDate:’DESC’, name:’ASC’}]</div></li>
|
||||||
|
* <li><code> maxItems</code> : Integer (optional) <div class="sub-desc">number items to return as result</div></li>
|
||||||
|
* <li><code> skipCount</code> : Integer (optional) <div class="sub-desc">This is tricky in a merged multi repository scenario</div></li>
|
||||||
|
* <li><code> renditionFilter</code> : array (optional) <div class="sub-desc">Instead of termlist an array of kind or mimetype is expected. If null or array.length == 0 all renditions are returned. See http://docs.oasis-open.org/cmis/CMIS/v1.0/cd04/cmis-spec-v1.0.html#_Ref237323310 for renditionFilter</div></li>
|
||||||
|
* </ul></div>
|
||||||
|
* @param {Function} callback - defines a callback function( items ) which will be called when all repositories returned their results or after a time out of 5sec.
|
||||||
|
* "items" is an Array of objects construced with Document/Folder.
|
||||||
|
* @void
|
||||||
|
*/
|
||||||
|
query: function ( params, callback ) {
|
||||||
|
var that = this,
|
||||||
|
repo,
|
||||||
|
// The merged results, collected from repository responses
|
||||||
|
allitems = [],
|
||||||
|
// the merge metainfo, collected from repository responses
|
||||||
|
allmetainfo = { numItems: 0, hasMoreItems: false },
|
||||||
|
// The set of repositories towhich we want to delegate work
|
||||||
|
repositories = [],
|
||||||
|
// A counting semaphore (working in reverse, ie: 0 means free)
|
||||||
|
numOpenCallbacks = 0,
|
||||||
|
// When this timer times-out, whatever has been collected in
|
||||||
|
// allitems will be returned to the calling client, and
|
||||||
|
// numOpenCallbacks will be reset to 0
|
||||||
|
timer,
|
||||||
|
i, j,
|
||||||
|
/**
|
||||||
|
* Invoked by each repository when it wants to present its
|
||||||
|
* results to the manager.
|
||||||
|
*
|
||||||
|
* Collects the results from each repository, and decrements
|
||||||
|
* the numOpenCallbacks semaphore to indicate that there is one
|
||||||
|
* less repository for which we are waiting a reponse.
|
||||||
|
*
|
||||||
|
* If a repository invokes this callback after all
|
||||||
|
* openCallbacks have been closed (ie: numOpenCallbacks == 0),
|
||||||
|
* then the repository was too late ("missed the ship"), and
|
||||||
|
* will be ignored.
|
||||||
|
*
|
||||||
|
* If numOpenCallbacks decrements to 0 during this call, it
|
||||||
|
* means that the the manager is ready to report the results
|
||||||
|
* back to the client through the queryCallback method.
|
||||||
|
*
|
||||||
|
* nb: "this" is reference to the calling repository.
|
||||||
|
*
|
||||||
|
* @param {Array} items - Results returned by the repository
|
||||||
|
* @param {Object<String,Number>} metainfo - optional Metainfo returned by the repository
|
||||||
|
*/
|
||||||
|
processResults = function ( items, metainfo ) {
|
||||||
|
if ( numOpenCallbacks == 0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var j = items ? items.length : 0;
|
||||||
|
|
||||||
|
if ( j ) {
|
||||||
|
// Add the repositoryId for each item if a negligent
|
||||||
|
// repository did not do so.
|
||||||
|
if ( !items[ 0 ].repositoryId ) {
|
||||||
|
var repoId = this.repositoryId;
|
||||||
|
for ( var i = 0; i < j; ++i ) {
|
||||||
|
items[ i ].repositoryId = repoId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jQuery.merge( allitems, items );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( metainfo && allmetainfo ) {
|
||||||
|
if ( jQuery.isNumeric( metainfo.numItems ) &&
|
||||||
|
jQuery.isNumeric( allmetainfo.numItems ) ) {
|
||||||
|
allmetainfo.numItems += metainfo.numItems;
|
||||||
|
} else {
|
||||||
|
allmetainfo.numItems = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( jQuery.isBoolean( metainfo.hasMoreItems ) &&
|
||||||
|
jQuery.isBoolean( allmetainfo.hasMoreItems ) ) {
|
||||||
|
allmetainfo.hasMoreItems = allmetainfo.hasMoreItems || metainfo.hasMoreItems;
|
||||||
|
} else {
|
||||||
|
allmetainfo.hasMoreItems = undefined;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// at least one repository did not return metainfo, so
|
||||||
|
// we have no aggregated metainfo at all
|
||||||
|
allmetainfo = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO how to return the metainfo here?
|
||||||
|
if ( --numOpenCallbacks == 0 ) {
|
||||||
|
that.queryCallback( callback, allitems, allmetainfo, timer );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Unless the calling client specifies otherwise, we will wait a
|
||||||
|
// maximum of 5 seconds for all repositories to be queried and
|
||||||
|
// respond. 5 seconds is deemed to be the reasonable time to wait
|
||||||
|
// when querying the repository manager in the context of something
|
||||||
|
// like autocomplete
|
||||||
|
var timeout = parseInt( params.timeout, 10 ) || 5000;
|
||||||
|
timer = setTimeout( function() {
|
||||||
|
numOpenCallbacks = 0;
|
||||||
|
that.queryCallback( callback, allitems, allmetainfo, timer );
|
||||||
|
}, timeout );
|
||||||
|
|
||||||
|
// If repositoryId or a list of repository ids, is not specified in
|
||||||
|
// the params object, then we will query all registered
|
||||||
|
// repositories
|
||||||
|
if ( params.repositoryId ) {
|
||||||
|
repositories.push( this.getRepository( params.repositoryId ) );
|
||||||
|
} else {
|
||||||
|
repositories = this.repositories;
|
||||||
|
}
|
||||||
|
|
||||||
|
j = repositories.length;
|
||||||
|
|
||||||
|
var repoQueue = [];
|
||||||
|
|
||||||
|
// We need to know how many callbacks we will open before invoking
|
||||||
|
// the query method on each, so that as soon as the first one does
|
||||||
|
// callback, the correct number of open callbacks will be available
|
||||||
|
// to check.
|
||||||
|
|
||||||
|
for ( i = 0; i < j; ++i ) {
|
||||||
|
repo = repositories[ i ];
|
||||||
|
|
||||||
|
if ( typeof repo.query == 'function' ) {
|
||||||
|
++numOpenCallbacks;
|
||||||
|
repoQueue.push( repo );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
j = repoQueue.length;
|
||||||
|
|
||||||
|
for ( i = 0; i < j; ++i ) {
|
||||||
|
repo = repoQueue[ i ];
|
||||||
|
repo.query(
|
||||||
|
params,
|
||||||
|
function () {
|
||||||
|
processResults.apply( repo, arguments );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If none of the repositories implemented the query method, then
|
||||||
|
// don't wait for the timeout, simply report to the client
|
||||||
|
if ( numOpenCallbacks == 0 ) {
|
||||||
|
this.queryCallback( callback, allitems, allmetainfo, timer );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Passes all the results we have collected to the client through the
|
||||||
|
* callback it specified
|
||||||
|
*
|
||||||
|
* @param {Function} callback - Callback specified by client when
|
||||||
|
* invoking the query method
|
||||||
|
* @param {Array} items - Results, collected from all repositories
|
||||||
|
* @param {Object<String,Number>} metainfo - optional object containing metainfo
|
||||||
|
* @param {Timer} timer - We need to clear this timer
|
||||||
|
* @return void
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
queryCallback: function ( callback, items, metainfo, timer ) {
|
||||||
|
if ( timer ) {
|
||||||
|
clearTimeout( timer );
|
||||||
|
timer = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement sorting based on repository specification
|
||||||
|
// sort items by weight
|
||||||
|
//items.sort(function (a,b) {
|
||||||
|
// return (b.weight || 0) - (a.weight || 0);
|
||||||
|
//});
|
||||||
|
|
||||||
|
// prepare result data for the JSON Reader
|
||||||
|
var result = {
|
||||||
|
items : items,
|
||||||
|
results : items.length
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( metainfo ) {
|
||||||
|
result.numItems = metainfo.numItems;
|
||||||
|
result.hasMoreItems = metainfo.hasMoreItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback.call( this, result );
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo: This method needs to be covered with some unit tests
|
||||||
|
*
|
||||||
|
* Returns children items. (see query for an example)
|
||||||
|
* @param {Object<String,Mixed>} params - object with properties
|
||||||
|
* <div class="mdetail-params"><ul>
|
||||||
|
* <li><code> objectTypeFilter</code> : array (optional) <div class="sub-desc">Object types that will be returned.</div></li>
|
||||||
|
* <li><code> filter</code> : array (optional) <div class="sub-desc">Attributes that will be returned.</div></li>
|
||||||
|
* <li><code> inFolderId</code> : boolean (optional) <div class="sub-desc">This indicates whether or not a candidate object is a child-object of the folder object identified by the given inFolderId (objectId).</div></li>
|
||||||
|
* <li><code> orderBy</code> : array (optional) <div class="sub-desc">ex. [{lastModificationDate:’DESC’, name:’ASC’}]</div></li>
|
||||||
|
* <li><code> maxItems</code> : Integer (optional) <div class="sub-desc">number items to return as result</div></li>
|
||||||
|
* <li><code> skipCount</code> : Integer (optional) <div class="sub-desc">This is tricky in a merged multi repository scenario</div></li>
|
||||||
|
* <li><code> renditionFilter</code> : array (optional) <div class="sub-desc">Instead of termlist an array of kind or mimetype is expected. If null or array.length == 0 all renditions are returned. See http://docs.oasis-open.org/cmis/CMIS/v1.0/cd04/cmis-spec-v1.0.html#_Ref237323310 for renditionFilter</div></li>
|
||||||
|
* </ul></div>
|
||||||
|
* @param {Function} callback - defines a callback function( items ) which will be called when all repositories returned their results or after a time out of 5sec.
|
||||||
|
* "items" is an Array of objects construced with Document/Folder.
|
||||||
|
* @void
|
||||||
|
*/
|
||||||
|
getChildren: function ( params, callback ) {
|
||||||
|
var that = this,
|
||||||
|
repo,
|
||||||
|
// The marged results, collected from repository responses
|
||||||
|
allitems = [],
|
||||||
|
// The set of repositories towhich we want to delegate work
|
||||||
|
repositories = [],
|
||||||
|
// A counting semaphore (working in reverse, ie: 0 means free)
|
||||||
|
numOpenCallbacks = 0,
|
||||||
|
// When this timer times-out, whatever has been collected in
|
||||||
|
// allitems will be returned to the calling client, and
|
||||||
|
// numOpenCallbacks will be reset to 0
|
||||||
|
timer,
|
||||||
|
i, j,
|
||||||
|
processResults = function ( items ) {
|
||||||
|
if ( numOpenCallbacks == 0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
jQuery.merge( allitems, items );
|
||||||
|
|
||||||
|
if ( --numOpenCallbacks == 0 ) {
|
||||||
|
that.getChildrenCallback( callback, allitems, timer );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the inFolderId is the default id of 'aloha', then return all
|
||||||
|
// registered repositories
|
||||||
|
if ( params.inFolderId == 'aloha' ) {
|
||||||
|
var repoFilter = params.repositoryFilter,
|
||||||
|
hasRepoFilter = ( repoFilter && repoFilter.length );
|
||||||
|
|
||||||
|
j = this.repositories.length;
|
||||||
|
|
||||||
|
for ( i = 0; i < j; ++i ) {
|
||||||
|
var repo = this.repositories[ i ];
|
||||||
|
if ( !hasRepoFilter || jQuery.inArray( repo.repositoryId, repoFilter ) > -1 ) {
|
||||||
|
repositories.push(
|
||||||
|
new Aloha.RepositoryFolder( {
|
||||||
|
id : repo.repositoryId,
|
||||||
|
name : repo.repositoryName,
|
||||||
|
repositoryId : repo.repositoryId,
|
||||||
|
type : 'repository',
|
||||||
|
hasMoreItems : true
|
||||||
|
} )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
that.getChildrenCallback( callback, repositories, null );
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
repositories = this.repositories;
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeout = parseInt( params.timeout, 10 ) || 5000;
|
||||||
|
timer = setTimeout( function () {
|
||||||
|
numOpenCallbacks = 0;
|
||||||
|
that.getChildrenCallback( callback, allitems, timer );
|
||||||
|
}, timeout );
|
||||||
|
|
||||||
|
j = repositories.length;
|
||||||
|
|
||||||
|
for ( i = 0; i < j; ++i ) {
|
||||||
|
repo = repositories[ i ];
|
||||||
|
|
||||||
|
if ( typeof repo.getChildren === 'function' ) {
|
||||||
|
++numOpenCallbacks;
|
||||||
|
|
||||||
|
repo.getChildren(
|
||||||
|
params,
|
||||||
|
function () {
|
||||||
|
processResults.apply( repo, arguments );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( numOpenCallbacks == 0 ) {
|
||||||
|
this.getChildrenCallback( callback, allitems, timer );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns results for getChildren to calling client
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
getChildrenCallback: function ( callback, items, timer ) {
|
||||||
|
if ( timer ) {
|
||||||
|
clearTimeout( timer );
|
||||||
|
timer = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback.call( this, items );
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fixme: Not tested, but the code for this function does not seem to
|
||||||
|
* compute repository.makeClean will be undefined
|
||||||
|
*
|
||||||
|
* @todo: Rewrite this function header comment so that is clearer
|
||||||
|
*
|
||||||
|
* Pass an object, which represents an marked repository to corresponding
|
||||||
|
* repository, so that it can make the content clean (prepare for saving)
|
||||||
|
*
|
||||||
|
* @param {jQuery} obj - representing an editable
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
makeClean: function ( obj ) {
|
||||||
|
// iterate through all registered repositories
|
||||||
|
var that = this,
|
||||||
|
repository = {},
|
||||||
|
i = 0,
|
||||||
|
j = that.repositories.length;
|
||||||
|
|
||||||
|
// find all repository tags
|
||||||
|
obj.find( '[data-gentics-aloha-repository=' + this.prefix + ']' )
|
||||||
|
.each( function () {
|
||||||
|
for ( ; i < j; ++i ) {
|
||||||
|
repository.makeClean( obj );
|
||||||
|
}
|
||||||
|
console.debug( that,
|
||||||
|
'Passing contents of HTML Element with id { ' +
|
||||||
|
this.attr('id') + ' } for cleaning to repository { ' +
|
||||||
|
repository.repositoryId + ' }' );
|
||||||
|
repository.makeClean( this );
|
||||||
|
} );
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Markes an object as repository of this type and with this item.id.
|
||||||
|
* Objects can be any DOM objects as A, SPAN, ABBR, etc. or
|
||||||
|
* special objects such as aloha-aloha_block elements.
|
||||||
|
* This method marks the target obj with two private attributes:
|
||||||
|
* (see http://dev.w3.org/html5/spec/elements.html#embedding-custom-non-visible-data)
|
||||||
|
* * data-gentics-aloha-repository: stores the repositoryId
|
||||||
|
* * data-gentics-aloha-object-id: stores the object.id
|
||||||
|
*
|
||||||
|
* @param {DOMObject} obj - DOM object to mark
|
||||||
|
* @param {Aloha.Repository.Object} item - the item which is applied to obj,
|
||||||
|
* if set to null, the data-GENTICS-... attributes are removed
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
markObject: function ( obj, item ) {
|
||||||
|
if ( !obj ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( item ) {
|
||||||
|
var repository = this.getRepository( item.repositoryId );
|
||||||
|
|
||||||
|
if ( repository ) {
|
||||||
|
jQuery( obj ).attr( {
|
||||||
|
'data-gentics-aloha-repository' : item.repositoryId,
|
||||||
|
'data-gentics-aloha-object-id' : item.id
|
||||||
|
} );
|
||||||
|
|
||||||
|
repository.markObject( obj, item );
|
||||||
|
} else {
|
||||||
|
console.error( this,
|
||||||
|
'Trying to apply a repository { ' + item.name +
|
||||||
|
' } to an object, but item has no repositoryId.' );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// remove the data attributes
|
||||||
|
jQuery( obj ).removeAttr( 'data-gentics-aloha-repository' );
|
||||||
|
jQuery( obj ).removeAttr( 'data-gentics-aloha-object-id' );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the object for which the given DOM object is marked from the
|
||||||
|
* repository.
|
||||||
|
*
|
||||||
|
* @param {DOMObject} obj - DOM object which probably is marked
|
||||||
|
* @param {Function} callback - callback function
|
||||||
|
*/
|
||||||
|
getObject: function ( obj, callback ) {
|
||||||
|
var that = this,
|
||||||
|
$obj = jQuery( obj ),
|
||||||
|
repository = this.getRepository( $obj.attr( 'data-gentics-aloha-repository' ) ),
|
||||||
|
itemId = $obj.attr( 'data-gentics-aloha-object-id' );
|
||||||
|
|
||||||
|
if ( repository && itemId ) {
|
||||||
|
// initialize the item cache (per repository) if not already done
|
||||||
|
this.itemCache = this.itemCache || [];
|
||||||
|
this.itemCache[ repository.repositoryId ] = this.itemCache[ repository.repositoryId ] || [];
|
||||||
|
|
||||||
|
// when the item is cached, we just call the callback method
|
||||||
|
if ( this.itemCache[ repository.repositoryId ][ itemId ] ) {
|
||||||
|
callback.call( this, [ this.itemCache[ repository.repositoryId ][ itemId ] ] );
|
||||||
|
} else {
|
||||||
|
// otherwise we get the object from the repository
|
||||||
|
repository.getObjectById( itemId, function ( items ) {
|
||||||
|
// make sure the item is in the cache (for subsequent calls)
|
||||||
|
that.itemCache[ repository.repositoryId ][ itemId ] = items[ 0 ];
|
||||||
|
callback.call( this, items );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {String} name of repository manager object
|
||||||
|
*/
|
||||||
|
toString: function () {
|
||||||
|
return 'repositorymanager';
|
||||||
|
}
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
Aloha.RepositoryManager = new Aloha.RepositoryManager();
|
||||||
|
|
||||||
|
// We return the constructor, not the instance of Aloha.RepositoryManager
|
||||||
|
return Aloha.RepositoryManager;
|
||||||
|
} );
|
146
vendor/assets/javascripts/locomotive/aloha/lib/aloha/repositoryobjects.js
vendored
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/*!
|
||||||
|
* This file is part of Aloha Editor
|
||||||
|
* Author & Copyright (c) 2010 Gentics Software GmbH, aloha@gentics.com
|
||||||
|
* Licensed unter the terms of http://www.aloha-editor.com/license.html
|
||||||
|
*/
|
||||||
|
define(
|
||||||
|
[ 'aloha/core', 'util/class'],
|
||||||
|
function( Aloha, Class ) {
|
||||||
|
|
||||||
|
|
||||||
|
var
|
||||||
|
// Aloha = window.Aloha,
|
||||||
|
// Class = window.Class,
|
||||||
|
GENTICS = window.GENTICS;
|
||||||
|
|
||||||
|
Aloha.RepositoryObject = function() {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace Aloha.Repository
|
||||||
|
* @class Document
|
||||||
|
* @constructor
|
||||||
|
*
|
||||||
|
* Abstract Document suitable for most Objects.<br /><br />
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
<pre><code>
|
||||||
|
var item = new Aloha.Repository.Document({
|
||||||
|
id: 1,
|
||||||
|
repositoryId: 'myrepository',
|
||||||
|
name: 'Aloha Editor - The HTML5 Editor',
|
||||||
|
type: 'website',
|
||||||
|
url:'http://aloha-editor.com',
|
||||||
|
});
|
||||||
|
</code></pre>
|
||||||
|
*
|
||||||
|
* @param {Object} properties An object with the data.
|
||||||
|
* <div class="mdetail-params"><ul>
|
||||||
|
* <li><code>id</code> : String <div class="sub-desc">Unique identifier</div></li>
|
||||||
|
* <li><code>repositoryId</code> : String <div class="sub-desc">Unique repository identifier</div></li>
|
||||||
|
* <li><code>name</code> : String <div class="sub-desc">Name of the object. This name is used to display</div></li>
|
||||||
|
* <li><code>type</code> : String <div class="sub-desc">The specific object type</div></li>
|
||||||
|
* <li><code>partentId</code> : String (optional) <div class="sub-desc"></div></li>
|
||||||
|
* <li><code>mimetype</code> : String (optional) <div class="sub-desc">MIME type of the Content Stream</div></li>
|
||||||
|
* <li><code>filename</code> : String (optional) <div class="sub-desc">File name of the Content Stream</div></li>
|
||||||
|
* <li><code>length</code> : String (optional) <div class="sub-desc">Length of the content stream (in bytes)</div></li>
|
||||||
|
* <li><code>url</code> : String (optional) <div class="sub-desc">URL of the content stream</div></li>
|
||||||
|
* <li><code>renditions</code> : Array (optional) <div class="sub-desc">Array of different renditions of this object</div></li>
|
||||||
|
* <li><code>localName</code> : String (optional) <div class="sub-desc">Name of the object. This name is used internally</div></li>
|
||||||
|
* <li><code>createdBy</code> : String (optional) <div class="sub-desc">User who created the object</div></li>
|
||||||
|
* <li><code>creationDate</code> : Date (optional) <div class="sub-desc">DateTime when the object was created</div></li>
|
||||||
|
* <li><code>lastModifiedBy</code> : String (optional) <div class="sub-desc">User who last modified the object</div></li>
|
||||||
|
* <li><code>lastModificationDate</code> : Date (optional) <div class="sub-desc">DateTime when the object was last modified</div></li>
|
||||||
|
* </ul></div>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Aloha.RepositoryDocument = Class.extend({
|
||||||
|
_constructor: function (properties) {
|
||||||
|
|
||||||
|
var p = properties;
|
||||||
|
|
||||||
|
this.type = 'document';
|
||||||
|
|
||||||
|
// Basic error checking for MUST attributes
|
||||||
|
if (!p.id ||
|
||||||
|
!p.name ||
|
||||||
|
!p.repositoryId
|
||||||
|
) {
|
||||||
|
// Aloha.Log.error(this, "No valid Aloha Object. Missing MUST property");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GENTICS.Utils.applyProperties(this, properties);
|
||||||
|
|
||||||
|
this.baseType = 'document';
|
||||||
|
}
|
||||||
|
// /**
|
||||||
|
// * Not implemented method to generate this JS API doc correctly.
|
||||||
|
// */
|
||||||
|
// ,empty = function() }
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace Aloha.Repository
|
||||||
|
* @class Folder
|
||||||
|
* @constructor
|
||||||
|
* Abstract Folder suitable for most strucural Objects.<br /><br />
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
<pre><code>
|
||||||
|
var item = new Aloha.Repository.Folder({
|
||||||
|
id: 2,
|
||||||
|
repositoryId: 'myrepository',
|
||||||
|
name: 'images',
|
||||||
|
type: 'directory',
|
||||||
|
parentId:'/www'
|
||||||
|
});
|
||||||
|
</code></pre>
|
||||||
|
* @param {Object} properties An object with the data.
|
||||||
|
* <div class="mdetail-params"><ul>
|
||||||
|
* <li><code>id</code> : String <div class="sub-desc">Unique identifier</div></li>
|
||||||
|
* <li><code>repositoryId</code> : String <div class="sub-desc">Unique repository identifier</div></li>
|
||||||
|
* <li><code>name</code> : String <div class="sub-desc">Name of the object. This name is used to display</div></li>
|
||||||
|
* <li><code>type</code> : String <div class="sub-desc">The specific object type</div></li>
|
||||||
|
* <li><code>partentId</code> : String (optional) <div class="sub-desc"></div></li>
|
||||||
|
* <li><code>localName</code> : String (optional) <div class="sub-desc">Name of the object. This name is used internally</div></li>
|
||||||
|
* <li><code>createdBy</code> : String (optional) <div class="sub-desc">User who created the object</div></li>
|
||||||
|
* <li><code>creationDate</code> : Date (optional) <div class="sub-desc">DateTime when the object was created</div></li>
|
||||||
|
* <li><code>lastModifiedBy</code> : String (optional) <div class="sub-desc">User who last modified the object</div></li>
|
||||||
|
* <li><code>lastModificationDate</code> : Date (optional) <div class="sub-desc">DateTime when the object was last modified</div></li>
|
||||||
|
* </ul></div>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Aloha.RepositoryFolder = Class.extend({
|
||||||
|
|
||||||
|
_constructor: function(properties) {
|
||||||
|
|
||||||
|
var p = properties;
|
||||||
|
|
||||||
|
this.type = 'folder';
|
||||||
|
|
||||||
|
// Basic error checking for MUST attributes
|
||||||
|
if (!p.id ||
|
||||||
|
!p.name ||
|
||||||
|
!p.repositoryId
|
||||||
|
) {
|
||||||
|
// Aloha.Log.error(this, "No valid Aloha Object. Missing MUST property");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GENTICS.Utils.applyProperties(this, properties);
|
||||||
|
|
||||||
|
this.baseType = 'folder';
|
||||||
|
|
||||||
|
}
|
||||||
|
// /**
|
||||||
|
// * Not implemented method to generate this JS API doc correctly.
|
||||||
|
// */
|
||||||
|
// ,empty = function() {};
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|