sir-trevor-rails 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +58 -62
  3. data/README.md +12 -73
  4. data/app/views/sir-trevor/blocks/_list_block.html.erb +3 -0
  5. data/app/views/sir-trevor/blocks/_quote_block.html.erb +3 -3
  6. data/app/views/sir-trevor/blocks/_text_block.html.erb +2 -2
  7. data/app/views/sir-trevor/blocks/_tweet_block.html.erb +4 -4
  8. data/app/views/sir-trevor/blocks/_video_block.html.erb +5 -5
  9. data/config/initializers/validators.rb +4 -2
  10. data/lib/generators/sir_trevor/block/templates/_block.html.erb +2 -2
  11. data/lib/sir-trevor-rails.rb +7 -3
  12. data/lib/sir-trevor/helpers/form_builder.rb +2 -2
  13. data/lib/sir-trevor/helpers/form_helper.rb +12 -6
  14. data/lib/sir-trevor/helpers/view_helper.rb +41 -40
  15. data/lib/sir-trevor/version.rb +1 -1
  16. data/sir-trevor-rails.gemspec +6 -2
  17. metadata +35 -34
  18. data/Rakefile +0 -10
  19. data/app/assets/images/sir-trevor/icons/block_editor_blockquote.png +0 -0
  20. data/app/assets/images/sir-trevor/icons/block_editor_embed.png +0 -0
  21. data/app/assets/images/sir-trevor/icons/block_editor_image.png +0 -0
  22. data/app/assets/images/sir-trevor/icons/block_editor_list.png +0 -0
  23. data/app/assets/images/sir-trevor/icons/block_editor_text.png +0 -0
  24. data/app/assets/images/sir-trevor/icons/block_editor_tweet.png +0 -0
  25. data/app/assets/images/sir-trevor/icons/block_editor_video.png +0 -0
  26. data/app/assets/images/sir-trevor/icons/close.gif +0 -0
  27. data/app/assets/images/sir-trevor/icons/handle.gif +0 -0
  28. data/app/assets/images/sir-trevor/icons/new_image.png +0 -0
  29. data/app/assets/images/sir-trevor/icons/new_tweet.png +0 -0
  30. data/app/assets/images/sir-trevor/icons/new_video.png +0 -0
  31. data/app/assets/images/sir-trevor/icons/quote.png +0 -0
  32. data/app/assets/images/sir-trevor/placeholders/placeholder.jpg +0 -0
  33. data/app/assets/images/sir-trevor/placeholders/post_placeholder.jpg +0 -0
  34. data/app/assets/images/sir-trevor/placeholders/thumbnail_placeholder.jpg +0 -0
  35. data/app/assets/javascript/sir-trevor.js +0 -2
  36. data/app/assets/javascript/sir-trevor/libs/underscore.js +0 -32
  37. data/app/assets/javascript/sir-trevor/sir-trevor.js +0 -2349
  38. data/app/assets/stylesheets/sir-trevor.css +0 -4
  39. data/app/assets/stylesheets/sir-trevor/icons.css.erb +0 -47
  40. data/app/assets/stylesheets/sir-trevor/sir-trevor.css +0 -439
  41. data/app/views/sir-trevor/blocks/_gallery_block.html.erb +0 -6
  42. data/app/views/sir-trevor/blocks/_ul_block.html.erb +0 -3
  43. data/lib/generators/sir_trevor/block/block_generator.rb +0 -37
  44. data/lib/generators/sir_trevor/block/templates/_block.css.erb +0 -7
  45. data/lib/generators/sir_trevor/block/templates/_block.js +0 -122
  46. data/lib/generators/sir_trevor/block/templates/_block.png +0 -0
@@ -1,3 +1,3 @@
1
1
  module SirTrevor
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -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", "~> 2.0.1"
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.3.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: 2.0.1
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: 2.0.1
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.3.0
162
+ summary: sir-trevor-rails-0.4.0
162
163
  test_files: []
data/Rakefile DELETED
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env rake
2
- # encoding: UTF-8
3
-
4
- require 'net/http'
5
-
6
- task :default => [:"get-latest-files"]
7
-
8
- desc "Updates the version of sir-trevor to the latest version in the Github repository"
9
- task :"get-latest-files" do
10
- end
@@ -1,2 +0,0 @@
1
- //= require_directory ./sir-trevor/libs
2
- //= require ./sir-trevor/sir-trevor
@@ -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,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")};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(/&nbsp;/g," ") // Strip white-space entities
2244
- .replace(/&lt;/g,"<").replace(/&gt;/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, _));