sir-trevor-rails 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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, _));