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 'mongoid', '~> 2.4.0'
|
||||
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', :git => 'git://github.com/locomotivecms/custom_fields.git', :branch => 'experimental'
|
||||
gem 'custom_fields', :path => '../gems/custom_fields' # DEV
|
||||
# gem 'custom_fields', :git => 'git://github.com/locomotivecms/custom_fields.git', :branch => 'experimental'
|
||||
gem 'kaminari'
|
||||
|
||||
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
|
||||
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)
|
||||
|
||||
window.foo = @model
|
||||
|
||||
@touched_url = false
|
||||
|
||||
@image_picker_view = new Locomotive.Views.ThemeAssets.ImagePickerView
|
||||
|
@ -10,5 +10,5 @@
|
||||
*= require codemirror/themes/default
|
||||
*= require locomotive/toggle.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 Public
|
||||
class RenderingController < ApplicationController
|
||||
class PagesController < ApplicationController
|
||||
|
||||
include Locomotive::Routing::SiteDispatcher
|
||||
|
||||
@ -8,7 +8,7 @@ module Locomotive
|
||||
|
||||
before_filter :require_site
|
||||
|
||||
before_filter :authenticate_admin!, :only => [:edit]
|
||||
before_filter :authenticate_locomotive_account!, :only => [:edit]
|
||||
|
||||
before_filter :validate_site_membership, :only => [:edit]
|
||||
|
||||
@ -17,8 +17,12 @@ module Locomotive
|
||||
end
|
||||
|
||||
def edit
|
||||
if params[:noiframe]
|
||||
@editing = true
|
||||
render_locomotive_page
|
||||
else
|
||||
render :layout => false
|
||||
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
|
||||
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
|
||||
h(self.source.raw_template)
|
||||
@ -12,7 +12,7 @@ module Locomotive
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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'
|
||||
|
||||
# magic urls
|
||||
match '/' => 'locomotive/public/rendering#show'
|
||||
match '*path/edit' => 'locomotive/public/rendering#edit'
|
||||
match '*path' => 'locomotive/public/rendering#show'
|
||||
# match '/editable' => 'locomotive/public/rendering#edit', :path => '/'
|
||||
match '/' => 'locomotive/public/pages#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
|
@ -4,45 +4,55 @@ module Liquid
|
||||
class InlineEditor < ::Liquid::Tag
|
||||
|
||||
def render(context)
|
||||
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[:current_locomotive_account] && context.registers[:inline_editor]
|
||||
%{
|
||||
<script type="text/javascript">
|
||||
window.parent.application_view.set_page(#{context.registers[:page].to_json});
|
||||
</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" />
|
||||
}
|
||||
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
|
||||
|
||||
|
@ -33,7 +33,7 @@ module Locomotive
|
||||
path.gsub!(/\.[a-zA-Z][a-zA-Z0-9]{2,}$/, '') # remove the page extension
|
||||
path.gsub!(/^\//, '') # remove the leading slash
|
||||
|
||||
path = 'index' if path.blank?
|
||||
path = 'index' if path.blank? || path == 'edit'
|
||||
|
||||
if path != 'index'
|
||||
dirname = File.dirname(path).gsub(/^\.$/, '') # also look for templatized page path
|
||||
@ -73,8 +73,6 @@ module Locomotive
|
||||
|
||||
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
|
||||
|
||||
if @page.templatized? # add instance from content type
|
||||
@ -97,6 +95,7 @@ module Locomotive
|
||||
flash.discard
|
||||
|
||||
response.headers['Content-Type'] = 'text/html; charset=utf-8'
|
||||
response.headers['Editable'] = 'true' unless self.editing_page?
|
||||
|
||||
if @page.with_cache?
|
||||
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
|
||||
config.assets.version = '1.0'
|
||||
|
||||
config.middleware.use 'Locomotive::InlineEditorMiddleware'
|
||||
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() {};
|
||||
|
||||
});
|
||||
});
|