sir-trevor-rails 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +58 -62
- data/README.md +12 -73
- data/app/views/sir-trevor/blocks/_list_block.html.erb +3 -0
- data/app/views/sir-trevor/blocks/_quote_block.html.erb +3 -3
- data/app/views/sir-trevor/blocks/_text_block.html.erb +2 -2
- data/app/views/sir-trevor/blocks/_tweet_block.html.erb +4 -4
- data/app/views/sir-trevor/blocks/_video_block.html.erb +5 -5
- data/config/initializers/validators.rb +4 -2
- data/lib/generators/sir_trevor/block/templates/_block.html.erb +2 -2
- data/lib/sir-trevor-rails.rb +7 -3
- data/lib/sir-trevor/helpers/form_builder.rb +2 -2
- data/lib/sir-trevor/helpers/form_helper.rb +12 -6
- data/lib/sir-trevor/helpers/view_helper.rb +41 -40
- data/lib/sir-trevor/version.rb +1 -1
- data/sir-trevor-rails.gemspec +6 -2
- metadata +35 -34
- data/Rakefile +0 -10
- data/app/assets/images/sir-trevor/icons/block_editor_blockquote.png +0 -0
- data/app/assets/images/sir-trevor/icons/block_editor_embed.png +0 -0
- data/app/assets/images/sir-trevor/icons/block_editor_image.png +0 -0
- data/app/assets/images/sir-trevor/icons/block_editor_list.png +0 -0
- data/app/assets/images/sir-trevor/icons/block_editor_text.png +0 -0
- data/app/assets/images/sir-trevor/icons/block_editor_tweet.png +0 -0
- data/app/assets/images/sir-trevor/icons/block_editor_video.png +0 -0
- data/app/assets/images/sir-trevor/icons/close.gif +0 -0
- data/app/assets/images/sir-trevor/icons/handle.gif +0 -0
- data/app/assets/images/sir-trevor/icons/new_image.png +0 -0
- data/app/assets/images/sir-trevor/icons/new_tweet.png +0 -0
- data/app/assets/images/sir-trevor/icons/new_video.png +0 -0
- data/app/assets/images/sir-trevor/icons/quote.png +0 -0
- data/app/assets/images/sir-trevor/placeholders/placeholder.jpg +0 -0
- data/app/assets/images/sir-trevor/placeholders/post_placeholder.jpg +0 -0
- data/app/assets/images/sir-trevor/placeholders/thumbnail_placeholder.jpg +0 -0
- data/app/assets/javascript/sir-trevor.js +0 -2
- data/app/assets/javascript/sir-trevor/libs/underscore.js +0 -32
- data/app/assets/javascript/sir-trevor/sir-trevor.js +0 -2349
- data/app/assets/stylesheets/sir-trevor.css +0 -4
- data/app/assets/stylesheets/sir-trevor/icons.css.erb +0 -47
- data/app/assets/stylesheets/sir-trevor/sir-trevor.css +0 -439
- data/app/views/sir-trevor/blocks/_gallery_block.html.erb +0 -6
- data/app/views/sir-trevor/blocks/_ul_block.html.erb +0 -3
- data/lib/generators/sir_trevor/block/block_generator.rb +0 -37
- data/lib/generators/sir_trevor/block/templates/_block.css.erb +0 -7
- data/lib/generators/sir_trevor/block/templates/_block.js +0 -122
- data/lib/generators/sir_trevor/block/templates/_block.png +0 -0
data/lib/sir-trevor/version.rb
CHANGED
data/sir-trevor-rails.gemspec
CHANGED
@@ -11,19 +11,23 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.authors = ["Andrew Sprinz", "Chris Bell"]
|
12
12
|
s.email = 'chris@madebymany.com'
|
13
13
|
s.homepage = 'https://github.com/madebymany/sir-trevor-rails'
|
14
|
+
|
15
|
+
s.license = 'MIT'
|
14
16
|
|
15
17
|
s.rubyforge_project = "sir-trevor-rails"
|
16
18
|
s.required_rubygems_version = "> 1.3.6"
|
17
19
|
|
20
|
+
s.add_dependency "activemodel"
|
18
21
|
s.add_dependency "activesupport" , ">= 3.0.7"
|
19
22
|
s.add_dependency "rails" , ">= 3.0.7"
|
20
23
|
s.add_dependency "jquery-rails"
|
24
|
+
s.add_dependency "multi_json"
|
21
25
|
|
22
|
-
s.add_dependency "redcarpet", "~>
|
26
|
+
s.add_dependency "redcarpet", "~> 3.0.0"
|
23
27
|
s.add_dependency 'twitter-text', '~> 1.4'
|
24
28
|
|
25
29
|
s.files = `git ls-files`.split("\n")
|
26
30
|
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
27
31
|
s.require_path = 'lib'
|
28
32
|
|
29
|
-
end
|
33
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sir-trevor-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Sprinz
|
@@ -11,6 +11,20 @@ bindir: bin
|
|
11
11
|
cert_chain: []
|
12
12
|
date: 2012-08-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activemodel
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - '>='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
14
28
|
- !ruby/object:Gem::Dependency
|
15
29
|
name: activesupport
|
16
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,20 +67,34 @@ dependencies:
|
|
53
67
|
- - '>='
|
54
68
|
- !ruby/object:Gem::Version
|
55
69
|
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: multi_json
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
56
84
|
- !ruby/object:Gem::Dependency
|
57
85
|
name: redcarpet
|
58
86
|
requirement: !ruby/object:Gem::Requirement
|
59
87
|
requirements:
|
60
88
|
- - ~>
|
61
89
|
- !ruby/object:Gem::Version
|
62
|
-
version:
|
90
|
+
version: 3.0.0
|
63
91
|
type: :runtime
|
64
92
|
prerelease: false
|
65
93
|
version_requirements: !ruby/object:Gem::Requirement
|
66
94
|
requirements:
|
67
95
|
- - ~>
|
68
96
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
97
|
+
version: 3.0.0
|
70
98
|
- !ruby/object:Gem::Dependency
|
71
99
|
name: twitter-text
|
72
100
|
requirement: !ruby/object:Gem::Requirement
|
@@ -92,42 +120,14 @@ files:
|
|
92
120
|
- Gemfile.lock
|
93
121
|
- MIT-LICENCE
|
94
122
|
- README.md
|
95
|
-
- Rakefile
|
96
|
-
- app/assets/images/sir-trevor/icons/block_editor_blockquote.png
|
97
|
-
- app/assets/images/sir-trevor/icons/block_editor_embed.png
|
98
|
-
- app/assets/images/sir-trevor/icons/block_editor_image.png
|
99
|
-
- app/assets/images/sir-trevor/icons/block_editor_list.png
|
100
|
-
- app/assets/images/sir-trevor/icons/block_editor_text.png
|
101
|
-
- app/assets/images/sir-trevor/icons/block_editor_tweet.png
|
102
|
-
- app/assets/images/sir-trevor/icons/block_editor_video.png
|
103
|
-
- app/assets/images/sir-trevor/icons/close.gif
|
104
|
-
- app/assets/images/sir-trevor/icons/handle.gif
|
105
|
-
- app/assets/images/sir-trevor/icons/new_image.png
|
106
|
-
- app/assets/images/sir-trevor/icons/new_tweet.png
|
107
|
-
- app/assets/images/sir-trevor/icons/new_video.png
|
108
|
-
- app/assets/images/sir-trevor/icons/quote.png
|
109
|
-
- app/assets/images/sir-trevor/placeholders/placeholder.jpg
|
110
|
-
- app/assets/images/sir-trevor/placeholders/post_placeholder.jpg
|
111
|
-
- app/assets/images/sir-trevor/placeholders/thumbnail_placeholder.jpg
|
112
|
-
- app/assets/javascript/sir-trevor.js
|
113
|
-
- app/assets/javascript/sir-trevor/libs/underscore.js
|
114
|
-
- app/assets/javascript/sir-trevor/sir-trevor.js
|
115
|
-
- app/assets/stylesheets/sir-trevor.css
|
116
|
-
- app/assets/stylesheets/sir-trevor/icons.css.erb
|
117
|
-
- app/assets/stylesheets/sir-trevor/sir-trevor.css
|
118
|
-
- app/views/sir-trevor/blocks/_gallery_block.html.erb
|
119
123
|
- app/views/sir-trevor/blocks/_image_block.html.erb
|
124
|
+
- app/views/sir-trevor/blocks/_list_block.html.erb
|
120
125
|
- app/views/sir-trevor/blocks/_quote_block.html.erb
|
121
126
|
- app/views/sir-trevor/blocks/_text_block.html.erb
|
122
127
|
- app/views/sir-trevor/blocks/_tweet_block.html.erb
|
123
|
-
- app/views/sir-trevor/blocks/_ul_block.html.erb
|
124
128
|
- app/views/sir-trevor/blocks/_video_block.html.erb
|
125
129
|
- config/initializers/validators.rb
|
126
|
-
- lib/generators/sir_trevor/block/block_generator.rb
|
127
|
-
- lib/generators/sir_trevor/block/templates/_block.css.erb
|
128
130
|
- lib/generators/sir_trevor/block/templates/_block.html.erb
|
129
|
-
- lib/generators/sir_trevor/block/templates/_block.js
|
130
|
-
- lib/generators/sir_trevor/block/templates/_block.png
|
131
131
|
- lib/generators/sir_trevor/views/views_generator.rb
|
132
132
|
- lib/sir-trevor-rails.rb
|
133
133
|
- lib/sir-trevor/engine.rb
|
@@ -137,7 +137,8 @@ files:
|
|
137
137
|
- lib/sir-trevor/version.rb
|
138
138
|
- sir-trevor-rails.gemspec
|
139
139
|
homepage: https://github.com/madebymany/sir-trevor-rails
|
140
|
-
licenses:
|
140
|
+
licenses:
|
141
|
+
- MIT
|
141
142
|
metadata: {}
|
142
143
|
post_install_message:
|
143
144
|
rdoc_options: []
|
@@ -158,5 +159,5 @@ rubyforge_project: sir-trevor-rails
|
|
158
159
|
rubygems_version: 2.0.14
|
159
160
|
signing_key:
|
160
161
|
specification_version: 4
|
161
|
-
summary: sir-trevor-rails-0.
|
162
|
+
summary: sir-trevor-rails-0.4.0
|
162
163
|
test_files: []
|
data/Rakefile
DELETED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -1,32 +0,0 @@
|
|
1
|
-
// Underscore.js 1.3.3
|
2
|
-
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
3
|
-
// Underscore is freely distributable under the MIT license.
|
4
|
-
// Portions of Underscore are inspired or borrowed from Prototype,
|
5
|
-
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
6
|
-
// For all details and documentation:
|
7
|
-
// http://documentcloud.github.com/underscore
|
8
|
-
(function(){function r(a,c,d){if(a===c)return 0!==a||1/a==1/c;if(null==a||null==c)return a===c;a._chain&&(a=a._wrapped);c._chain&&(c=c._wrapped);if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return!1;switch(e){case "[object String]":return a==""+c;case "[object Number]":return a!=+a?c!=+c:0==a?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
|
9
|
-
c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if("object"!=typeof a||"object"!=typeof c)return!1;for(var f=d.length;f--;)if(d[f]==a)return!0;d.push(a);var f=0,g=!0;if("[object Array]"==e){if(f=a.length,g=f==c.length)for(;f--&&(g=f in a==f in c&&r(a[f],c[f],d)););}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return!1;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,h)&&!f--)break;
|
10
|
-
g=!f}}d.pop();return g}var s=this,I=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,J=k.unshift,l=p.toString,K=p.hasOwnProperty,y=k.forEach,z=k.map,A=k.reduce,B=k.reduceRight,C=k.filter,D=k.every,E=k.some,q=k.indexOf,F=k.lastIndexOf,p=Array.isArray,L=Object.keys,t=Function.prototype.bind,b=function(a){return new m(a)};"undefined"!==typeof exports?("undefined"!==typeof module&&module.exports&&(exports=module.exports=b),exports._=b):s._=b;b.VERSION="1.3.3";var j=b.each=b.forEach=function(a,
|
11
|
-
c,d){if(a!=null)if(y&&a.forEach===y)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===o)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===o)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(z&&a.map===z)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(A&&
|
12
|
-
a.reduce===A){e&&(c=b.bind(c,e));return f?a.reduce(c,d):a.reduce(c)}j(a,function(a,b,i){if(f)d=c.call(e,d,a,b,i);else{d=a;f=true}});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(B&&a.reduceRight===B){e&&(c=b.bind(c,e));return f?a.reduceRight(c,d):a.reduceRight(c)}var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=function(a,
|
13
|
-
c,b){var e;G(a,function(a,g,h){if(c.call(b,a,g,h)){e=a;return true}});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(C&&a.filter===C)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(D&&a.every===D)return a.every(c,b);j(a,function(a,g,h){if(!(e=e&&c.call(b,
|
14
|
-
a,g,h)))return o});return!!e};var G=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(E&&a.some===E)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;if(q&&a.indexOf===q)return a.indexOf(c)!=-1;return b=G(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
|
15
|
-
function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&
|
16
|
-
(e={value:a,computed:b})});return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){d=Math.floor(Math.random()*(f+1));b[f]=b[d];b[d]=a});return b};b.sortBy=function(a,c,d){var e=b.isFunction(c)?c:function(a){return a[c]};return b.pluck(b.map(a,function(a,b,c){return{value:a,criteria:e.call(d,a,b,c)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c===void 0?1:d===void 0?-1:c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};
|
17
|
-
j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:b.isArray(a)||b.isArguments(a)?i.call(a):a.toArray&&b.isFunction(a.toArray)?a.toArray():b.values(a)};b.size=function(a){return b.isArray(a)?a.length:b.keys(a).length};b.first=b.head=b.take=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,
|
18
|
-
0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,
|
19
|
-
e=[];a.length<3&&(c=true);b.reduce(d,function(d,g,h){if(c?b.last(d)!==g||!d.length:!b.include(d,g)){d.push(g);e.push(a[h])}return d},[]);return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1),true);return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=
|
20
|
-
i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,d){if(a==null)return-1;var e;if(d){d=b.sortedIndex(a,c);return a[d]===c?d:-1}if(q&&a.indexOf===q)return a.indexOf(c);d=0;for(e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(F&&a.lastIndexOf===F)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){if(arguments.length<=
|
21
|
-
1){b=a||0;a=0}for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;){g[f++]=a;a=a+d}return g};var H=function(){};b.bind=function(a,c){var d,e;if(a.bind===t&&t)return t.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));H.prototype=a.prototype;var b=new H,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=
|
22
|
-
i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(null,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i,j=b.debounce(function(){h=
|
23
|
-
g=false},c);return function(){d=this;e=arguments;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);j()},c));g?h=true:i=a.apply(d,e);j();g=true;return i}};b.debounce=function(a,b,d){var e;return function(){var f=this,g=arguments;d&&!e&&a.apply(f,g);clearTimeout(e);e=setTimeout(function(){e=null;d||a.apply(f,g)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));
|
24
|
-
return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=L||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&
|
25
|
-
c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.pick=function(a){var c={};j(b.flatten(i.call(arguments,1)),function(b){b in a&&(c[b]=a[b])});return c};b.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty=
|
26
|
-
function(a){if(a==null)return true;if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};b.isArguments=function(a){return l.call(a)=="[object Arguments]"};b.isArguments(arguments)||(b.isArguments=function(a){return!(!a||!b.has(a,"callee"))});b.isFunction=function(a){return l.call(a)=="[object Function]"};
|
27
|
-
b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isFinite=function(a){return b.isNumber(a)&&isFinite(a)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,
|
28
|
-
b){return K.call(a,b)};b.noConflict=function(){s._=I;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.result=function(a,c){if(a==null)return null;var d=a[c];return b.isFunction(d)?d.call(a):d};b.mixin=function(a){j(b.functions(a),function(c){M(c,b[c]=a[c])})};var N=0;b.uniqueId=
|
29
|
-
function(a){var b=N++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var u=/.^/,n={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"},v;for(v in n)n[n[v]]=v;var O=/\\|'|\r|\n|\t|\u2028|\u2029/g,P=/\\(\\|'|r|n|t|u2028|u2029)/g,w=function(a){return a.replace(P,function(a,b){return n[b]})};b.template=function(a,c,d){d=b.defaults(d||{},b.templateSettings);a="__p+='"+a.replace(O,function(a){return"\\"+n[a]}).replace(d.escape||
|
30
|
-
u,function(a,b){return"'+\n_.escape("+w(b)+")+\n'"}).replace(d.interpolate||u,function(a,b){return"'+\n("+w(b)+")+\n'"}).replace(d.evaluate||u,function(a,b){return"';\n"+w(b)+"\n;__p+='"})+"';\n";d.variable||(a="with(obj||{}){\n"+a+"}\n");var a="var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n"+a+"return __p;\n",e=new Function(d.variable||"obj","_",a);if(c)return e(c,b);c=function(a){return e.call(this,a,b)};c.source="function("+(d.variable||"obj")+"){\n"+a+"}";return c};
|
31
|
-
b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var x=function(a,c){return c?b(a).chain():a},M=function(a,c){m.prototype[a]=function(){var a=i.call(arguments);J.call(a,this._wrapped);return x(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return x(d,
|
32
|
-
this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return x(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);
|
@@ -1,2349 +0,0 @@
|
|
1
|
-
(function ($, _){
|
2
|
-
|
3
|
-
var root = this,
|
4
|
-
SirTrevor;
|
5
|
-
|
6
|
-
SirTrevor = root.SirTrevor = {};
|
7
|
-
SirTrevor.DEBUG = false;
|
8
|
-
SirTrevor.SKIP_VALIDATION = false;
|
9
|
-
|
10
|
-
/*
|
11
|
-
Define default attributes that can be extended through an object passed to the
|
12
|
-
initialize function of SirTrevor
|
13
|
-
*/
|
14
|
-
|
15
|
-
SirTrevor.DEFAULTS = {
|
16
|
-
baseCSSClass: "sir-trevor",
|
17
|
-
errorClass: "error",
|
18
|
-
defaultType: "Text",
|
19
|
-
spinner: {
|
20
|
-
className: 'spinner',
|
21
|
-
lines: 9,
|
22
|
-
length: 8,
|
23
|
-
width: 3,
|
24
|
-
radius: 6,
|
25
|
-
color: '#000',
|
26
|
-
speed: 1.4,
|
27
|
-
trail: 57,
|
28
|
-
shadow: false,
|
29
|
-
left: '50%',
|
30
|
-
top: '50%'
|
31
|
-
},
|
32
|
-
marker: {
|
33
|
-
baseCSSClass: "marker",
|
34
|
-
buttonClass: "button",
|
35
|
-
addText: "Click to add:",
|
36
|
-
dropText: "Drop to place content"
|
37
|
-
},
|
38
|
-
formatBar: {
|
39
|
-
baseCSSClass: "formatting-control"
|
40
|
-
},
|
41
|
-
blockLimit: 0,
|
42
|
-
blockTypeLimits: {},
|
43
|
-
required: [],
|
44
|
-
uploadUrl: '/attachments',
|
45
|
-
baseImageUrl: '/sir-trevor-uploads/'
|
46
|
-
};
|
47
|
-
|
48
|
-
SirTrevor.Blocks = {};
|
49
|
-
SirTrevor.Formatters = {};
|
50
|
-
SirTrevor.instances = [];
|
51
|
-
|
52
|
-
var formBound = false; // Flag to tell us once we've bound our submit event
|
53
|
-
|
54
|
-
/* Generic function binding utility, used by lots of our classes */
|
55
|
-
var FunctionBind = {
|
56
|
-
bound: [],
|
57
|
-
_bindFunctions: function(){
|
58
|
-
var args = [];
|
59
|
-
args.push(this);
|
60
|
-
args.join(this.bound);
|
61
|
-
_.bindAll.apply(this, args);
|
62
|
-
}
|
63
|
-
};
|
64
|
-
|
65
|
-
/*
|
66
|
-
Given an array or object, flatten it and return only the key => true
|
67
|
-
*/
|
68
|
-
|
69
|
-
function flattern(obj){
|
70
|
-
var x = {};
|
71
|
-
_.each(obj, function(a,b) {
|
72
|
-
x[(_.isArray(obj)) ? a : b] = true;
|
73
|
-
});
|
74
|
-
return x;
|
75
|
-
}
|
76
|
-
/* Halt event execution */
|
77
|
-
function halt(ev){
|
78
|
-
ev.preventDefault();
|
79
|
-
ev.stopPropagation();
|
80
|
-
}
|
81
|
-
|
82
|
-
function controlKeyDown(ev){
|
83
|
-
return (ev.which == 17 || ev.which == 224);
|
84
|
-
}
|
85
|
-
|
86
|
-
function isElementNear($element, distance, event) {
|
87
|
-
var left = $element.offset().left - distance,
|
88
|
-
top = $element.offset().top - distance,
|
89
|
-
right = left + $element.width() + ( 2 * distance ),
|
90
|
-
bottom = top + $element.height() + ( 2 * distance ),
|
91
|
-
x = event.pageX,
|
92
|
-
y = event.pageY;
|
93
|
-
|
94
|
-
return ( x > left && x < right && y > top && y < bottom );
|
95
|
-
}
|
96
|
-
|
97
|
-
/*
|
98
|
-
Drop Area Plugin from @maccman
|
99
|
-
http://blog.alexmaccaw.com/svbtle-image-uploading
|
100
|
-
--
|
101
|
-
Tweaked so we use the parent class of dropzone
|
102
|
-
*/
|
103
|
-
|
104
|
-
(function($){
|
105
|
-
function dragEnter(e) {
|
106
|
-
halt(e);
|
107
|
-
}
|
108
|
-
|
109
|
-
function dragOver(e) {
|
110
|
-
e.originalEvent.dataTransfer.dropEffect = "copy";
|
111
|
-
halt(e);
|
112
|
-
}
|
113
|
-
|
114
|
-
function dragLeave(e) {
|
115
|
-
halt(e);
|
116
|
-
}
|
117
|
-
|
118
|
-
$.fn.dropArea = function(){
|
119
|
-
this.bind("dragenter", dragEnter).
|
120
|
-
bind("dragover", dragOver).
|
121
|
-
bind("dragleave", dragLeave);
|
122
|
-
return this;
|
123
|
-
};
|
124
|
-
|
125
|
-
$.fn.noDropArea = function(){
|
126
|
-
this.unbind("dragenter").
|
127
|
-
unbind("dragover").
|
128
|
-
unbind("dragleave");
|
129
|
-
return this;
|
130
|
-
};
|
131
|
-
|
132
|
-
})(jQuery);
|
133
|
-
/*
|
134
|
-
Backbone Inheritence
|
135
|
-
--
|
136
|
-
From: https://github.com/documentcloud/backbone/blob/master/backbone.js
|
137
|
-
Backbone.js 0.9.2
|
138
|
-
(c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
139
|
-
*/
|
140
|
-
|
141
|
-
// The self-propagating extend function that Backbone classes use.
|
142
|
-
var extend = function(protoProps, classProps) {
|
143
|
-
return inherits(this, protoProps, classProps);
|
144
|
-
};
|
145
|
-
|
146
|
-
// Shared empty constructor function to aid in prototype-chain creation.
|
147
|
-
var ctor = function(){};
|
148
|
-
|
149
|
-
// Helper function to correctly set up the prototype chain, for subclasses.
|
150
|
-
// Similar to `goog.inherits`, but uses a hash of prototype properties and
|
151
|
-
// class properties to be extended.
|
152
|
-
var inherits = function(parent, protoProps, staticProps) {
|
153
|
-
var child;
|
154
|
-
|
155
|
-
// The constructor function for the new subclass is either defined by you
|
156
|
-
// (the "constructor" property in your `extend` definition), or defaulted
|
157
|
-
// by us to simply call the parent's constructor.
|
158
|
-
if (protoProps && protoProps.hasOwnProperty('constructor')) {
|
159
|
-
child = protoProps.constructor;
|
160
|
-
} else {
|
161
|
-
child = function(){ parent.apply(this, arguments); };
|
162
|
-
}
|
163
|
-
|
164
|
-
// Inherit class (static) properties from parent.
|
165
|
-
_.extend(child, parent);
|
166
|
-
|
167
|
-
// Set the prototype chain to inherit from `parent`, without calling
|
168
|
-
// `parent`'s constructor function.
|
169
|
-
ctor.prototype = parent.prototype;
|
170
|
-
child.prototype = new ctor();
|
171
|
-
|
172
|
-
// Add prototype properties (instance properties) to the subclass,
|
173
|
-
// if supplied.
|
174
|
-
if (protoProps) _.extend(child.prototype, protoProps);
|
175
|
-
|
176
|
-
// Add static properties to the constructor function, if supplied.
|
177
|
-
if (staticProps) _.extend(child, staticProps);
|
178
|
-
|
179
|
-
// Correctly set child's `prototype.constructor`.
|
180
|
-
child.prototype.constructor = child;
|
181
|
-
|
182
|
-
// Set a convenience property in case the parent's prototype is needed later.
|
183
|
-
child.__super__ = parent.prototype;
|
184
|
-
|
185
|
-
return child;
|
186
|
-
};
|
187
|
-
/*
|
188
|
-
* Ultra simple logging
|
189
|
-
*/
|
190
|
-
|
191
|
-
SirTrevor.log = function(message) {
|
192
|
-
if (!_.isUndefined(console) && SirTrevor.DEBUG) {
|
193
|
-
console.log(message);
|
194
|
-
}
|
195
|
-
};
|
196
|
-
/* String to slug */
|
197
|
-
|
198
|
-
function toSlug(string)
|
199
|
-
{
|
200
|
-
return string
|
201
|
-
.toLowerCase()
|
202
|
-
.replace(/[^\w ]+/g,'')
|
203
|
-
.replace(/ +/g,'-');
|
204
|
-
}
|
205
|
-
/* jQuery Tiny Pub/Sub - v0.7 - 10/27/2011
|
206
|
-
* http://benalman.com/
|
207
|
-
* Copyright (c) 2011 "Cowboy" Ben Alman; Licensed MIT, GPL */
|
208
|
-
var o = $(SirTrevor);
|
209
|
-
SirTrevor.subscribe = SirTrevor.on = function() {
|
210
|
-
o.on.apply(o, arguments);
|
211
|
-
};
|
212
|
-
SirTrevor.unsubscribe = SirTrevor.off = function() {
|
213
|
-
o.off.apply(o, arguments);
|
214
|
-
};
|
215
|
-
SirTrevor.publish = SirTrevor.trigger = function() {
|
216
|
-
o.trigger.apply(o, arguments);
|
217
|
-
};
|
218
|
-
SirTrevor.subscribeAll = function(subscriptions) {
|
219
|
-
_.each(subscriptions, function(subscription) {
|
220
|
-
o.on.apply(o, arguments);
|
221
|
-
});
|
222
|
-
};
|
223
|
-
//fgnass.github.com/spin.js#v1.2.5
|
224
|
-
(function(a,b,c){function g(a,c){var d=b.createElement(a||"div"),e;for(e in c)d[e]=c[e];return d}function h(a){for(var b=1,c=arguments.length;b<c;b++)a.appendChild(arguments[b]);return a}function j(a,b,c,d){var g=["opacity",b,~~(a*100),c,d].join("-"),h=.01+c/d*100,j=Math.max(1-(1-a)/b*(100-h),a),k=f.substring(0,f.indexOf("Animation")).toLowerCase(),l=k&&"-"+k+"-"||"";return e[g]||(i.insertRule("@"+l+"keyframes "+g+"{"+"0%{opacity:"+j+"}"+h+"%{opacity:"+a+"}"+(h+.01)+"%{opacity:1}"+(h+b)%100+"%{opacity:"+a+"}"+"100%{opacity:"+j+"}"+"}",0),e[g]=1),g}function k(a,b){var e=a.style,f,g;if(e[b]!==c)return b;b=b.charAt(0).toUpperCase()+b.slice(1);for(g=0;g<d.length;g++){f=d[g]+b;if(e[f]!==c)return f}}function l(a,b){for(var c in b)a.style[k(a,c)||c]=b[c];return a}function m(a){for(var b=1;b<arguments.length;b++){var d=arguments[b];for(var e in d)a[e]===c&&(a[e]=d[e])}return a}function n(a){var b={x:a.offsetLeft,y:a.offsetTop};while(a=a.offsetParent)b.x+=a.offsetLeft,b.y+=a.offsetTop;return b}var d=["webkit","Moz","ms","O"],e={},f,i=function(){var a=g("style");return h(b.getElementsByTagName("head")[0],a),a.sheet||a.styleSheet}(),o={lines:12,length:7,width:5,radius:10,rotate:0,color:"#000",speed:1,trail:100,opacity:.25,fps:20,zIndex:2e9,className:"spinner",top:"auto",left:"auto"},p=function q(a){if(!this.spin)return new q(a);this.opts=m(a||{},q.defaults,o)};p.defaults={},m(p.prototype,{spin:function(a){this.stop();var b=this,c=b.opts,d=b.el=l(g(0,{className:c.className}),{position:"relative",zIndex:c.zIndex}),e=c.radius+c.length+c.width,h,i;a&&(a.insertBefore(d,a.firstChild||null),i=n(a),h=n(d),l(d,{left:(c.left=="auto"?i.x-h.x+(a.offsetWidth>>1):c.left+e)+"px",top:(c.top=="auto"?i.y-h.y+(a.offsetHeight>>1):c.top+e)+"px"})),d.setAttribute("aria-role","progressbar"),b.lines(d,b.opts);if(!f){var j=0,k=c.fps,m=k/c.speed,o=(1-c.opacity)/(m*c.trail/100),p=m/c.lines;!function q(){j++;for(var a=c.lines;a;a--){var e=Math.max(1-(j+a*p)%m*o,c.opacity);b.opacity(d,c.lines-a,e,c)}b.timeout=b.el&&setTimeout(q,~~(1e3/k))}()}return b},stop:function(){var a=this.el;return a&&(clearTimeout(this.timeout),a.parentNode&&a.parentNode.removeChild(a),this.el=c),this},lines:function(a,b){function e(a,d){return l(g(),{position:"absolute",width:b.length+b.width+"px",height:b.width+"px",background:a,boxShadow:d,transformOrigin:"left",transform:"rotate("+~~(360/b.lines*c+b.rotate)+"deg) translate("+b.radius+"px"+",0)",borderRadius:(b.width>>1)+"px"})}var c=0,d;for(;c<b.lines;c++)d=l(g(),{position:"absolute",top:1+~(b.width/2)+"px",transform:b.hwaccel?"translate3d(0,0,0)":"",opacity:b.opacity,animation:f&&j(b.opacity,b.trail,c,b.lines)+" "+1/b.speed+"s linear infinite"}),b.shadow&&h(d,l(e("#000","0 0 4px #000"),{top:"2px"})),h(a,h(d,e(b.color,"0 0 1px rgba(0,0,0,.1)")));return a},opacity:function(a,b,c){b<a.childNodes.length&&(a.childNodes[b].style.opacity=c)}}),!function(){function a(a,b){return g("<"+a+' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">',b)}var b=l(g("group"),{behavior:"url(#default#VML)"});!k(b,"transform")&&b.adj?(i.addRule(".spin-vml","behavior:url(#default#VML)"),p.prototype.lines=function(b,c){function f(){return l(a("group",{coordsize:e+" "+e,coordorigin:-d+" "+ -d}),{width:e,height:e})}function k(b,e,g){h(i,h(l(f(),{rotation:360/c.lines*b+"deg",left:~~e}),h(l(a("roundrect",{arcsize:1}),{width:d,height:c.width,left:c.radius,top:-c.width>>1,filter:g}),a("fill",{color:c.color,opacity:c.opacity}),a("stroke",{opacity:0}))))}var d=c.length+c.width,e=2*d,g=-(c.width+c.length)*2+"px",i=l(f(),{position:"absolute",top:g,left:g}),j;if(c.shadow)for(j=1;j<=c.lines;j++)k(j,-2,"progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)");for(j=1;j<=c.lines;j++)k(j);return h(b,i)},p.prototype.opacity=function(a,b,c,d){var e=a.firstChild;d=d.shadow&&d.lines||0,e&&b+d<e.childNodes.length&&(e=e.childNodes[b+d],e=e&&e.firstChild,e=e&&e.firstChild,e&&(e.opacity=c))}):f=k(b,"animation")}(),a.Spinner=p})(window,document);
|
225
|
-
/* Soft character limits on inputs and textareas */
|
226
|
-
|
227
|
-
(function($){
|
228
|
-
|
229
|
-
$.fn.limit_chars = function() {
|
230
|
-
|
231
|
-
if (this.length===0) return;
|
232
|
-
|
233
|
-
// Remove browser maxlength, add soft limit
|
234
|
-
if(this.attr('maxlength')) {
|
235
|
-
this.attr('data-maxlength',this.attr('maxlength'));
|
236
|
-
this.removeAttr('maxlength');
|
237
|
-
}
|
238
|
-
|
239
|
-
if(this.parents('.extended_input').length === 0) {
|
240
|
-
|
241
|
-
count = (this.chars()<this.attr('data-maxlength')) ? this.chars() : '<em>'+this.chars()+'</em>';
|
242
|
-
|
243
|
-
// Build UI
|
244
|
-
this.wrap($('<div>',{
|
245
|
-
"class": "extended_input"
|
246
|
-
})).after($('<span>', {
|
247
|
-
"class": "count",
|
248
|
-
html: count+' of '+this.attr('data-maxlength')
|
249
|
-
}));
|
250
|
-
|
251
|
-
// Attach event
|
252
|
-
this.bind('keydown keyup paste',function(ev){
|
253
|
-
count = ($(this).chars()<$(this).attr('data-maxlength')) ? $(this).chars() : '<em>'+$(this).chars()+'</em>';
|
254
|
-
$(this).parent().find('.count').html(count+' of '+$(this).attr('data-maxlength'));
|
255
|
-
});
|
256
|
-
|
257
|
-
}
|
258
|
-
|
259
|
-
};
|
260
|
-
|
261
|
-
$.fn.chars = function() {
|
262
|
-
count = (this.attr('contenteditable')!==undefined) ? this.text().length : this.val().length;
|
263
|
-
return count;
|
264
|
-
};
|
265
|
-
|
266
|
-
$.fn.too_long = function() {
|
267
|
-
return this.chars() > this.attr('data-maxlength');
|
268
|
-
};
|
269
|
-
|
270
|
-
})(jQuery);
|
271
|
-
/*
|
272
|
-
* Sir Trevor Block Store
|
273
|
-
* By default we store the data on the instance
|
274
|
-
* We can easily extend this and store it on some server or something
|
275
|
-
*/
|
276
|
-
|
277
|
-
SirTrevor.blockStore = function(method, block, options) {
|
278
|
-
|
279
|
-
var resp;
|
280
|
-
|
281
|
-
options = options || {};
|
282
|
-
|
283
|
-
switch(method) {
|
284
|
-
|
285
|
-
case "create":
|
286
|
-
var data = options.data || {};
|
287
|
-
block.dataStore = { type: block.type.toLowerCase(), data: data };
|
288
|
-
break;
|
289
|
-
|
290
|
-
case "save":
|
291
|
-
if (options.data) {
|
292
|
-
block.dataStore.data = options.data;
|
293
|
-
resp = block.dataStore;
|
294
|
-
}
|
295
|
-
break;
|
296
|
-
|
297
|
-
case "read":
|
298
|
-
resp = block.dataStore;
|
299
|
-
break;
|
300
|
-
|
301
|
-
}
|
302
|
-
|
303
|
-
if(resp) {
|
304
|
-
return resp;
|
305
|
-
}
|
306
|
-
|
307
|
-
};
|
308
|
-
/*
|
309
|
-
* Sir Trevor Editor Store
|
310
|
-
* By default we store the complete data on the instances $el
|
311
|
-
* We can easily extend this and store it on some server or something
|
312
|
-
*/
|
313
|
-
|
314
|
-
SirTrevor.editorStore = function(method, editor, options) {
|
315
|
-
|
316
|
-
var resp;
|
317
|
-
|
318
|
-
options = options || {};
|
319
|
-
|
320
|
-
switch(method) {
|
321
|
-
|
322
|
-
case "create":
|
323
|
-
// Grab our JSON from the textarea and clean any whitespace incase there is a line wrap between the opening and closing textarea tags
|
324
|
-
var content = _.trim(editor.$el.val());
|
325
|
-
editor.dataStore = { data: [] };
|
326
|
-
|
327
|
-
if (content.length > 0) {
|
328
|
-
try {
|
329
|
-
// Ensure the JSON string has a data element that's an array
|
330
|
-
var str = JSON.parse(content);
|
331
|
-
if (!_.isUndefined(str.data)) {
|
332
|
-
// Set it
|
333
|
-
editor.dataStore = str;
|
334
|
-
}
|
335
|
-
} catch(e) {
|
336
|
-
console.log('Sorry there has been a problem with parsing the JSON');
|
337
|
-
console.log(e);
|
338
|
-
}
|
339
|
-
}
|
340
|
-
break;
|
341
|
-
|
342
|
-
case "reset":
|
343
|
-
editor.dataStore = { data: [] };
|
344
|
-
break;
|
345
|
-
|
346
|
-
case "add":
|
347
|
-
if (options.data) {
|
348
|
-
editor.dataStore.data.push(options.data);
|
349
|
-
resp = editor.dataStore;
|
350
|
-
}
|
351
|
-
break;
|
352
|
-
|
353
|
-
case "save":
|
354
|
-
// Store to our element
|
355
|
-
editor.$el.val((editor.dataStore.data.length > 0) ? JSON.stringify(editor.dataStore) : '');
|
356
|
-
break;
|
357
|
-
|
358
|
-
case "read":
|
359
|
-
resp = editor.dataStore;
|
360
|
-
break;
|
361
|
-
|
362
|
-
}
|
363
|
-
|
364
|
-
if(resp) {
|
365
|
-
return resp;
|
366
|
-
}
|
367
|
-
|
368
|
-
};
|
369
|
-
/*
|
370
|
-
SirTrevor.Submittable
|
371
|
-
--
|
372
|
-
We need a global way of setting if the editor can and can't be submitted,
|
373
|
-
and a way to disable the submit button and add messages (when appropriate)
|
374
|
-
We also need this to be highly extensible so it can be overridden.
|
375
|
-
This will be triggered *by anything* so it needs to subscribe to events.
|
376
|
-
*/
|
377
|
-
|
378
|
-
var Submittable = function(){
|
379
|
-
this.intialize();
|
380
|
-
};
|
381
|
-
|
382
|
-
_.extend(Submittable.prototype, {
|
383
|
-
|
384
|
-
intialize: function(){
|
385
|
-
this.submitBtn = $("input[type='submit']");
|
386
|
-
|
387
|
-
var btnTitles = [];
|
388
|
-
|
389
|
-
_.each(this.submitBtn, function(btn){
|
390
|
-
btnTitles.push($(btn).attr('value'));
|
391
|
-
});
|
392
|
-
|
393
|
-
this.submitBtnTitles = btnTitles;
|
394
|
-
this.canSubmit = true;
|
395
|
-
this.globalUploadCount = 0;
|
396
|
-
this._bindEvents();
|
397
|
-
},
|
398
|
-
|
399
|
-
setSubmitButton: function(e, message) {
|
400
|
-
this.submitBtn.attr('value', message);
|
401
|
-
},
|
402
|
-
|
403
|
-
resetSubmitButton: function(){
|
404
|
-
_.each(this.submitBtn, _.bind(function(item, index){
|
405
|
-
$(item).attr('value', this.submitBtnTitles[index]);
|
406
|
-
}, this));
|
407
|
-
},
|
408
|
-
|
409
|
-
onUploadStart: function(e){
|
410
|
-
this.globalUploadCount++;
|
411
|
-
SirTrevor.log('onUploadStart called ' + this.globalUploadCount);
|
412
|
-
|
413
|
-
if(this.globalUploadCount === 1) {
|
414
|
-
this._disableSubmitButton();
|
415
|
-
}
|
416
|
-
},
|
417
|
-
|
418
|
-
onUploadStop: function(e) {
|
419
|
-
this.globalUploadCount = (this.globalUploadCount <= 0) ? 0 : this.globalUploadCount - 1;
|
420
|
-
|
421
|
-
SirTrevor.log('onUploadStop called ' + this.globalUploadCount);
|
422
|
-
|
423
|
-
if(this.globalUploadCount === 0) {
|
424
|
-
this._enableSubmitButton();
|
425
|
-
}
|
426
|
-
},
|
427
|
-
|
428
|
-
onError: function(e){
|
429
|
-
SirTrevor.log('onError called');
|
430
|
-
this.canSubmit = false;
|
431
|
-
},
|
432
|
-
|
433
|
-
_disableSubmitButton: function(message){
|
434
|
-
this.setSubmitButton(null, message || "Please wait...");
|
435
|
-
this.submitBtn
|
436
|
-
.attr('disabled', 'disabled')
|
437
|
-
.addClass('disabled');
|
438
|
-
},
|
439
|
-
|
440
|
-
_enableSubmitButton: function(){
|
441
|
-
this.resetSubmitButton();
|
442
|
-
this.submitBtn
|
443
|
-
.removeAttr('disabled')
|
444
|
-
.removeClass('disabled');
|
445
|
-
},
|
446
|
-
|
447
|
-
_bindEvents: function(){
|
448
|
-
SirTrevor.subscribe("disableSubmitButton", _.bind(this._disableSubmitButton, this));
|
449
|
-
SirTrevor.subscribe("enableSubmitButton", _.bind(this._enableSubmitButton, this));
|
450
|
-
SirTrevor.subscribe("setSubmitButton", _.bind(this.setSubmitButton, this));
|
451
|
-
SirTrevor.subscribe("resetSubmitButton", _.bind(this.resetSubmitButton, this));
|
452
|
-
SirTrevor.subscribe("onError", _.bind(this.onError, this));
|
453
|
-
SirTrevor.subscribe("onUploadStart", _.bind(this.onUploadStart, this));
|
454
|
-
SirTrevor.subscribe("onUploadStop", _.bind(this.onUploadStop, this));
|
455
|
-
}
|
456
|
-
|
457
|
-
});
|
458
|
-
|
459
|
-
SirTrevor.submittable = function(){
|
460
|
-
new Submittable();
|
461
|
-
};
|
462
|
-
/*
|
463
|
-
* Sir Trevor Uploader
|
464
|
-
* Generic Upload implementation that can be extended for blocks
|
465
|
-
*/
|
466
|
-
|
467
|
-
SirTrevor.fileUploader = function(block, file, success, error) {
|
468
|
-
|
469
|
-
SirTrevor.publish("onUploadStart");
|
470
|
-
|
471
|
-
var uid = [block.instance.ID, (new Date()).getTime(), 'raw'].join('-');
|
472
|
-
|
473
|
-
var data = new FormData();
|
474
|
-
|
475
|
-
data.append('attachment[name]', file.name);
|
476
|
-
data.append('attachment[file]', file);
|
477
|
-
data.append('attachment[uid]', uid);
|
478
|
-
|
479
|
-
var callbackSuccess = function(data){
|
480
|
-
if (!_.isUndefined(success) && _.isFunction(success)) {
|
481
|
-
SirTrevor.log('Upload callback called');
|
482
|
-
SirTrevor.publish("onUploadStop");
|
483
|
-
_.bind(success, block)(data);
|
484
|
-
}
|
485
|
-
};
|
486
|
-
|
487
|
-
var callbackError = function(jqXHR, status, errorThrown){
|
488
|
-
if (!_.isUndefined(error) && _.isFunction(error)) {
|
489
|
-
SirTrevor.log('Upload callback error called');
|
490
|
-
SirTrevor.publish("onUploadError");
|
491
|
-
_.bind(error, block)(status);
|
492
|
-
}
|
493
|
-
};
|
494
|
-
|
495
|
-
$.ajax({
|
496
|
-
url: block.instance.options.uploadUrl,
|
497
|
-
data: data,
|
498
|
-
cache: false,
|
499
|
-
contentType: false,
|
500
|
-
processData: false,
|
501
|
-
type: 'POST',
|
502
|
-
success: callbackSuccess,
|
503
|
-
error: callbackError
|
504
|
-
});
|
505
|
-
|
506
|
-
};
|
507
|
-
/*
|
508
|
-
Underscore helpers
|
509
|
-
*/
|
510
|
-
|
511
|
-
var url_regex = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
|
512
|
-
|
513
|
-
_.mixin({
|
514
|
-
isURI : function(string) {
|
515
|
-
return (url_regex.test(string));
|
516
|
-
},
|
517
|
-
|
518
|
-
capitalize : function(string) {
|
519
|
-
return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase();
|
520
|
-
},
|
521
|
-
|
522
|
-
trim : function(string) {
|
523
|
-
return string.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
|
524
|
-
}
|
525
|
-
|
526
|
-
});
|
527
|
-
|
528
|
-
var Block = SirTrevor.Block = function(instance, data) {
|
529
|
-
this.instance = instance;
|
530
|
-
this.type = this._getBlockType();
|
531
|
-
|
532
|
-
this.store("create", this, { data: data });
|
533
|
-
|
534
|
-
this.uploadsCount = 0;
|
535
|
-
this.blockID = _.uniqueId(this.className + '-');
|
536
|
-
|
537
|
-
this._setBaseElements();
|
538
|
-
this._bindFunctions();
|
539
|
-
|
540
|
-
this.render();
|
541
|
-
|
542
|
-
this.initialize.apply(this, arguments);
|
543
|
-
};
|
544
|
-
|
545
|
-
var blockOptions = [
|
546
|
-
"className",
|
547
|
-
"toolbarEnabled",
|
548
|
-
"formattingEnabled",
|
549
|
-
"dropEnabled",
|
550
|
-
"title",
|
551
|
-
"limit",
|
552
|
-
"editorHTML",
|
553
|
-
"dropzoneHTML",
|
554
|
-
"validate",
|
555
|
-
"loadData",
|
556
|
-
"toData",
|
557
|
-
"onDrop",
|
558
|
-
"onContentPasted",
|
559
|
-
"onBlockRender",
|
560
|
-
"beforeBlockRender",
|
561
|
-
"setTextLimit",
|
562
|
-
"toMarkdown",
|
563
|
-
"toHTML"
|
564
|
-
];
|
565
|
-
|
566
|
-
_.extend(Block.prototype, FunctionBind, {
|
567
|
-
|
568
|
-
bound: ["_handleDrop", "_handleContentPaste", "onBlockFocus", "onBlockBlur", "onDrop", "onDragStart", "onDragEnd"],
|
569
|
-
|
570
|
-
$: function(selector) {
|
571
|
-
return this.$el.find(selector);
|
572
|
-
},
|
573
|
-
|
574
|
-
$$: function(selector) {
|
575
|
-
return this.$editor.find(selector);
|
576
|
-
},
|
577
|
-
|
578
|
-
/* Defaults to be overriden if required */
|
579
|
-
className: '',
|
580
|
-
title: '',
|
581
|
-
limit: 0,
|
582
|
-
editorHTML: '<div></div>',
|
583
|
-
dropzoneHTML: '<div class="dropzone"><p>Drop content here</p></div>',
|
584
|
-
toolbarEnabled: true,
|
585
|
-
dropEnabled: false,
|
586
|
-
formattingEnabled: true,
|
587
|
-
|
588
|
-
initialize: function() {},
|
589
|
-
|
590
|
-
loadData: function(data) {},
|
591
|
-
onBlockRender: function(){},
|
592
|
-
beforeBlockRender: function(){},
|
593
|
-
setTextLimit: function() {},
|
594
|
-
toMarkdown: function(markdown){ return markdown; },
|
595
|
-
toHTML: function(html){ return html; },
|
596
|
-
|
597
|
-
store: function(){
|
598
|
-
return SirTrevor.blockStore.apply(this, arguments);
|
599
|
-
},
|
600
|
-
|
601
|
-
render: function() {
|
602
|
-
|
603
|
-
this.beforeBlockRender();
|
604
|
-
|
605
|
-
// Insert before the marker
|
606
|
-
this.instance.marker.hide();
|
607
|
-
this.instance.marker.$el.before(this.$el);
|
608
|
-
|
609
|
-
// Do we have a dropzone?
|
610
|
-
if (this.dropEnabled) {
|
611
|
-
this._initDragDrop();
|
612
|
-
}
|
613
|
-
|
614
|
-
// Has data already?
|
615
|
-
var currentData = this.getData();
|
616
|
-
|
617
|
-
if (!_.isUndefined(currentData) && !_.isEmpty(currentData)) {
|
618
|
-
this._loadData();
|
619
|
-
}
|
620
|
-
|
621
|
-
// And save the state
|
622
|
-
this.save();
|
623
|
-
|
624
|
-
// Add UI elements
|
625
|
-
this.$el.append($('<span>',{ 'class': this.instance.baseCSS("drag-handle"), draggable: true }));
|
626
|
-
this.$el.append($('<span>',{ 'class': this.instance.baseCSS("remove-block") }));
|
627
|
-
|
628
|
-
// Stop events propagating through to the container
|
629
|
-
this.$el
|
630
|
-
.bind('drop', halt)
|
631
|
-
.bind('mouseover', halt)
|
632
|
-
.bind('mouseout', halt)
|
633
|
-
.bind('dragleave', halt)
|
634
|
-
.bind('mouseover', function(ev){ $(this).siblings().removeClass('active'); $(this).addClass('active'); })
|
635
|
-
.bind('mouseout', function(ev){ $(this).removeClass('active'); })
|
636
|
-
.bind('dragover', function(ev){ ev.preventDefault(); });
|
637
|
-
|
638
|
-
// Handle pastes
|
639
|
-
this._initPaste();
|
640
|
-
|
641
|
-
// Delete
|
642
|
-
this.$('.' + this.instance.baseCSS("remove-block")).bind('click', this.onDeleteClick);
|
643
|
-
|
644
|
-
// Handle text blocks
|
645
|
-
if (this.$$('.text-block').length > 0) {
|
646
|
-
document.execCommand("styleWithCSS", false, false);
|
647
|
-
document.execCommand("insertBrOnReturn", false, true);
|
648
|
-
|
649
|
-
// Strip out all the HTML on paste
|
650
|
-
this.$$('.text-block')
|
651
|
-
.bind('paste', this._handleContentPaste)
|
652
|
-
.bind('focus', this.onBlockFocus)
|
653
|
-
.bind('blur', this.onBlockBlur);
|
654
|
-
|
655
|
-
// Formatting
|
656
|
-
this._initFormatting();
|
657
|
-
}
|
658
|
-
|
659
|
-
// Focus if we're adding an empty block, but only if not
|
660
|
-
// the only block (i.e. page has just loaded a new editor)
|
661
|
-
if (_.isEmpty(currentData.data) && this.instance.blocks.length > 0) {
|
662
|
-
var inputs = this.$$('[contenteditable="true"], input');
|
663
|
-
if (inputs.length > 0 && !this.dropEnabled) {
|
664
|
-
inputs[0].focus();
|
665
|
-
}
|
666
|
-
}
|
667
|
-
|
668
|
-
// Reorderable
|
669
|
-
this._initReordering();
|
670
|
-
|
671
|
-
// Set ready state
|
672
|
-
this.$el.addClass(this.instance.baseCSS('item-ready'));
|
673
|
-
|
674
|
-
this.setTextLimit();
|
675
|
-
this.onBlockRender();
|
676
|
-
},
|
677
|
-
|
678
|
-
remove: function() {
|
679
|
-
this.$el.remove();
|
680
|
-
},
|
681
|
-
|
682
|
-
/* Save the state of this block onto the blocks data attr */
|
683
|
-
save: function() {
|
684
|
-
this.toData();
|
685
|
-
return this.store("read", this);
|
686
|
-
},
|
687
|
-
|
688
|
-
getData: function() {
|
689
|
-
return this.store("read", this).data;
|
690
|
-
},
|
691
|
-
|
692
|
-
setData: function(data) {
|
693
|
-
SirTrevor.log("Setting data for block " + this.blockID);
|
694
|
-
this.store("save", this, { data: data });
|
695
|
-
},
|
696
|
-
|
697
|
-
loading: function() {
|
698
|
-
|
699
|
-
if(!_.isUndefined(this.spinner)) {
|
700
|
-
this.ready();
|
701
|
-
}
|
702
|
-
|
703
|
-
this.spinner = new Spinner(this.instance.options.spinner);
|
704
|
-
this.spinner.spin(this.$el[0]);
|
705
|
-
|
706
|
-
this.$el.addClass('loading');
|
707
|
-
},
|
708
|
-
|
709
|
-
ready: function() {
|
710
|
-
this.$el.removeClass('loading');
|
711
|
-
if (!_.isUndefined(this.spinner)) {
|
712
|
-
this.spinner.stop();
|
713
|
-
delete this.spinner;
|
714
|
-
}
|
715
|
-
},
|
716
|
-
|
717
|
-
/* Generic implementations */
|
718
|
-
|
719
|
-
validate: function() {
|
720
|
-
|
721
|
-
this._beforeValidate();
|
722
|
-
|
723
|
-
var fields = this.$$('.required, [data-maxlength]'),
|
724
|
-
errors = 0;
|
725
|
-
|
726
|
-
_.each(fields, _.bind(function(field) {
|
727
|
-
field = $(field);
|
728
|
-
var content = (field.attr('contenteditable')) ? field.text() : field.val(),
|
729
|
-
too_long = (field.attr('data-maxlength') && field.too_long()),
|
730
|
-
required = field.hasClass('required');
|
731
|
-
|
732
|
-
if ((required && content.length === 0) || too_long) {
|
733
|
-
// Error!
|
734
|
-
field.addClass(this.instance.baseCSS(this.instance.options.errorClass));
|
735
|
-
errors++;
|
736
|
-
}
|
737
|
-
}, this));
|
738
|
-
|
739
|
-
if (errors > 0) {
|
740
|
-
this.$el.addClass(this.instance.baseCSS('block-with-errors'));
|
741
|
-
}
|
742
|
-
|
743
|
-
return (errors === 0);
|
744
|
-
},
|
745
|
-
|
746
|
-
/*
|
747
|
-
Generic toData implementation.
|
748
|
-
Can be overwritten, although hopefully this will cover most situations
|
749
|
-
*/
|
750
|
-
toData: function() {
|
751
|
-
|
752
|
-
SirTrevor.log("toData for " + this.blockID);
|
753
|
-
|
754
|
-
var bl = this.$el,
|
755
|
-
dataObj = {};
|
756
|
-
|
757
|
-
/* Simple to start. Add conditions later */
|
758
|
-
if (this.$$('.text-block').length > 0) {
|
759
|
-
var content = this.$$('.text-block').html();
|
760
|
-
if (content.length > 0) {
|
761
|
-
dataObj.text = this.instance._toMarkdown(content, this.type);
|
762
|
-
}
|
763
|
-
}
|
764
|
-
|
765
|
-
var hasTextAndData = (!_.isUndefined(dataObj.text) || this.$$('.text-block').length === 0);
|
766
|
-
|
767
|
-
// Add any inputs to the data attr
|
768
|
-
if(this.$$('input[type="text"]').not('.paste-block').length > 0) {
|
769
|
-
this.$$('input[type="text"]').each(function(index,input){
|
770
|
-
input = $(input);
|
771
|
-
if (input.val().length > 0 && hasTextAndData) {
|
772
|
-
dataObj[input.attr('name')] = input.val();
|
773
|
-
}
|
774
|
-
});
|
775
|
-
}
|
776
|
-
|
777
|
-
this.$$('select').each(function(index,input){
|
778
|
-
input = $(input);
|
779
|
-
if(input.val().length > 0 && hasTextAndData) {
|
780
|
-
dataObj[input.attr('name')] = input.val();
|
781
|
-
}
|
782
|
-
});
|
783
|
-
|
784
|
-
this.$$('input[type="file"]').each(function(index,input) {
|
785
|
-
input = $(input);
|
786
|
-
dataObj.file = input.data('json');
|
787
|
-
});
|
788
|
-
|
789
|
-
// Set
|
790
|
-
if(!_.isEmpty(dataObj)) {
|
791
|
-
this.setData(dataObj);
|
792
|
-
}
|
793
|
-
},
|
794
|
-
|
795
|
-
/*
|
796
|
-
* Event handlers
|
797
|
-
*/
|
798
|
-
|
799
|
-
onDrop: function(dataTransferObj) {},
|
800
|
-
|
801
|
-
onDragStart: function(ev){
|
802
|
-
var item = $(ev.target);
|
803
|
-
ev.originalEvent.dataTransfer.setDragImage(item.parent()[0], 13, 25);
|
804
|
-
ev.originalEvent.dataTransfer.setData('Text', item.parent().attr('id'));
|
805
|
-
item.parent().addClass('dragging');
|
806
|
-
this.instance.formatBar.hide();
|
807
|
-
},
|
808
|
-
|
809
|
-
onDragEnd: function(ev){
|
810
|
-
var item = $(ev.target);
|
811
|
-
item.parent().removeClass('dragging');
|
812
|
-
this.instance.marker.hide();
|
813
|
-
this.instance.formatBar.show();
|
814
|
-
},
|
815
|
-
|
816
|
-
onDeleteClick: function(ev) {
|
817
|
-
if (confirm('Are you sure you wish to delete this content?')) {
|
818
|
-
this.instance.removeBlock(this);
|
819
|
-
halt(ev);
|
820
|
-
}
|
821
|
-
},
|
822
|
-
|
823
|
-
onContentPasted: function(ev){
|
824
|
-
var textBlock = this.$$('.text-block');
|
825
|
-
if (textBlock.length > 0) {
|
826
|
-
textBlock.html(this.instance._toHTML(this.instance._toMarkdown(textBlock.html(), this.type),this.type));
|
827
|
-
}
|
828
|
-
},
|
829
|
-
|
830
|
-
onBlockFocus: function(e) {
|
831
|
-
this.$el.addClass('focussed');
|
832
|
-
},
|
833
|
-
|
834
|
-
onBlockBlur: function(e) {
|
835
|
-
this.$el.removeClass('focussed');
|
836
|
-
},
|
837
|
-
|
838
|
-
/*
|
839
|
-
Generic Upload Attachment Function
|
840
|
-
Designed to handle any attachments
|
841
|
-
*/
|
842
|
-
|
843
|
-
uploader: function(file, callback){
|
844
|
-
SirTrevor.fileUploader(this, file, callback);
|
845
|
-
},
|
846
|
-
|
847
|
-
/* Private methods */
|
848
|
-
|
849
|
-
_loadData: function() {
|
850
|
-
SirTrevor.log("loadData for " + this.blockID);
|
851
|
-
|
852
|
-
this.loading();
|
853
|
-
|
854
|
-
if(this.dropEnabled) {
|
855
|
-
this.$dropzone.hide();
|
856
|
-
this.$editor.show();
|
857
|
-
}
|
858
|
-
|
859
|
-
SirTrevor.publish("editor/block/loadData");
|
860
|
-
|
861
|
-
this.loadData(this.getData());
|
862
|
-
this.ready();
|
863
|
-
},
|
864
|
-
|
865
|
-
_beforeValidate: function() {
|
866
|
-
this.errors = [];
|
867
|
-
var errorClass = this.instance.baseCSS("error");
|
868
|
-
this.$el.removeClass(this.instance.baseCSS('block-with-errors'));
|
869
|
-
this.$('.' + errorClass).removeClass(errorClass);
|
870
|
-
this.$('.error-marker').remove();
|
871
|
-
},
|
872
|
-
|
873
|
-
_handleContentPaste: function(ev) {
|
874
|
-
// We need a little timeout here
|
875
|
-
var timed = function(ev){
|
876
|
-
// Delegate this off to the super method that can be overwritten
|
877
|
-
this.onContentPasted(ev);
|
878
|
-
};
|
879
|
-
_.delay(_.bind(timed, this, ev), 100);
|
880
|
-
},
|
881
|
-
|
882
|
-
_handleDrop: function(e) {
|
883
|
-
|
884
|
-
e.preventDefault();
|
885
|
-
e = e.originalEvent;
|
886
|
-
|
887
|
-
SirTrevor.publish("editor/block/handleDrop");
|
888
|
-
|
889
|
-
var el = $(e.target),
|
890
|
-
types = e.dataTransfer.types,
|
891
|
-
type, data = [];
|
892
|
-
|
893
|
-
this.instance.marker.hide();
|
894
|
-
this.$dropzone.removeClass('drag-enter');
|
895
|
-
|
896
|
-
/*
|
897
|
-
Check the type we just received,
|
898
|
-
delegate it away to our blockTypes to process
|
899
|
-
*/
|
900
|
-
|
901
|
-
if (!_.isUndefined(types)) {
|
902
|
-
if (_.include(types, 'Files') || _.include(types, 'text/plain') || _.include(types, 'text/uri-list')) {
|
903
|
-
this.onDrop(e.dataTransfer);
|
904
|
-
}
|
905
|
-
}
|
906
|
-
},
|
907
|
-
|
908
|
-
_setBaseElements: function(){
|
909
|
-
var el = (_.isFunction(this.editorHTML)) ? this.editorHTML() : this.editorHTML;
|
910
|
-
|
911
|
-
// Set
|
912
|
-
var editor = $('<div>', {
|
913
|
-
'class': this.instance.baseCSS("editor-block") + ' ' + this._getBlockClass(),
|
914
|
-
html: el
|
915
|
-
});
|
916
|
-
|
917
|
-
this.$el = $('<div>', {
|
918
|
-
'class': this.instance.baseCSS("block"),
|
919
|
-
id: this.blockID,
|
920
|
-
"data-type": this.type,
|
921
|
-
"data-instance": this.instance.ID,
|
922
|
-
html: editor
|
923
|
-
});
|
924
|
-
|
925
|
-
// Set our element references
|
926
|
-
this.el = this.$el[0];
|
927
|
-
this.$editor = editor;
|
928
|
-
},
|
929
|
-
|
930
|
-
_getBlockType: function() {
|
931
|
-
var objName = "";
|
932
|
-
for (var block in SirTrevor.Blocks) {
|
933
|
-
if (SirTrevor.Blocks[block].prototype == Object.getPrototypeOf(this)) {
|
934
|
-
objName = block;
|
935
|
-
}
|
936
|
-
}
|
937
|
-
return objName;
|
938
|
-
},
|
939
|
-
|
940
|
-
_getBlockClass: function() {
|
941
|
-
return this.className + '-block';
|
942
|
-
},
|
943
|
-
|
944
|
-
/*
|
945
|
-
* Init functions for adding functionality
|
946
|
-
*/
|
947
|
-
|
948
|
-
_initDragDrop: function() {
|
949
|
-
SirTrevor.log("Adding drag and drop capabilities for block " + this.blockID);
|
950
|
-
|
951
|
-
this.$dropzone = $("<div>", {
|
952
|
-
html: this.dropzoneHTML,
|
953
|
-
'class': "dropzone " + this._getBlockClass()
|
954
|
-
});
|
955
|
-
this.$el.append(this.$dropzone);
|
956
|
-
this.$editor.hide();
|
957
|
-
|
958
|
-
// Bind our drop event
|
959
|
-
this.$dropzone.bind('drop', this._handleDrop)
|
960
|
-
.bind('dragenter', function(e) { halt(e); $(this).addClass('drag-enter'); })
|
961
|
-
.bind('dragover', function(e) {
|
962
|
-
e.originalEvent.dataTransfer.dropEffect = "copy";
|
963
|
-
halt(e);
|
964
|
-
$(this).addClass('drag-enter');
|
965
|
-
})
|
966
|
-
.bind('dragleave', function(e) { halt(e); $(this).removeClass('drag-enter'); });
|
967
|
-
},
|
968
|
-
|
969
|
-
_initReordering: function() {
|
970
|
-
this.$('.' + this.instance.baseCSS("drag-handle"))
|
971
|
-
.bind('dragstart', this.onDragStart)
|
972
|
-
.bind('dragend', this.onDragEnd)
|
973
|
-
.bind('drag', this.instance.marker.show);
|
974
|
-
},
|
975
|
-
|
976
|
-
_initFormatting: function() {
|
977
|
-
// Enable formatting keyboard input
|
978
|
-
var formatter;
|
979
|
-
for (var name in this.instance.formatters) {
|
980
|
-
if (this.instance.formatters.hasOwnProperty(name)) {
|
981
|
-
formatter = SirTrevor.Formatters[name];
|
982
|
-
if (!_.isUndefined(formatter.keyCode)) {
|
983
|
-
formatter._bindToBlock(this.$editor);
|
984
|
-
}
|
985
|
-
}
|
986
|
-
}
|
987
|
-
},
|
988
|
-
|
989
|
-
_initPaste: function() {
|
990
|
-
this.$('.paste-block')
|
991
|
-
.bind('click', function(){ $(this).select(); })
|
992
|
-
.bind('paste', this._handleContentPaste)
|
993
|
-
.bind('submit', this._handleContentPaste);
|
994
|
-
},
|
995
|
-
|
996
|
-
_initTextLimits: function() {
|
997
|
-
this.$$('input[maxlength!=-1][maxlength!=524288][maxlength!=2147483647]').limit_chars();
|
998
|
-
}
|
999
|
-
|
1000
|
-
});
|
1001
|
-
|
1002
|
-
Block.extend = extend; // Allow our Block to be extended.
|
1003
|
-
var Format = SirTrevor.Formatter = function(options){
|
1004
|
-
this.formatId = _.uniqueId('format-');
|
1005
|
-
this._configure(options || {});
|
1006
|
-
this.initialize.apply(this, arguments);
|
1007
|
-
};
|
1008
|
-
|
1009
|
-
var formatOptions = ["title", "className", "cmd", "keyCode", "param", "onClick", "toMarkdown", "toHTML"];
|
1010
|
-
|
1011
|
-
_.extend(Format.prototype, {
|
1012
|
-
|
1013
|
-
title: '',
|
1014
|
-
className: '',
|
1015
|
-
cmd: null,
|
1016
|
-
keyCode: null,
|
1017
|
-
param: null,
|
1018
|
-
toMarkdown: function(markdown){ return markdown; },
|
1019
|
-
toHTML: function(html){ return html; },
|
1020
|
-
|
1021
|
-
initialize: function(){},
|
1022
|
-
|
1023
|
-
_configure: function(options) {
|
1024
|
-
if (this.options) options = _.extend({}, this.options, options);
|
1025
|
-
for (var i = 0, l = formatOptions.length; i < l; i++) {
|
1026
|
-
var attr = formatOptions[i];
|
1027
|
-
if (options[attr]) this[attr] = options[attr];
|
1028
|
-
}
|
1029
|
-
this.options = options;
|
1030
|
-
},
|
1031
|
-
|
1032
|
-
_bindToBlock: function(block) {
|
1033
|
-
|
1034
|
-
var formatter = this,
|
1035
|
-
ctrlDown = false;
|
1036
|
-
|
1037
|
-
block
|
1038
|
-
.on('keyup','.text-block', function(ev) {
|
1039
|
-
if(ev.which == 17 || ev.which == 224) {
|
1040
|
-
ctrlDown = false;
|
1041
|
-
}
|
1042
|
-
})
|
1043
|
-
.on('keydown','.text-block', { formatter: formatter }, function(ev) {
|
1044
|
-
if(ev.which == 17 || ev.which == 224) {
|
1045
|
-
ctrlDown = true;
|
1046
|
-
}
|
1047
|
-
if(ev.which == ev.data.formatter.keyCode && ctrlDown === true) {
|
1048
|
-
document.execCommand(ev.data.formatter.cmd, false, true);
|
1049
|
-
ev.preventDefault();
|
1050
|
-
}
|
1051
|
-
});
|
1052
|
-
}
|
1053
|
-
});
|
1054
|
-
|
1055
|
-
Format.extend = extend; // Allow our Formatters to be extended.
|
1056
|
-
|
1057
|
-
/* Default Blocks */
|
1058
|
-
/*
|
1059
|
-
Block Quote
|
1060
|
-
*/
|
1061
|
-
|
1062
|
-
SirTrevor.Blocks.Quote = SirTrevor.Block.extend({
|
1063
|
-
|
1064
|
-
title: "Quote",
|
1065
|
-
className: "quote",
|
1066
|
-
limit: 0,
|
1067
|
-
|
1068
|
-
editorHTML: function() {
|
1069
|
-
return _.template('<blockquote class="required text-block <%= className %>" contenteditable="true"></blockquote><div class="input text"><label>Credit</label><input maxlength="140" name="cite" class="input-string required" type="text" /></div>', this);
|
1070
|
-
},
|
1071
|
-
|
1072
|
-
loadData: function(data){
|
1073
|
-
this.$$('.text-block').html(this.instance._toHTML(data.text, this.type));
|
1074
|
-
this.$$('input').val(data.cite);
|
1075
|
-
},
|
1076
|
-
|
1077
|
-
toMarkdown: function(markdown) {
|
1078
|
-
return markdown.replace(/^(.+)$/mg,"> $1");
|
1079
|
-
}
|
1080
|
-
|
1081
|
-
});
|
1082
|
-
/*
|
1083
|
-
Gallery
|
1084
|
-
*/
|
1085
|
-
|
1086
|
-
var dropzone_templ = "<p>Drop images here</p><div class=\"input submit\"><input type=\"file\" multiple=\"multiple\" /></div><button>...or choose file(s)</button>";
|
1087
|
-
|
1088
|
-
SirTrevor.Blocks.Gallery = SirTrevor.Block.extend({
|
1089
|
-
|
1090
|
-
title: "Gallery",
|
1091
|
-
className: "gallery",
|
1092
|
-
dropEnabled: true,
|
1093
|
-
editorHTML: "<div class=\"gallery-items\"><p>Gallery Contents:</p><ul></ul></div>",
|
1094
|
-
dropzoneHTML: dropzone_templ,
|
1095
|
-
|
1096
|
-
loadData: function(data){
|
1097
|
-
// Find all our gallery blocks and draw nice list items from it
|
1098
|
-
if (_.isArray(data)) {
|
1099
|
-
_.each(data, _.bind(function(item){
|
1100
|
-
// Create an image block from this
|
1101
|
-
this.renderGalleryThumb(item);
|
1102
|
-
}, this));
|
1103
|
-
|
1104
|
-
// Show the dropzone too
|
1105
|
-
this.$dropzone.show();
|
1106
|
-
}
|
1107
|
-
},
|
1108
|
-
|
1109
|
-
renderGalleryThumb: function(item) {
|
1110
|
-
|
1111
|
-
if(_.isUndefined(item.data.file)) return false;
|
1112
|
-
|
1113
|
-
var img = $("<img>", {
|
1114
|
-
src: item.data.file.thumb.url
|
1115
|
-
});
|
1116
|
-
|
1117
|
-
var list = $('<li>', {
|
1118
|
-
id: _.uniqueId('gallery-item'),
|
1119
|
-
class: 'gallery-item',
|
1120
|
-
html: img
|
1121
|
-
});
|
1122
|
-
|
1123
|
-
list.append($("<span>", {
|
1124
|
-
class: 'delete',
|
1125
|
-
click: _.bind(function(e){
|
1126
|
-
// Remove this item
|
1127
|
-
halt(e);
|
1128
|
-
|
1129
|
-
if (confirm('Are you sure you wish to delete this image?')) {
|
1130
|
-
$(e.target).parent().remove();
|
1131
|
-
this.reindexData();
|
1132
|
-
}
|
1133
|
-
}, this)
|
1134
|
-
}));
|
1135
|
-
|
1136
|
-
list.data('block', item);
|
1137
|
-
|
1138
|
-
this.$$('ul').append(list);
|
1139
|
-
|
1140
|
-
// Make it sortable
|
1141
|
-
list
|
1142
|
-
.dropArea()
|
1143
|
-
.bind('dragstart', _.bind(function(ev){
|
1144
|
-
var item = $(ev.target);
|
1145
|
-
ev.originalEvent.dataTransfer.setData('Text', item.parent().attr('id'));
|
1146
|
-
item.parent().addClass('dragging');
|
1147
|
-
}, this))
|
1148
|
-
|
1149
|
-
.bind('drag', _.bind(function(ev){
|
1150
|
-
|
1151
|
-
}, this))
|
1152
|
-
|
1153
|
-
.bind('dragend', _.bind(function(ev){
|
1154
|
-
var item = $(ev.target);
|
1155
|
-
item.parent().removeClass('dragging');
|
1156
|
-
}, this))
|
1157
|
-
|
1158
|
-
.bind('dragover', _.bind(function(ev){
|
1159
|
-
var item = $(ev.target);
|
1160
|
-
item.parents('li').addClass('dragover');
|
1161
|
-
}, this))
|
1162
|
-
|
1163
|
-
.bind('dragleave', _.bind(function(ev){
|
1164
|
-
var item = $(ev.target);
|
1165
|
-
item.parents('li').removeClass('dragover');
|
1166
|
-
}, this))
|
1167
|
-
|
1168
|
-
.bind('drop', _.bind(function(ev){
|
1169
|
-
|
1170
|
-
var item = $(ev.target),
|
1171
|
-
parent = item.parent();
|
1172
|
-
|
1173
|
-
item = (item.hasClass('gallery-item') ? item : parent);
|
1174
|
-
|
1175
|
-
this.$$('ul li.dragover').removeClass('dragover');
|
1176
|
-
|
1177
|
-
// Get the item
|
1178
|
-
var target = $('#' + ev.originalEvent.dataTransfer.getData("text/plain"));
|
1179
|
-
|
1180
|
-
if(target.attr('id') === item.attr('id')) return false;
|
1181
|
-
|
1182
|
-
if (target.length > 0 && target.hasClass('gallery-item')) {
|
1183
|
-
item.before(target);
|
1184
|
-
}
|
1185
|
-
|
1186
|
-
// Reindex the data
|
1187
|
-
this.reindexData();
|
1188
|
-
|
1189
|
-
}, this));
|
1190
|
-
},
|
1191
|
-
|
1192
|
-
onBlockRender: function(){
|
1193
|
-
// We need to setup this block for reordering
|
1194
|
-
/* Setup the upload button */
|
1195
|
-
this.$dropzone.find('button').bind('click', halt);
|
1196
|
-
this.$dropzone.find('input').on('change', _.bind(function(ev){
|
1197
|
-
this.onDrop(ev.currentTarget);
|
1198
|
-
}, this));
|
1199
|
-
},
|
1200
|
-
|
1201
|
-
reindexData: function() {
|
1202
|
-
var dataStruct = this.getData();
|
1203
|
-
dataStruct = [];
|
1204
|
-
|
1205
|
-
_.each(this.$$('li.gallery-item'), function(li){
|
1206
|
-
li = $(li);
|
1207
|
-
dataStruct.push(li.data('block'));
|
1208
|
-
});
|
1209
|
-
|
1210
|
-
this.setData(dataStruct);
|
1211
|
-
},
|
1212
|
-
|
1213
|
-
onDrop: function(transferData){
|
1214
|
-
|
1215
|
-
if (transferData.files.length > 0) {
|
1216
|
-
// Multi files 'ere
|
1217
|
-
var l = transferData.files.length,
|
1218
|
-
file, urlAPI = (typeof URL !== "undefined") ? URL : (typeof webkitURL !== "undefined") ? webkitURL : null;
|
1219
|
-
|
1220
|
-
this.loading();
|
1221
|
-
|
1222
|
-
while (l--) {
|
1223
|
-
file = transferData.files[l];
|
1224
|
-
if (/image/.test(file.type)) {
|
1225
|
-
// Inc the upload count
|
1226
|
-
this.uploadsCount += 1;
|
1227
|
-
this.$editor.show();
|
1228
|
-
|
1229
|
-
/* Upload */
|
1230
|
-
this.uploader(file, function(data){
|
1231
|
-
|
1232
|
-
this.uploadsCount -= 1;
|
1233
|
-
var dataStruct = this.getData();
|
1234
|
-
data = { type: "image", data: data };
|
1235
|
-
|
1236
|
-
// Add to our struct
|
1237
|
-
if (!_.isArray(dataStruct)) {
|
1238
|
-
dataStruct = [];
|
1239
|
-
}
|
1240
|
-
dataStruct.push(data);
|
1241
|
-
this.setData(dataStruct);
|
1242
|
-
|
1243
|
-
// Pass this off to our render gallery thumb method
|
1244
|
-
this.renderGalleryThumb(data);
|
1245
|
-
|
1246
|
-
if(this.uploadsCount === 0) {
|
1247
|
-
this.ready();
|
1248
|
-
}
|
1249
|
-
});
|
1250
|
-
}
|
1251
|
-
}
|
1252
|
-
}
|
1253
|
-
}
|
1254
|
-
|
1255
|
-
});
|
1256
|
-
/*
|
1257
|
-
Simple Image Block
|
1258
|
-
*/
|
1259
|
-
|
1260
|
-
var dropzone_templ = "<p>Drop image here</p><div class=\"input submit\"><input type=\"file\" /></div><button>...or choose a file</button>";
|
1261
|
-
|
1262
|
-
|
1263
|
-
SirTrevor.Blocks.Image = SirTrevor.Block.extend({
|
1264
|
-
|
1265
|
-
title: "Image",
|
1266
|
-
className: "image",
|
1267
|
-
dropEnabled: true,
|
1268
|
-
|
1269
|
-
dropzoneHTML: dropzone_templ,
|
1270
|
-
|
1271
|
-
loadData: function(data){
|
1272
|
-
// Create our image tag
|
1273
|
-
this.$editor.html($('<img>', {
|
1274
|
-
src: data.file.url
|
1275
|
-
}));
|
1276
|
-
},
|
1277
|
-
|
1278
|
-
onBlockRender: function(){
|
1279
|
-
/* Setup the upload button */
|
1280
|
-
this.$dropzone.find('button').bind('click', halt);
|
1281
|
-
this.$dropzone.find('input').on('change', _.bind(function(ev){
|
1282
|
-
this.onDrop(ev.currentTarget);
|
1283
|
-
}, this));
|
1284
|
-
},
|
1285
|
-
|
1286
|
-
onDrop: function(transferData){
|
1287
|
-
var file = transferData.files[0],
|
1288
|
-
urlAPI = (typeof URL !== "undefined") ? URL : (typeof webkitURL !== "undefined") ? webkitURL : null;
|
1289
|
-
|
1290
|
-
// Handle one upload at a time
|
1291
|
-
if (/image/.test(file.type)) {
|
1292
|
-
this.loading();
|
1293
|
-
// Show this image on here
|
1294
|
-
this.$dropzone.hide();
|
1295
|
-
this.$editor.html($('<img>', {
|
1296
|
-
src: urlAPI.createObjectURL(file)
|
1297
|
-
}));
|
1298
|
-
this.$editor.show();
|
1299
|
-
|
1300
|
-
// Upload!
|
1301
|
-
SirTrevor.publish('setSubmitButton', ['Please wait...']);
|
1302
|
-
this.uploader(
|
1303
|
-
file,
|
1304
|
-
function(data){
|
1305
|
-
// Store the data on this block
|
1306
|
-
this.setData(data);
|
1307
|
-
// Done
|
1308
|
-
this.ready();
|
1309
|
-
},
|
1310
|
-
function(error){
|
1311
|
-
alert('Error!');
|
1312
|
-
}
|
1313
|
-
);
|
1314
|
-
}
|
1315
|
-
}
|
1316
|
-
});
|
1317
|
-
/*
|
1318
|
-
Text Block
|
1319
|
-
*/
|
1320
|
-
SirTrevor.Blocks.Text = SirTrevor.Block.extend({
|
1321
|
-
|
1322
|
-
title: "Text",
|
1323
|
-
className: "text",
|
1324
|
-
limit: 0,
|
1325
|
-
|
1326
|
-
editorHTML: '<div class="required text-block" contenteditable="true"></div>',
|
1327
|
-
|
1328
|
-
loadData: function(data){
|
1329
|
-
this.$$('.text-block').html(this.instance._toHTML(data.text, this.type));
|
1330
|
-
}
|
1331
|
-
});
|
1332
|
-
var t_template = '<p>Drop tweet link here</p><div class="input text"><label>or paste URL:</label><input type="text" class="paste-block"></div>';
|
1333
|
-
var tweet_template = '<div class="tweet"><img src="<%= user.profile_image_url %>" class="tweet-avatar"><div class="tweet-body"><p class="tweet-user"><a href="http://twitter.com/#!/<%= user.screen_name %>" class="tweet-user">@<%= user.screen_name %></a> on Twitter</p><p class="tweet-text"><%= text %></p><time><%= created_at %></time></div></div>';
|
1334
|
-
|
1335
|
-
SirTrevor.Blocks.Tweet = SirTrevor.Block.extend({
|
1336
|
-
|
1337
|
-
title: "Tweet",
|
1338
|
-
className: "tweet",
|
1339
|
-
dropEnabled: true,
|
1340
|
-
|
1341
|
-
dropzoneHTML: t_template,
|
1342
|
-
|
1343
|
-
loadData: function(data){
|
1344
|
-
this.$editor.html(_.template(tweet_template, data));
|
1345
|
-
},
|
1346
|
-
|
1347
|
-
onContentPasted: function(event){
|
1348
|
-
// Content pasted. Delegate to the drop parse method
|
1349
|
-
var input = $(event.target),
|
1350
|
-
val = input.val();
|
1351
|
-
|
1352
|
-
// Pass this to the same handler as onDrop
|
1353
|
-
this.handleTwitterDropPaste(val);
|
1354
|
-
},
|
1355
|
-
|
1356
|
-
handleTwitterDropPaste: function(url){
|
1357
|
-
|
1358
|
-
if(_.isURI(url))
|
1359
|
-
{
|
1360
|
-
if (url.indexOf("twitter") != -1 && url.indexOf("status") != -1) {
|
1361
|
-
// Twitter status
|
1362
|
-
var tweetID = url.match(/[^\/]+$/);
|
1363
|
-
if (!_.isEmpty(tweetID)) {
|
1364
|
-
|
1365
|
-
this.loading();
|
1366
|
-
|
1367
|
-
tweetID = tweetID[0];
|
1368
|
-
|
1369
|
-
var tweetCallbackSuccess = function(data) {
|
1370
|
-
// Parse the twitter object into something a bit slimmer..
|
1371
|
-
var obj = {
|
1372
|
-
user: {
|
1373
|
-
profile_image_url: data.user.profile_image_url,
|
1374
|
-
profile_image_url_https: data.user.profile_image_url_https,
|
1375
|
-
screen_name: data.user.screen_name,
|
1376
|
-
name: data.user.name
|
1377
|
-
},
|
1378
|
-
text: data.text,
|
1379
|
-
created_at: data.created_at,
|
1380
|
-
status_url: url
|
1381
|
-
};
|
1382
|
-
|
1383
|
-
// Save this data on the block
|
1384
|
-
this.setData(obj);
|
1385
|
-
this._loadData();
|
1386
|
-
|
1387
|
-
this.ready();
|
1388
|
-
};
|
1389
|
-
|
1390
|
-
var tweetCallbackFail = function(){
|
1391
|
-
this.ready();
|
1392
|
-
};
|
1393
|
-
|
1394
|
-
// Make our AJAX call
|
1395
|
-
$.ajax({
|
1396
|
-
url: "http://api.twitter.com/1/statuses/show/" + tweetID + ".json",
|
1397
|
-
dataType: "JSONP",
|
1398
|
-
success: _.bind(tweetCallbackSuccess, this),
|
1399
|
-
error: _.bind(tweetCallbackFail, this)
|
1400
|
-
});
|
1401
|
-
}
|
1402
|
-
}
|
1403
|
-
}
|
1404
|
-
|
1405
|
-
},
|
1406
|
-
|
1407
|
-
onDrop: function(transferData){
|
1408
|
-
var url = transferData.getData('text/plain');
|
1409
|
-
this.handleTwitterDropPaste(url);
|
1410
|
-
}
|
1411
|
-
});
|
1412
|
-
/*
|
1413
|
-
Unordered List
|
1414
|
-
*/
|
1415
|
-
|
1416
|
-
var template = '<div class="text-block <%= className %>" contenteditable="true"></div>';
|
1417
|
-
|
1418
|
-
SirTrevor.Blocks.Ul = SirTrevor.Block.extend({
|
1419
|
-
|
1420
|
-
title: "List",
|
1421
|
-
className: "list",
|
1422
|
-
|
1423
|
-
editorHTML: function() {
|
1424
|
-
return _.template(template, this);
|
1425
|
-
},
|
1426
|
-
|
1427
|
-
onBlockRender: function() {
|
1428
|
-
this.$$('.text-block').bind('click', function(){
|
1429
|
-
if($(this).html().length === 0){
|
1430
|
-
document.execCommand("insertUnorderedList",false,false);
|
1431
|
-
}
|
1432
|
-
});
|
1433
|
-
|
1434
|
-
// Put in a list
|
1435
|
-
if (_.isEmpty(this.data)) {
|
1436
|
-
this.$$('.text-block').focus().click();
|
1437
|
-
}
|
1438
|
-
|
1439
|
-
},
|
1440
|
-
|
1441
|
-
loadData: function(data){
|
1442
|
-
this.$$('.text-block').html("<ul>" + this.instance._toHTML(data.text, this.type) + "</ul>");
|
1443
|
-
},
|
1444
|
-
|
1445
|
-
toMarkdown: function(markdown) {
|
1446
|
-
return markdown.replace(/<\/li>/mg,"\n")
|
1447
|
-
.replace(/<\/?[^>]+(>|$)/g, "")
|
1448
|
-
.replace(/^(.+)$/mg," - $1");
|
1449
|
-
},
|
1450
|
-
|
1451
|
-
toHTML: function(html) {
|
1452
|
-
html = html.replace(/^ - (.+)$/mg,"<li>$1</li>")
|
1453
|
-
.replace(/\n/mg,"");
|
1454
|
-
|
1455
|
-
html = "<ul>" + html + "</ul>"
|
1456
|
-
|
1457
|
-
return html
|
1458
|
-
}
|
1459
|
-
|
1460
|
-
});
|
1461
|
-
var video_drop_template = '<p>Drop video link here</p><div class="input text"><label>or paste URL:</label><input type="text" class="paste-block"></div>';
|
1462
|
-
var video_regex = /http[s]?:\/\/(?:www.)?(?:(vimeo).com\/(.*))|(?:(youtu(?:be)?).(?:be|com)\/(?:watch\?v=)?([^&]*)(?:&(?:.))?)/;
|
1463
|
-
|
1464
|
-
SirTrevor.Blocks.Video = SirTrevor.Block.extend({
|
1465
|
-
|
1466
|
-
title: "Video",
|
1467
|
-
className: "video",
|
1468
|
-
dropEnabled: true,
|
1469
|
-
|
1470
|
-
dropzoneHTML: video_drop_template,
|
1471
|
-
|
1472
|
-
loadData: function(data){
|
1473
|
-
if(data.source == "youtube" || data.source == "youtu") {
|
1474
|
-
this.$editor.html("<iframe src=\""+window.location.protocol+"//www.youtube.com/embed/" + data.remote_id + "\" width=\"580\" height=\"320\" frameborder=\"0\" allowfullscreen></iframe>");
|
1475
|
-
} else if(data.source == "vimeo") {
|
1476
|
-
this.$editor.html("<iframe src=\""+window.location.protocol+"//player.vimeo.com/video/" + data.remote_id + "?title=0&byline=0\" width=\"580\" height=\"320\" frameborder=\"0\"></iframe>");
|
1477
|
-
}
|
1478
|
-
},
|
1479
|
-
|
1480
|
-
onContentPasted: function(event){
|
1481
|
-
// Content pasted. Delegate to the drop parse method
|
1482
|
-
var input = $(event.target),
|
1483
|
-
val = input.val();
|
1484
|
-
|
1485
|
-
// Pass this to the same handler as onDrop
|
1486
|
-
this.handleDropPaste(val);
|
1487
|
-
},
|
1488
|
-
|
1489
|
-
handleDropPaste: function(url){
|
1490
|
-
|
1491
|
-
if(_.isURI(url))
|
1492
|
-
{
|
1493
|
-
if (url.indexOf("youtu") != -1 || url.indexOf("vimeo") != -1) {
|
1494
|
-
|
1495
|
-
var data = {},
|
1496
|
-
videos = url.match(video_regex);
|
1497
|
-
|
1498
|
-
// Work out the source and extract ID
|
1499
|
-
if(videos[3] !== undefined) {
|
1500
|
-
data.source = videos[3];
|
1501
|
-
data.remote_id = videos[4];
|
1502
|
-
} else if (videos[1] !== undefined) {
|
1503
|
-
data.source = videos[1];
|
1504
|
-
data.remote_id = videos[2];
|
1505
|
-
}
|
1506
|
-
|
1507
|
-
if (data.source == "youtu") {
|
1508
|
-
data.source = "youtube";
|
1509
|
-
}
|
1510
|
-
|
1511
|
-
// Save the data
|
1512
|
-
this.setData(data);
|
1513
|
-
|
1514
|
-
// Render
|
1515
|
-
this._loadData();
|
1516
|
-
}
|
1517
|
-
}
|
1518
|
-
|
1519
|
-
},
|
1520
|
-
|
1521
|
-
onDrop: function(transferData){
|
1522
|
-
var url = transferData.getData('text/plain');
|
1523
|
-
this.handleDropPaste(url);
|
1524
|
-
}
|
1525
|
-
});
|
1526
|
-
/* Default Formatters */
|
1527
|
-
/* Our base formatters */
|
1528
|
-
|
1529
|
-
var Bold = SirTrevor.Formatter.extend({
|
1530
|
-
title: "B",
|
1531
|
-
className: "bold",
|
1532
|
-
cmd: "bold",
|
1533
|
-
keyCode: 66
|
1534
|
-
});
|
1535
|
-
|
1536
|
-
var Italic = SirTrevor.Formatter.extend({
|
1537
|
-
title: "I",
|
1538
|
-
className: "italic",
|
1539
|
-
cmd: "italic",
|
1540
|
-
keyCode: 73
|
1541
|
-
});
|
1542
|
-
|
1543
|
-
var Underline = SirTrevor.Formatter.extend({
|
1544
|
-
title: "U",
|
1545
|
-
className: "underline",
|
1546
|
-
cmd: "underline"
|
1547
|
-
});
|
1548
|
-
|
1549
|
-
var Link = SirTrevor.Formatter.extend({
|
1550
|
-
|
1551
|
-
title: "Link",
|
1552
|
-
className: "link",
|
1553
|
-
cmd: "CreateLink",
|
1554
|
-
|
1555
|
-
onClick: function() {
|
1556
|
-
|
1557
|
-
var link = prompt("Enter a link"),
|
1558
|
-
link_regex = /(ftp|http|https):\/\/./;
|
1559
|
-
|
1560
|
-
if(link && link.length > 0) {
|
1561
|
-
|
1562
|
-
if (!link_regex.test(link)) {
|
1563
|
-
link = "http://" + link;
|
1564
|
-
}
|
1565
|
-
|
1566
|
-
document.execCommand(this.cmd, false, link);
|
1567
|
-
}
|
1568
|
-
}
|
1569
|
-
});
|
1570
|
-
|
1571
|
-
var UnLink = SirTrevor.Formatter.extend({
|
1572
|
-
title: "Unlink",
|
1573
|
-
className: "link",
|
1574
|
-
cmd: "unlink"
|
1575
|
-
});
|
1576
|
-
|
1577
|
-
/*
|
1578
|
-
Create our formatters and add a static reference to them
|
1579
|
-
*/
|
1580
|
-
SirTrevor.Formatters.Bold = new Bold();
|
1581
|
-
SirTrevor.Formatters.Italic = new Italic();
|
1582
|
-
SirTrevor.Formatters.Link = new Link();
|
1583
|
-
SirTrevor.Formatters.Unlink = new UnLink();
|
1584
|
-
/* Marker */
|
1585
|
-
/*
|
1586
|
-
SirTrevor Marker
|
1587
|
-
--
|
1588
|
-
This is our toolbar. It's attached to a SirTrveor.Editor instance.
|
1589
|
-
*/
|
1590
|
-
|
1591
|
-
var Marker = SirTrevor.Marker = function(options, editorInstance){
|
1592
|
-
this.instance = editorInstance;
|
1593
|
-
this.options = _.extend({}, SirTrevor.DEFAULTS.marker, options || {});
|
1594
|
-
this._bindFunctions();
|
1595
|
-
};
|
1596
|
-
|
1597
|
-
_.extend(Marker.prototype, FunctionBind, {
|
1598
|
-
|
1599
|
-
bound: ["onButtonClick", "show", "hide", "onDrop"],
|
1600
|
-
|
1601
|
-
render: function() {
|
1602
|
-
|
1603
|
-
var marker = $('<div>', {
|
1604
|
-
'class': this.instance.baseCSS(this.options.baseCSSClass),
|
1605
|
-
html: '<p>' + this.options.addText + '</p>'
|
1606
|
-
});
|
1607
|
-
|
1608
|
-
var btns_cont = $("<div>", {
|
1609
|
-
'class': this.instance.baseCSS("buttons")
|
1610
|
-
});
|
1611
|
-
|
1612
|
-
marker.append(btns_cont);
|
1613
|
-
|
1614
|
-
// Bind to the wrapper
|
1615
|
-
this.instance.$wrapper.append(marker);
|
1616
|
-
|
1617
|
-
// Cache our elements for later use
|
1618
|
-
this.$el = marker;
|
1619
|
-
this.$btns = btns_cont;
|
1620
|
-
this.$p = this.$el.find('p');
|
1621
|
-
|
1622
|
-
// Add all of our buttons
|
1623
|
-
var blockName, block;
|
1624
|
-
|
1625
|
-
for (blockName in this.instance.blockTypes) {
|
1626
|
-
if (SirTrevor.Blocks.hasOwnProperty(blockName)) {
|
1627
|
-
block = SirTrevor.Blocks[blockName];
|
1628
|
-
if (block.prototype.toolbarEnabled) {
|
1629
|
-
this.$btns.append(
|
1630
|
-
$("<a>", {
|
1631
|
-
"href": "#",
|
1632
|
-
"class": this.instance.baseCSS(this.options.buttonClass) + " new-" + block.prototype.className,
|
1633
|
-
"data-type": blockName,
|
1634
|
-
"text": block.prototype.title,
|
1635
|
-
click: this.onButtonClick
|
1636
|
-
})
|
1637
|
-
);
|
1638
|
-
}
|
1639
|
-
}
|
1640
|
-
}
|
1641
|
-
|
1642
|
-
// Do we have any buttons?
|
1643
|
-
if(this.$btns.children().length === 0) this.$el.addClass('hidden');
|
1644
|
-
|
1645
|
-
// Bind our marker to the wrapper
|
1646
|
-
var throttled_show = _.throttle(this.show, 0),
|
1647
|
-
throttled_hide = _.throttle(this.hide, 0);
|
1648
|
-
|
1649
|
-
this.instance.$outer.bind('mouseover', throttled_show)
|
1650
|
-
.bind('mouseout', throttled_hide)
|
1651
|
-
.bind('dragover', throttled_show);
|
1652
|
-
|
1653
|
-
this.$el.bind('dragover', halt);
|
1654
|
-
|
1655
|
-
// Bind the drop function onto here
|
1656
|
-
this.instance.$outer.dropArea()
|
1657
|
-
.bind('dragleave', throttled_hide)
|
1658
|
-
.bind('drop', this.onDrop);
|
1659
|
-
|
1660
|
-
this.$el.addClass(this.instance.baseCSS("item-ready"));
|
1661
|
-
},
|
1662
|
-
|
1663
|
-
show: function(ev) {
|
1664
|
-
var target = $(ev.target),
|
1665
|
-
target_parent = target.parent();
|
1666
|
-
|
1667
|
-
if (target.is(this.$el) || target.is(this.$btns) || target_parent.is(this.$el) || target_parent.is(this.$btns)) {
|
1668
|
-
this.$el.addClass(this.instance.baseCSS("item-ready"));
|
1669
|
-
return;
|
1670
|
-
}
|
1671
|
-
|
1672
|
-
if(ev.type == 'drag' || ev.type == 'dragover') {
|
1673
|
-
this.$el.addClass('drop-zone');
|
1674
|
-
this.$p.text(this.options.dropText);
|
1675
|
-
this.$btns.hide();
|
1676
|
-
} else {
|
1677
|
-
this.$el.removeClass('drop-zone');
|
1678
|
-
this.$p.text(this.options.addText);
|
1679
|
-
this.$btns.show();
|
1680
|
-
}
|
1681
|
-
|
1682
|
-
// Check to see we're not over the formatting bar
|
1683
|
-
if (target.is(this.instance.formatBar.$el) || target_parent.is(this.instance.formatBar.$el)) {
|
1684
|
-
return this.hide();
|
1685
|
-
}
|
1686
|
-
|
1687
|
-
var mouse_enter = (ev) ? ev.originalEvent.pageY : 0;
|
1688
|
-
|
1689
|
-
// Do we have any sedit blocks?
|
1690
|
-
if (this.instance.blocks.length > 0) {
|
1691
|
-
|
1692
|
-
// Find the closest block to this position
|
1693
|
-
var closest_block = this.findClosestBlock(mouse_enter);
|
1694
|
-
|
1695
|
-
// Position it
|
1696
|
-
if (closest_block) {
|
1697
|
-
this.$el.insertBefore(closest_block);
|
1698
|
-
} else if(mouse_enter > 0) {
|
1699
|
-
this.$el.insertAfter(this.instance.cachedDomBlocks.last());
|
1700
|
-
} else {
|
1701
|
-
this.$el.insertBefore(this.instance.cachedDomBlocks.first());
|
1702
|
-
}
|
1703
|
-
}
|
1704
|
-
this.$el.addClass(this.instance.baseCSS("item-ready"));
|
1705
|
-
},
|
1706
|
-
|
1707
|
-
hide: function(ev){
|
1708
|
-
this.$el.removeClass(this.instance.baseCSS("item-ready"));
|
1709
|
-
},
|
1710
|
-
|
1711
|
-
onDrop: function(ev){
|
1712
|
-
ev.preventDefault();
|
1713
|
-
|
1714
|
-
var marker = this.$el,
|
1715
|
-
item_id = ev.originalEvent.dataTransfer.getData("text/plain"),
|
1716
|
-
block = $('#' + item_id);
|
1717
|
-
|
1718
|
-
if (!_.isUndefined(item_id) && !_.isEmpty(block) && block.attr('data-instance') == this.instance.ID) {
|
1719
|
-
marker.after(block);
|
1720
|
-
}
|
1721
|
-
},
|
1722
|
-
|
1723
|
-
findClosestBlock: function(mouse_enter) {
|
1724
|
-
var closest_block = false;
|
1725
|
-
|
1726
|
-
var blockIterator = function(block, index) {
|
1727
|
-
block = $(block);
|
1728
|
-
|
1729
|
-
var block_top = block.offset().top - 40,
|
1730
|
-
block_bottom = block.offset().top + block.outerHeight(true) - 40;
|
1731
|
-
|
1732
|
-
if(block_top <= mouse_enter && mouse_enter < block_bottom) {
|
1733
|
-
closest_block = block;
|
1734
|
-
}
|
1735
|
-
};
|
1736
|
-
_.each(this.instance.cachedDomBlocks, _.bind(blockIterator, this));
|
1737
|
-
|
1738
|
-
return closest_block;
|
1739
|
-
},
|
1740
|
-
|
1741
|
-
remove: function(){ this.$el.remove(); },
|
1742
|
-
|
1743
|
-
onButtonClick: function(ev){
|
1744
|
-
halt(ev);
|
1745
|
-
var button = $(ev.target);
|
1746
|
-
|
1747
|
-
if (button.hasClass('inactive')) {
|
1748
|
-
alert('You cannot create any more blocks of this type');
|
1749
|
-
return false;
|
1750
|
-
}
|
1751
|
-
|
1752
|
-
this.instance.createBlock(button.attr('data-type'), {});
|
1753
|
-
},
|
1754
|
-
|
1755
|
-
move: function(top) {
|
1756
|
-
this.$el.css({ top: top })
|
1757
|
-
.show()
|
1758
|
-
.addClass(this.instance.baseCSS("item-ready"));
|
1759
|
-
}
|
1760
|
-
});
|
1761
|
-
|
1762
|
-
|
1763
|
-
|
1764
|
-
/* FormatBar */
|
1765
|
-
/*
|
1766
|
-
Format Bar
|
1767
|
-
--
|
1768
|
-
Displayed on focus on a text area.
|
1769
|
-
Renders with all available options for the editor instance
|
1770
|
-
*/
|
1771
|
-
|
1772
|
-
var FormatBar = SirTrevor.FormatBar = function(options, editorInstance) {
|
1773
|
-
this.instance = editorInstance;
|
1774
|
-
this.options = _.extend({}, SirTrevor.DEFAULTS.formatBar, options || {});
|
1775
|
-
this.className = this.instance.baseCSS(this.options.baseCSSClass);
|
1776
|
-
this.clicked = false;
|
1777
|
-
this._bindFunctions();
|
1778
|
-
};
|
1779
|
-
|
1780
|
-
_.extend(FormatBar.prototype, FunctionBind, {
|
1781
|
-
|
1782
|
-
bound: ["onFormatButtonClick"],
|
1783
|
-
|
1784
|
-
render: function(){
|
1785
|
-
var bar = $("<div>", {
|
1786
|
-
"class": this.className
|
1787
|
-
});
|
1788
|
-
|
1789
|
-
this.instance.$wrapper.prepend(bar);
|
1790
|
-
this.$el = bar;
|
1791
|
-
|
1792
|
-
var formats = this.instance.formatters,
|
1793
|
-
formatName, format;
|
1794
|
-
|
1795
|
-
for (formatName in formats) {
|
1796
|
-
if (SirTrevor.Formatters.hasOwnProperty(formatName)) {
|
1797
|
-
format = SirTrevor.Formatters[formatName];
|
1798
|
-
$("<button>", {
|
1799
|
-
'class': this.instance.baseCSS("format-button"),
|
1800
|
-
'text': format.title,
|
1801
|
-
'data-type': formatName,
|
1802
|
-
'data-cmd': format.cmd,
|
1803
|
-
click: this.onFormatButtonClick
|
1804
|
-
}).appendTo(this.$el);
|
1805
|
-
}
|
1806
|
-
}
|
1807
|
-
|
1808
|
-
var throttled_scroll = _.throttle(_.bind(this.handleDocumentScroll, this), 150);
|
1809
|
-
$(document).bind('scroll', throttled_scroll);
|
1810
|
-
|
1811
|
-
if(this.$el.find('button').length === 0) this.$el.addClass('hidden');
|
1812
|
-
this.show();
|
1813
|
-
},
|
1814
|
-
|
1815
|
-
handleDocumentScroll: function() {
|
1816
|
-
var instance_height = this.instance.$outer.height(),
|
1817
|
-
instance_offset = this.instance.$outer.offset().top,
|
1818
|
-
viewport_top = $(document).scrollTop();
|
1819
|
-
|
1820
|
-
if (this.$el.hasClass('fixed')) {
|
1821
|
-
instance_offset = this.$el.offset().top;
|
1822
|
-
}
|
1823
|
-
|
1824
|
-
if ((viewport_top > 5) && viewport_top >= instance_offset) {
|
1825
|
-
this.$el.addClass('fixed')
|
1826
|
-
.css({ 'width': this.instance.$wrapper.width() });
|
1827
|
-
|
1828
|
-
this.instance.$wrapper.css({ 'padding-top': '104px' });
|
1829
|
-
} else {
|
1830
|
-
this.$el.removeClass('fixed').css({ 'width': '100%' });
|
1831
|
-
this.instance.$wrapper.css({ 'padding-top': '16px' });
|
1832
|
-
}
|
1833
|
-
},
|
1834
|
-
|
1835
|
-
hide: function() {
|
1836
|
-
this.$el.removeClass(this.instance.baseCSS('item-ready'));
|
1837
|
-
},
|
1838
|
-
|
1839
|
-
show: function() {
|
1840
|
-
this.$el.addClass(this.instance.baseCSS('item-ready'));
|
1841
|
-
},
|
1842
|
-
|
1843
|
-
remove: function(){ this.$el.remove(); },
|
1844
|
-
|
1845
|
-
onFormatButtonClick: function(ev){
|
1846
|
-
halt(ev);
|
1847
|
-
|
1848
|
-
var btn = $(ev.target),
|
1849
|
-
format = SirTrevor.Formatters[btn.attr('data-type')];
|
1850
|
-
|
1851
|
-
// Do we have a click function defined on this formatter?
|
1852
|
-
if(!_.isUndefined(format.onClick) && _.isFunction(format.onClick)) {
|
1853
|
-
format.onClick(); // Delegate
|
1854
|
-
} else {
|
1855
|
-
// Call default
|
1856
|
-
document.execCommand(btn.attr('data-cmd'), false, format.param);
|
1857
|
-
}
|
1858
|
-
// Make sure we still show the bar
|
1859
|
-
this.show();
|
1860
|
-
}
|
1861
|
-
|
1862
|
-
});
|
1863
|
-
/*
|
1864
|
-
Sir Trevor Editor
|
1865
|
-
--
|
1866
|
-
Represents one Sir Trevor editor instance (with multiple blocks)
|
1867
|
-
Each block references this instance.
|
1868
|
-
BlockTypes are global however.
|
1869
|
-
*/
|
1870
|
-
|
1871
|
-
var SirTrevorEditor = SirTrevor.Editor = function(options) {
|
1872
|
-
|
1873
|
-
SirTrevor.log("Init SirTrevor.Editor");
|
1874
|
-
|
1875
|
-
this.blockTypes = {};
|
1876
|
-
this.formatters = {};
|
1877
|
-
this.blockCounts = {}; // Cached block type counts
|
1878
|
-
this.blocks = []; // Block references
|
1879
|
-
this.errors = [];
|
1880
|
-
this.cachedDomBlocks = [];
|
1881
|
-
this.options = _.extend({}, SirTrevor.DEFAULTS, options || {});
|
1882
|
-
this.ID = _.uniqueId(this.options.baseCSSClass + "-");
|
1883
|
-
|
1884
|
-
if (this._ensureAndSetElements()) {
|
1885
|
-
|
1886
|
-
this.marker = new SirTrevor.Marker(this.options.marker, this);
|
1887
|
-
this.formatBar = new SirTrevor.FormatBar(this.options.formatBar, this);
|
1888
|
-
|
1889
|
-
if(!_.isUndefined(this.options.onEditorRender) && _.isFunction(this.options.onEditorRender)) {
|
1890
|
-
this.onEditorRender = this.options.onEditorRender;
|
1891
|
-
}
|
1892
|
-
|
1893
|
-
this._setRequired();
|
1894
|
-
this._setBlocksAndFormatters();
|
1895
|
-
this._bindFunctions();
|
1896
|
-
|
1897
|
-
this.store("create", this); // Make our storage
|
1898
|
-
this.build();
|
1899
|
-
|
1900
|
-
SirTrevor.instances.push(this); // Store a reference to this instance
|
1901
|
-
SirTrevor.bindFormSubmit(this.$form);
|
1902
|
-
}
|
1903
|
-
};
|
1904
|
-
|
1905
|
-
_.extend(SirTrevorEditor.prototype, FunctionBind, {
|
1906
|
-
|
1907
|
-
bound: ['onFormSubmit'],
|
1908
|
-
|
1909
|
-
initialize: function() {},
|
1910
|
-
|
1911
|
-
/*
|
1912
|
-
Build the Editor instance.
|
1913
|
-
Check to see if we've been passed JSON already, and if not try and create a default block.
|
1914
|
-
If we have JSON then we need to build all of our blocks from this.
|
1915
|
-
*/
|
1916
|
-
build: function() {
|
1917
|
-
this.$el.hide();
|
1918
|
-
|
1919
|
-
// Render marker & format bar
|
1920
|
-
this.marker.render();
|
1921
|
-
this.formatBar.render();
|
1922
|
-
|
1923
|
-
var store = this.store("read", this);
|
1924
|
-
|
1925
|
-
if (store.data.length === 0) {
|
1926
|
-
// Create a default instance
|
1927
|
-
this.createBlock(this.options.defaultType);
|
1928
|
-
} else {
|
1929
|
-
// We have data. Build our blocks from here.
|
1930
|
-
_.each(store.data, _.bind(function(block){
|
1931
|
-
SirTrevor.log('Creating: ', block);
|
1932
|
-
this.createBlock(block.type, block.data);
|
1933
|
-
}, this));
|
1934
|
-
}
|
1935
|
-
|
1936
|
-
this.$wrapper.addClass('sir-trevor-ready');
|
1937
|
-
|
1938
|
-
if(!_.isUndefined(this.onEditorRender)) {
|
1939
|
-
this.onEditorRender();
|
1940
|
-
}
|
1941
|
-
},
|
1942
|
-
|
1943
|
-
store: function(){
|
1944
|
-
return SirTrevor.editorStore.apply(this, arguments);
|
1945
|
-
},
|
1946
|
-
|
1947
|
-
/*
|
1948
|
-
Create an instance of a block from an available type.
|
1949
|
-
We have to check the number of blocks we're allowed to create before adding one and handle fails accordingly.
|
1950
|
-
A block will have a reference to an Editor instance & the parent BlockType.
|
1951
|
-
We also have to remember to store static counts for how many blocks we have, and keep a nice array of all the blocks available.
|
1952
|
-
*/
|
1953
|
-
createBlock: function(type, data) {
|
1954
|
-
|
1955
|
-
type = _.capitalize(type); // Proper case
|
1956
|
-
|
1957
|
-
if (this._blockTypeAvailable(type)) {
|
1958
|
-
|
1959
|
-
var blockType = SirTrevor.Blocks[type],
|
1960
|
-
currentBlockCount = (_.isUndefined(this.blockCounts[type])) ? 0 : this.blockCounts[type],
|
1961
|
-
totalBlockCounts = this.blocks.length,
|
1962
|
-
blockTypeLimit = this._getBlockTypeLimit(type);
|
1963
|
-
|
1964
|
-
// Can we have another one of these blocks?
|
1965
|
-
if ((blockTypeLimit !== 0 && currentBlockCount > blockTypeLimit) || this.options.blockLimit !== 0 && totalBlockCounts >= this.options.blockLimit) {
|
1966
|
-
SirTrevor.log("Block Limit reached for type " + type);
|
1967
|
-
return false;
|
1968
|
-
}
|
1969
|
-
|
1970
|
-
var block = new blockType(this, data || {});
|
1971
|
-
|
1972
|
-
if (_.isUndefined(this.blockCounts[type])) {
|
1973
|
-
this.blockCounts[type] = 0;
|
1974
|
-
}
|
1975
|
-
|
1976
|
-
this.blocks.push(block);
|
1977
|
-
currentBlockCount++;
|
1978
|
-
this.blockCounts[type] = currentBlockCount;
|
1979
|
-
|
1980
|
-
// Check to see if we can add any more blocks
|
1981
|
-
if (this.options.blockLimit !== 0 && this.blocks.length >= this.options.blockLimit) {
|
1982
|
-
this.marker.$el.addClass('hidden');
|
1983
|
-
}
|
1984
|
-
|
1985
|
-
if (blockTypeLimit !== 0 && currentBlockCount >= blockTypeLimit) {
|
1986
|
-
SirTrevor.log("Block Limit reached for type " + type + " setting state as inactive");
|
1987
|
-
this.marker.$el.find('[data-type="' + type + '"]')
|
1988
|
-
.addClass('inactive')
|
1989
|
-
.attr('title','You have reached the limit for this type of block');
|
1990
|
-
}
|
1991
|
-
|
1992
|
-
SirTrevor.publish("editor/block/createBlock");
|
1993
|
-
|
1994
|
-
SirTrevor.log("Block created of type " + type);
|
1995
|
-
this.cachedDomBlocks = this.$wrapper.find('.' + this.baseCSS("block"));
|
1996
|
-
} else {
|
1997
|
-
SirTrevor.log("Block type not available " + type);
|
1998
|
-
}
|
1999
|
-
},
|
2000
|
-
|
2001
|
-
removeBlock: function(block) {
|
2002
|
-
// Blocks exist purely on the dom.
|
2003
|
-
// Remove the block and decrement the blockCount
|
2004
|
-
block.remove();
|
2005
|
-
this.blockCounts[block.type] = this.blockCounts[block.type] - 1;
|
2006
|
-
|
2007
|
-
// Remove the block from our store
|
2008
|
-
this.blocks = _.reject(this.blocks, function(item){ return (item.blockID == block.blockID); });
|
2009
|
-
if(_.isUndefined(this.blocks)) this.blocks = [];
|
2010
|
-
|
2011
|
-
SirTrevor.publish("editor/block/removeBlock");
|
2012
|
-
this.cachedDomBlocks = this.$wrapper.find('.' + this.baseCSS("block"));
|
2013
|
-
|
2014
|
-
// Remove our inactive class if it's no longer relevant
|
2015
|
-
if(this._getBlockTypeLimit(block.type) > this.blockCounts[block.type]) {
|
2016
|
-
SirTrevor.log("Removing block limit for " + block.type);
|
2017
|
-
this.marker.$el.find('[data-type="' + block.type + '"]')
|
2018
|
-
.removeClass('inactive')
|
2019
|
-
.attr('title','Add a ' + block.type + ' block');
|
2020
|
-
}
|
2021
|
-
},
|
2022
|
-
|
2023
|
-
performValidations : function(_block, should_validate) {
|
2024
|
-
|
2025
|
-
var errors = 0;
|
2026
|
-
|
2027
|
-
if (!SirTrevor.SKIP_VALIDATION && should_validate) {
|
2028
|
-
if(!_block.validate()){
|
2029
|
-
// fail validations
|
2030
|
-
SirTrevor.log("Block " + _block.blockID + " failed validation");
|
2031
|
-
++errors;
|
2032
|
-
}
|
2033
|
-
} else {
|
2034
|
-
// not validating so clear validation warnings
|
2035
|
-
_block._beforeValidate();
|
2036
|
-
}
|
2037
|
-
|
2038
|
-
// success
|
2039
|
-
var store = _block.save();
|
2040
|
-
if(!_.isEmpty(store.data)) {
|
2041
|
-
SirTrevor.log("Adding data for block " + _block.blockID + " to block store");
|
2042
|
-
this.store("add", this, { data: store });
|
2043
|
-
}
|
2044
|
-
return errors;
|
2045
|
-
},
|
2046
|
-
|
2047
|
-
/*
|
2048
|
-
Handle a form submission of this Editor instance.
|
2049
|
-
Validate all of our blocks, and serialise all data onto the JSON objects
|
2050
|
-
*/
|
2051
|
-
onFormSubmit: function(should_validate) {
|
2052
|
-
|
2053
|
-
// if undefined or null or anything other than false - treat as true
|
2054
|
-
should_validate = (should_validate === false) ? false : true;
|
2055
|
-
|
2056
|
-
SirTrevor.log("Handling form submission for Editor " + this.ID);
|
2057
|
-
|
2058
|
-
var blockLength, block, result, errors = 0;
|
2059
|
-
|
2060
|
-
this.removeErrors();
|
2061
|
-
// Reset our store
|
2062
|
-
this.store("reset", this);
|
2063
|
-
|
2064
|
-
// Loop through blocks to validate
|
2065
|
-
var blockIterator = function(block,index) {
|
2066
|
-
// Find our block
|
2067
|
-
block = $(block);
|
2068
|
-
var _block = _.find(this.blocks, function(b){ return (b.blockID == block.attr('id')); });
|
2069
|
-
|
2070
|
-
if (!_.isUndefined(_block) || !_.isEmpty(_block) || typeof _block == SirTrevor.Block) {
|
2071
|
-
// Validate our block
|
2072
|
-
errors += this.performValidations(_block, should_validate);
|
2073
|
-
}
|
2074
|
-
|
2075
|
-
};
|
2076
|
-
_.each(this.$wrapper.find('.' + this.options.baseCSSClass + "-block"), _.bind(blockIterator, this));
|
2077
|
-
|
2078
|
-
// Validate against our required fields (if there are any)
|
2079
|
-
if (this.required && (!SirTrevor.SKIP_VALIDATION && should_validate)) {
|
2080
|
-
_.each(this.required, _.bind(function(type) {
|
2081
|
-
|
2082
|
-
if (this._blockTypeAvailable(type)) {
|
2083
|
-
// Valid block type to validate against
|
2084
|
-
if (_.isUndefined(this.blockCounts[type]) || this.blockCounts[type] === 0) {
|
2085
|
-
|
2086
|
-
this.errors.push({ text: "You must have a block of type " + type });
|
2087
|
-
|
2088
|
-
SirTrevor.log("Failed validation on required block type " + type);
|
2089
|
-
errors++;
|
2090
|
-
|
2091
|
-
} else {
|
2092
|
-
// We need to also validate that we have some data of this type too.
|
2093
|
-
// This is ugly, but necessary for proper validation on blocks that don't have required fields.
|
2094
|
-
var blocks = _.filter(this.blocks, function(b){ return (b.type == type && !_.isEmpty(b.getData())); });
|
2095
|
-
|
2096
|
-
if (blocks.length === 0) {
|
2097
|
-
this.errors.push({ text: "A required block type " + type + " is empty" });
|
2098
|
-
errors++;
|
2099
|
-
SirTrevor.log("A required block type " + type + " is empty");
|
2100
|
-
}
|
2101
|
-
|
2102
|
-
}
|
2103
|
-
}
|
2104
|
-
}, this));
|
2105
|
-
}
|
2106
|
-
|
2107
|
-
// Save it
|
2108
|
-
this.store("save", this);
|
2109
|
-
|
2110
|
-
if (errors > 0) this.renderErrors();
|
2111
|
-
|
2112
|
-
return errors;
|
2113
|
-
},
|
2114
|
-
|
2115
|
-
renderErrors: function() {
|
2116
|
-
if (this.errors.length > 0) {
|
2117
|
-
|
2118
|
-
if (_.isUndefined(this.$errors)) {
|
2119
|
-
this.$errors = $("<div>", {
|
2120
|
-
'class': this.baseCSS("errors"),
|
2121
|
-
html: "<p>You have the following errors: </p><ul></ul>"
|
2122
|
-
});
|
2123
|
-
this.$outer.prepend(this.$errors);
|
2124
|
-
}
|
2125
|
-
|
2126
|
-
var list = this.$errors.find('ul');
|
2127
|
-
|
2128
|
-
_.each(this.errors, _.bind(function(error) {
|
2129
|
-
list.append($("<li>", {
|
2130
|
-
'class': this.baseCSS("error-msg"),
|
2131
|
-
html: error.text
|
2132
|
-
}));
|
2133
|
-
}, this));
|
2134
|
-
|
2135
|
-
this.$errors.show();
|
2136
|
-
}
|
2137
|
-
},
|
2138
|
-
|
2139
|
-
removeErrors: function() {
|
2140
|
-
if (this.errors.length > 0) {
|
2141
|
-
// We have old errors to remove
|
2142
|
-
this.$errors.find('ul').html('');
|
2143
|
-
this.$errors.hide();
|
2144
|
-
this.errors = [];
|
2145
|
-
}
|
2146
|
-
},
|
2147
|
-
|
2148
|
-
/*
|
2149
|
-
Get Block Type Limit
|
2150
|
-
--
|
2151
|
-
returns the limit for this block, which can be set on a per Editor instance, or on a global blockType scope.
|
2152
|
-
*/
|
2153
|
-
_getBlockTypeLimit: function(t) {
|
2154
|
-
if (this._blockTypeAvailable(t)) {
|
2155
|
-
return (_.isUndefined(this.options.blockTypeLimits[t])) ? SirTrevor.Blocks[t].prototype.limit : this.options.blockTypeLimits[t];
|
2156
|
-
}
|
2157
|
-
return 0;
|
2158
|
-
},
|
2159
|
-
|
2160
|
-
/*
|
2161
|
-
Availability helper methods
|
2162
|
-
--
|
2163
|
-
Checks if the object exists within the instance of the Editor.
|
2164
|
-
*/
|
2165
|
-
_blockTypeAvailable: function(t) {
|
2166
|
-
return !_.isUndefined(this.blockTypes[t]);
|
2167
|
-
},
|
2168
|
-
|
2169
|
-
_formatterAvailable: function(f) {
|
2170
|
-
return !_.isUndefined(this.formatters[f]);
|
2171
|
-
},
|
2172
|
-
|
2173
|
-
_ensureAndSetElements: function() {
|
2174
|
-
if(_.isUndefined(this.options.el) || _.isEmpty(this.options.el)) {
|
2175
|
-
SirTrevor.log("You must provide an el");
|
2176
|
-
return false;
|
2177
|
-
}
|
2178
|
-
|
2179
|
-
this.$el = this.options.el;
|
2180
|
-
this.el = this.options.el[0];
|
2181
|
-
this.$form = this.$el.parents('form');
|
2182
|
-
|
2183
|
-
var blockCSSClass = this.baseCSS("blocks");
|
2184
|
-
|
2185
|
-
// Wrap our element in lots of containers *eww*
|
2186
|
-
this.$el.wrap($('<div>', { id: this.ID, 'class': this.options.baseCSSClass, dropzone: 'copy link move' }))
|
2187
|
-
.wrap($("<div>", { 'class': blockCSSClass }));
|
2188
|
-
|
2189
|
-
this.$outer = this.$form.find('#' + this.ID);
|
2190
|
-
this.$wrapper = this.$outer.find("." + blockCSSClass);
|
2191
|
-
|
2192
|
-
return true;
|
2193
|
-
},
|
2194
|
-
|
2195
|
-
|
2196
|
-
/*
|
2197
|
-
Set our blockTypes and formatters.
|
2198
|
-
These will either be set on a per Editor instance, or set on a global scope.
|
2199
|
-
*/
|
2200
|
-
_setBlocksAndFormatters: function() {
|
2201
|
-
this.blockTypes = flattern((_.isUndefined(this.options.blockTypes)) ? SirTrevor.Blocks : this.options.blockTypes);
|
2202
|
-
this.formatters = flattern((_.isUndefined(this.options.formatters)) ? SirTrevor.Formatters : this.options.formatters);
|
2203
|
-
},
|
2204
|
-
|
2205
|
-
/* Get our required blocks (if any) */
|
2206
|
-
_setRequired: function() {
|
2207
|
-
this.required = (_.isArray(this.options.required) && !_.isEmpty(this.options.required)) ? this.options.required : false;
|
2208
|
-
},
|
2209
|
-
|
2210
|
-
/*
|
2211
|
-
A very generic HTML -> Markdown parser
|
2212
|
-
Looks for available formatters / blockTypes toMarkdown methods and calls these if they exist.
|
2213
|
-
*/
|
2214
|
-
_toMarkdown: function(content, type) {
|
2215
|
-
|
2216
|
-
var markdown;
|
2217
|
-
|
2218
|
-
markdown = content.replace(/\n/mg,"")
|
2219
|
-
.replace(/<a.*?href=[""'](.*?)[""'].*?>(.*?)<\/a>/g,"[$2]($1)") // Hyperlinks
|
2220
|
-
.replace(/<\/?b>/g,"**")
|
2221
|
-
.replace(/<\/?STRONG>/g,"**") // Bold
|
2222
|
-
.replace(/<\/?i>/g,"_")
|
2223
|
-
.replace(/<\/?EM>/g,"_"); // Italic
|
2224
|
-
|
2225
|
-
// Use custom formatters toMarkdown functions (if any exist)
|
2226
|
-
var formatName, format;
|
2227
|
-
for(formatName in this.formatters) {
|
2228
|
-
if (SirTrevor.Formatters.hasOwnProperty(formatName)) {
|
2229
|
-
format = SirTrevor.Formatters[formatName];
|
2230
|
-
// Do we have a toMarkdown function?
|
2231
|
-
if (!_.isUndefined(format.toMarkdown) && _.isFunction(format.toMarkdown)) {
|
2232
|
-
markdown = format.toMarkdown(markdown);
|
2233
|
-
}
|
2234
|
-
}
|
2235
|
-
}
|
2236
|
-
|
2237
|
-
// Do our generic stripping out
|
2238
|
-
markdown = markdown.replace(/([^<>]+)(<div>)/g,"$1\n\n$2") // Divitis style line breaks (handle the first line)
|
2239
|
-
.replace(/(?:<div>)([^<>]+)(?:<div>)/g,"$1\n\n") // ^ (handle nested divs that start with content)
|
2240
|
-
.replace(/(?:<div>)(?:<br>)?([^<>]+)(?:<br>)?(?:<\/div>)/g,"$1\n\n") // ^ (handle content inside divs)
|
2241
|
-
.replace(/<\/p>/g,"\n\n\n\n") // P tags as line breaks
|
2242
|
-
.replace(/<(.)?br(.)?>/g,"\n\n") // Convert normal line breaks
|
2243
|
-
.replace(/ /g," ") // Strip white-space entities
|
2244
|
-
.replace(/</g,"<").replace(/>/g,">"); // Encoding
|
2245
|
-
|
2246
|
-
|
2247
|
-
// Use custom block toMarkdown functions (if any exist)
|
2248
|
-
var block;
|
2249
|
-
if (SirTrevor.Blocks.hasOwnProperty(type)) {
|
2250
|
-
block = SirTrevor.Blocks[type];
|
2251
|
-
// Do we have a toMarkdown function?
|
2252
|
-
if (!_.isUndefined(block.prototype.toMarkdown) && _.isFunction(block.prototype.toMarkdown)) {
|
2253
|
-
markdown = block.prototype.toMarkdown(markdown);
|
2254
|
-
}
|
2255
|
-
}
|
2256
|
-
|
2257
|
-
// Strip remaining HTML
|
2258
|
-
markdown = markdown.replace(/<\/?[^>]+(>|$)/g, "");
|
2259
|
-
|
2260
|
-
return markdown;
|
2261
|
-
},
|
2262
|
-
|
2263
|
-
/*
|
2264
|
-
A very generic Markdown -> HTML parser
|
2265
|
-
Looks for available formatters / blockTypes toMarkdown methods and calls these if they exist.
|
2266
|
-
*/
|
2267
|
-
_toHTML: function(markdown, type) {
|
2268
|
-
var html = markdown;
|
2269
|
-
|
2270
|
-
// Use custom formatters toHTML functions (if any exist)
|
2271
|
-
var formatName, format;
|
2272
|
-
for(formatName in this.formatters) {
|
2273
|
-
if (SirTrevor.Formatters.hasOwnProperty(formatName)) {
|
2274
|
-
format = SirTrevor.Formatters[formatName];
|
2275
|
-
// Do we have a toHTML function?
|
2276
|
-
if (!_.isUndefined(format.toHTML) && _.isFunction(format.toHTML)) {
|
2277
|
-
html = format.toHTML(html);
|
2278
|
-
}
|
2279
|
-
}
|
2280
|
-
}
|
2281
|
-
|
2282
|
-
// Use custom block toHTML functions (if any exist)
|
2283
|
-
var block;
|
2284
|
-
if (SirTrevor.Blocks.hasOwnProperty(type)) {
|
2285
|
-
|
2286
|
-
block = SirTrevor.Blocks[type];
|
2287
|
-
// Do we have a toHTML function?
|
2288
|
-
if (!_.isUndefined(block.prototype.toHTML) && _.isFunction(block.prototype.toHTML)) {
|
2289
|
-
html = block.prototype.toHTML(html);
|
2290
|
-
}
|
2291
|
-
}
|
2292
|
-
|
2293
|
-
html = html.replace(/^\> (.+)$/mg,"$1") // Blockquotes
|
2294
|
-
.replace(/\n\n/g,"<br>") // Give me some <br>s
|
2295
|
-
.replace(/\[([^\]]+)\]\(([^\)]+)\)/g,"<a href='$2'>$1</a>") // Links
|
2296
|
-
.replace(/(?:_)([^*|_(http)]+)(?:_)/g,"<i>$1</i>") // Italic, avoid italicizing two links with underscores next to each other
|
2297
|
-
.replace(/(?:\*\*)([^*|_]+)(?:\*\*)/g,"<b>$1</b>"); // Bold
|
2298
|
-
|
2299
|
-
return html;
|
2300
|
-
},
|
2301
|
-
|
2302
|
-
baseCSS: function(additional) {
|
2303
|
-
return this.options.baseCSSClass + "-" + additional;
|
2304
|
-
}
|
2305
|
-
});
|
2306
|
-
|
2307
|
-
|
2308
|
-
/* We need a form handler here to handle all the form submits */
|
2309
|
-
|
2310
|
-
SirTrevor.bindFormSubmit = function(form) {
|
2311
|
-
if (!formBound) {
|
2312
|
-
SirTrevor.submittable();
|
2313
|
-
form.bind('submit', this.onFormSubmit);
|
2314
|
-
formBound = true;
|
2315
|
-
}
|
2316
|
-
};
|
2317
|
-
|
2318
|
-
SirTrevor.onBeforeSubmit = function(should_validate) {
|
2319
|
-
// Loop through all of our instances and do our form submits on them
|
2320
|
-
var errors = 0;
|
2321
|
-
_.each(SirTrevor.instances, function(inst, i) {
|
2322
|
-
errors += inst.onFormSubmit(should_validate);
|
2323
|
-
});
|
2324
|
-
SirTrevor.log("Total errors: " + errors);
|
2325
|
-
|
2326
|
-
return errors;
|
2327
|
-
};
|
2328
|
-
|
2329
|
-
SirTrevor.onFormSubmit = function(ev) {
|
2330
|
-
var errors = SirTrevor.onBeforeSubmit();
|
2331
|
-
|
2332
|
-
if(errors > 0) {
|
2333
|
-
SirTrevor.publish("onError");
|
2334
|
-
ev.preventDefault();
|
2335
|
-
}
|
2336
|
-
};
|
2337
|
-
|
2338
|
-
SirTrevor.runOnAllInstances = function(method) {
|
2339
|
-
if (_.has(SirTrevor.Editor.prototype, method)) {
|
2340
|
-
// augment the arguments pseudo array and pass on to invoke()
|
2341
|
-
// this allows us to pass arguments on to the target methods
|
2342
|
-
[].unshift.call(arguments, SirTrevor.instances);
|
2343
|
-
_.invoke.apply(_, arguments);
|
2344
|
-
} else {
|
2345
|
-
SirTrevor.log("method doesn't exist");
|
2346
|
-
}
|
2347
|
-
};
|
2348
|
-
|
2349
|
-
}(jQuery, _));
|