adminpanel 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (175) hide show
  1. data/.rspec +2 -2
  2. data/.ruby-version +1 -0
  3. data/.travis.yml +8 -8
  4. data/Gemfile +26 -24
  5. data/README.md +57 -57
  6. data/adminpanel.gemspec +41 -39
  7. data/app/assets/fonts/fontawesome-webfont.svg +254 -254
  8. data/app/assets/javascripts/adminpanel/bootstrap-datepicker.js +1159 -1159
  9. data/app/assets/javascripts/adminpanel/bootstrap-timepicker.js +803 -803
  10. data/app/assets/javascripts/adminpanel/bootstrap.js +2170 -2170
  11. data/app/assets/javascripts/adminpanel/{products.js → images_form.js} +13 -13
  12. data/app/assets/javascripts/adminpanel/imagesloaded.js +12 -12
  13. data/app/assets/javascripts/adminpanel/jquery.dataTables.min.js +155 -155
  14. data/app/assets/javascripts/adminpanel/jquery.facybox.js +395 -395
  15. data/app/assets/javascripts/adminpanel/jquery.masonry.min.js +9 -9
  16. data/app/assets/javascripts/adminpanel/jquery.slimscroll.min.js +13 -13
  17. data/app/assets/javascripts/adminpanel/medium-editor.js +702 -702
  18. data/app/assets/javascripts/adminpanel/realm.js +87 -87
  19. data/app/assets/javascripts/adminpanel/tables.js +126 -126
  20. data/app/assets/javascripts/application-admin.js +15 -15
  21. data/app/assets/stylesheets/adminpanel/_clearfix.css.scss +7 -7
  22. data/app/assets/stylesheets/adminpanel/alertify.css +241 -241
  23. data/app/assets/stylesheets/adminpanel/bootstrap.css +6103 -6103
  24. data/app/assets/stylesheets/adminpanel/colorpicker.css +6 -6
  25. data/app/assets/stylesheets/adminpanel/datepicker.css +9 -9
  26. data/app/assets/stylesheets/adminpanel/elfinder.min.css +59 -59
  27. data/app/assets/stylesheets/adminpanel/facybox.css +146 -146
  28. data/app/assets/stylesheets/adminpanel/font-awesome.min.css +33 -33
  29. data/app/assets/stylesheets/adminpanel/fullcalendar.css +618 -618
  30. data/app/assets/stylesheets/adminpanel/fullcalendar.print.css +61 -61
  31. data/app/assets/stylesheets/adminpanel/select2.css +524 -524
  32. data/app/assets/stylesheets/adminpanel/theme.css +1571 -1571
  33. data/app/assets/stylesheets/adminpanel/timepicker.css +82 -82
  34. data/app/assets/stylesheets/application-admin.css +13 -13
  35. data/app/controllers/adminpanel/application_controller.rb +32 -17
  36. data/app/controllers/adminpanel/galleries_controller.rb +80 -80
  37. data/app/controllers/adminpanel/sections_controller.rb +45 -45
  38. data/app/controllers/adminpanel/sessions_controller.rb +26 -26
  39. data/app/controllers/adminpanel/users_controller.rb +84 -84
  40. data/app/helpers/adminpanel/application_helper.rb +41 -51
  41. data/app/helpers/adminpanel/breadcrumbs_helper.rb +16 -0
  42. data/app/helpers/adminpanel/custom_form_builder.rb +248 -0
  43. data/app/helpers/adminpanel/images_helper.rb +9 -9
  44. data/app/helpers/adminpanel/rest_actions_helper.rb +47 -0
  45. data/app/helpers/adminpanel/router_helper.rb +33 -0
  46. data/app/helpers/adminpanel/sessions_helper.rb +25 -25
  47. data/app/models/adminpanel/gallery.rb +60 -60
  48. data/app/models/adminpanel/image.rb +14 -14
  49. data/app/models/adminpanel/section.rb +22 -22
  50. data/app/models/adminpanel/user.rb +35 -35
  51. data/app/uploaders/adminpanel/gallery_uploader.rb +55 -55
  52. data/app/uploaders/adminpanel/image_uploader.rb +57 -57
  53. data/app/views/adminpanel/galleries/_galleries_table.html.erb +14 -14
  54. data/app/views/adminpanel/galleries/create.html.erb +2 -2
  55. data/app/views/adminpanel/galleries/delete.html.erb +2 -2
  56. data/app/views/adminpanel/galleries/edit.html.erb +25 -25
  57. data/app/views/adminpanel/galleries/index.html.erb +51 -51
  58. data/app/views/adminpanel/galleries/new.html.erb +17 -17
  59. data/app/views/adminpanel/galleries/show.html.erb +17 -17
  60. data/app/views/adminpanel/galleries/update.html.erb +2 -2
  61. data/app/views/adminpanel/sections/_image_fields.html.erb +23 -23
  62. data/app/views/adminpanel/sections/_sections_table.html.erb +16 -16
  63. data/app/views/adminpanel/sections/create.html.erb +2 -2
  64. data/app/views/adminpanel/sections/destroy.html.erb +2 -2
  65. data/app/views/adminpanel/sections/edit.html.erb +41 -41
  66. data/app/views/adminpanel/sections/index.html.erb +44 -44
  67. data/app/views/adminpanel/sections/new.html.erb +26 -26
  68. data/app/views/adminpanel/sections/show.html.erb +30 -30
  69. data/app/views/adminpanel/sections/update.html.erb +2 -2
  70. data/app/views/adminpanel/sessions/new.html.erb +25 -25
  71. data/app/views/adminpanel/users/_user_form.html.erb +20 -20
  72. data/app/views/adminpanel/users/edit.html.erb +5 -5
  73. data/app/views/adminpanel/users/index.html.erb +49 -49
  74. data/app/views/adminpanel/users/new.html.erb +5 -5
  75. data/app/views/adminpanel/users/show.html.erb +20 -20
  76. data/app/views/layouts/_shim.html.erb +3 -3
  77. data/app/views/layouts/_side_menu.html.erb +49 -43
  78. data/app/views/layouts/_top_bar.html.erb +43 -43
  79. data/app/views/layouts/admin-login.html.erb +28 -28
  80. data/app/views/layouts/admin.html.erb +41 -39
  81. data/app/views/shared/_breadcrumb.html.erb +6 -6
  82. data/app/views/shared/_error_messages.html.erb +16 -16
  83. data/app/views/shared/_form_fields.html.erb +25 -0
  84. data/app/views/{adminpanel/products → shared}/_image_fields.html.erb +23 -23
  85. data/app/views/shared/_init_editor.html.erb +24 -0
  86. data/app/views/shared/edit.html.erb +28 -0
  87. data/app/views/shared/index.html.erb +94 -0
  88. data/app/views/shared/new.html.erb +28 -0
  89. data/app/views/shared/show.html.erb +63 -0
  90. data/config/locales/en.yml +5 -5
  91. data/config/locales/es.yml +127 -129
  92. data/config/routes.rb +24 -16
  93. data/lib/adminpanel.rb +9 -7
  94. data/lib/adminpanel/active_record_extension.rb +38 -0
  95. data/lib/adminpanel/engine.rb +5 -5
  96. data/lib/adminpanel/version.rb +3 -3
  97. data/lib/generators/adminpanel/initialize/initialize_generator.rb +17 -0
  98. data/lib/generators/adminpanel/{install/templates/migrations → initialize/templates}/create_adminpanel_tables.rb +45 -64
  99. data/lib/generators/adminpanel/resource/resource_generator.rb +105 -0
  100. data/lib/generators/adminpanel/resource/templates/controller.rb +4 -0
  101. data/lib/generators/adminpanel/resource/templates/migration.rb +9 -0
  102. data/lib/generators/adminpanel/resource/templates/resource.rb +19 -0
  103. data/spec/dummy/.gitignore +17 -17
  104. data/spec/dummy/README.rdoc +261 -261
  105. data/spec/dummy/Rakefile +7 -7
  106. data/{app/views/adminpanel/categories/show.html.erb → spec/dummy/app/adminpanel/products.rb} +0 -0
  107. data/spec/dummy/app/assets/javascripts/application.js +13 -13
  108. data/spec/dummy/app/assets/stylesheets/application.css +13 -13
  109. data/spec/dummy/app/controllers/adminpanel/.gitkeep +0 -0
  110. data/spec/dummy/app/controllers/application_controller.rb +3 -11
  111. data/spec/dummy/app/helpers/application_helper.rb +2 -2
  112. data/spec/dummy/app/views/layouts/application.html.erb +14 -14
  113. data/spec/dummy/config.ru +4 -4
  114. data/spec/dummy/config/application.rb +62 -62
  115. data/spec/dummy/config/boot.rb +9 -9
  116. data/spec/dummy/config/carrierwve.rb +5 -5
  117. data/spec/dummy/config/database.yml +24 -24
  118. data/spec/dummy/config/environment.rb +5 -5
  119. data/spec/dummy/config/environments/development.rb +41 -41
  120. data/spec/dummy/config/environments/production.rb +72 -72
  121. data/spec/dummy/config/environments/test.rb +41 -41
  122. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -7
  123. data/spec/dummy/config/initializers/inflections.rb +15 -15
  124. data/spec/dummy/config/initializers/mime_types.rb +5 -5
  125. data/spec/dummy/config/initializers/secret_token.rb +8 -8
  126. data/spec/dummy/config/initializers/session_store.rb +8 -8
  127. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -14
  128. data/spec/dummy/config/locales/en.yml +5 -5
  129. data/spec/dummy/config/routes.rb +4 -4
  130. data/spec/dummy/db/schema.rb +15 -15
  131. data/spec/dummy/public/404.html +26 -26
  132. data/spec/dummy/public/422.html +26 -26
  133. data/spec/dummy/public/500.html +25 -25
  134. data/spec/dummy/public/uploads/gallery/file/1/hipster.jpg +0 -0
  135. data/spec/dummy/public/uploads/gallery/file/1/thumb_hipster.jpg +0 -0
  136. data/spec/dummy/script/rails +6 -6
  137. data/spec/features/authentication_pages_spec.rb +43 -43
  138. data/spec/features/galleries_pages_spec.rb +124 -124
  139. data/spec/features/section_pages_spec.rb +37 -37
  140. data/spec/features/user_pages_spec.rb +48 -48
  141. data/spec/generators/initialize_spec.rb +9 -0
  142. data/spec/generators/resource_spec.rb +122 -0
  143. data/spec/models/gallery_spec.rb +21 -21
  144. data/spec/models/section_spec.rb +66 -66
  145. data/spec/models/user_spec.rb +105 -105
  146. data/spec/spec_helper.rb +32 -31
  147. data/spec/support/define_factory_models.rb +25 -36
  148. data/spec/support/helper_methods.rb +26 -26
  149. data/spec/support/submit_forms_without_button.rb +16 -16
  150. data/spec/support/test_database.rb +45 -58
  151. data/spec/uploaders/gallery_uploader_spec.rb +36 -30
  152. data/spec/uploaders/image_uploader_spec.rb +30 -29
  153. metadata +208 -64
  154. checksums.yaml +0 -7
  155. data/app/assets/javascripts/adminpanel/init_editor.js +0 -28
  156. data/app/controllers/adminpanel/categories_controller.rb +0 -41
  157. data/app/controllers/adminpanel/products_controller.rb +0 -88
  158. data/app/helpers/custom_form_builder.rb +0 -219
  159. data/app/models/adminpanel/category.rb +0 -7
  160. data/app/models/adminpanel/product.rb +0 -24
  161. data/app/views/adminpanel/categories/edit.html.erb +0 -18
  162. data/app/views/adminpanel/categories/index.html.erb +0 -55
  163. data/app/views/adminpanel/categories/new.html.erb +0 -18
  164. data/app/views/adminpanel/products/.DS_Store +0 -0
  165. data/app/views/adminpanel/products/_product_form.html.erb +0 -31
  166. data/app/views/adminpanel/products/edit.html.erb +0 -10
  167. data/app/views/adminpanel/products/index.html.erb +0 -51
  168. data/app/views/adminpanel/products/new.html.erb +0 -10
  169. data/app/views/adminpanel/products/show.html.erb +0 -61
  170. data/config/database.yml +0 -24
  171. data/lib/generators/adminpanel/install/install_generator.rb +0 -12
  172. data/spec/features/categories_pages_spec.rb +0 -44
  173. data/spec/features/product_pages_spec.rb +0 -64
  174. data/spec/models/category_spec.rb +0 -58
  175. data/spec/models/product_spec.rb +0 -51
@@ -1,10 +1,10 @@
1
- /**
2
- * jQuery Masonry v2.1.06
3
- * A dynamic layout plugin for jQuery
4
- * The flip-side of CSS Floats
5
- * http://masonry.desandro.com
6
- *
7
- * Licensed under the MIT license.
8
- * Copyright 2012 David DeSandro
9
- */
1
+ /**
2
+ * jQuery Masonry v2.1.06
3
+ * A dynamic layout plugin for jQuery
4
+ * The flip-side of CSS Floats
5
+ * http://masonry.desandro.com
6
+ *
7
+ * Licensed under the MIT license.
8
+ * Copyright 2012 David DeSandro
9
+ */
10
10
  (function(a,b,c){"use strict";var d=b.event,e;d.special.smartresize={setup:function(){b(this).bind("resize",d.special.smartresize.handler)},teardown:function(){b(this).unbind("resize",d.special.smartresize.handler)},handler:function(a,c){var d=this,f=arguments;a.type="smartresize",e&&clearTimeout(e),e=setTimeout(function(){b.event.handle.apply(d,f)},c==="execAsap"?0:100)}},b.fn.smartresize=function(a){return a?this.bind("smartresize",a):this.trigger("smartresize",["execAsap"])},b.Mason=function(a,c){this.element=b(c),this._create(a),this._init()},b.Mason.settings={isResizable:!0,isAnimated:!1,animationOptions:{queue:!1,duration:500},gutterWidth:0,isRTL:!1,isFitWidth:!1,containerStyle:{position:"relative"}},b.Mason.prototype={_filterFindBricks:function(a){var b=this.options.itemSelector;return b?a.filter(b).add(a.find(b)):a},_getBricks:function(a){var b=this._filterFindBricks(a).css({position:"absolute"}).addClass("masonry-brick");return b},_create:function(c){this.options=b.extend(!0,{},b.Mason.settings,c),this.styleQueue=[];var d=this.element[0].style;this.originalStyle={height:d.height||""};var e=this.options.containerStyle;for(var f in e)this.originalStyle[f]=d[f]||"";this.element.css(e),this.horizontalDirection=this.options.isRTL?"right":"left";var g=this.element.css("padding-"+this.horizontalDirection),h=this.element.css("padding-top");this.offset={x:g?parseInt(g,10):0,y:h?parseInt(h,10):0},this.isFluid=this.options.columnWidth&&typeof this.options.columnWidth=="function";var i=this;setTimeout(function(){i.element.addClass("masonry")},0),this.options.isResizable&&b(a).bind("smartresize.masonry",function(){i.resize()}),this.reloadItems()},_init:function(a){this._getColumns(),this._reLayout(a)},option:function(a,c){b.isPlainObject(a)&&(this.options=b.extend(!0,this.options,a))},layout:function(a,b){for(var c=0,d=a.length;c<d;c++)this._placeBrick(a[c]);var e={};e.height=Math.max.apply(Math,this.colYs);if(this.options.isFitWidth){var f=0;c=this.cols;while(--c){if(this.colYs[c]!==0)break;f++}e.width=(this.cols-f)*this.columnWidth-this.options.gutterWidth}this.styleQueue.push({$el:this.element,style:e});var g=this.isLaidOut?this.options.isAnimated?"animate":"css":"css",h=this.options.animationOptions,i;for(c=0,d=this.styleQueue.length;c<d;c++)i=this.styleQueue[c],i.$el[g](i.style,h);this.styleQueue=[],b&&b.call(a),this.isLaidOut=!0},_getColumns:function(){var a=this.options.isFitWidth?this.element.parent():this.element,b=a.width();this.columnWidth=this.isFluid?this.options.columnWidth(b):this.options.columnWidth||this.$bricks.outerWidth(!0)||b,this.columnWidth+=this.options.gutterWidth,this.cols=Math.floor((b+this.options.gutterWidth)/this.columnWidth),this.cols=Math.max(this.cols,1)},_placeBrick:function(a){var c=b(a),d,e,f,g,h;d=Math.ceil(c.outerWidth(!0)/this.columnWidth),d=Math.min(d,this.cols);if(d===1)f=this.colYs;else{e=this.cols+1-d,f=[];for(h=0;h<e;h++)g=this.colYs.slice(h,h+d),f[h]=Math.max.apply(Math,g)}var i=Math.min.apply(Math,f),j=0;for(var k=0,l=f.length;k<l;k++)if(f[k]===i){j=k;break}var m={top:i+this.offset.y};m[this.horizontalDirection]=this.columnWidth*j+this.offset.x,this.styleQueue.push({$el:c,style:m});var n=i+c.outerHeight(!0),o=this.cols+1-l;for(k=0;k<o;k++)this.colYs[j+k]=n},resize:function(){var a=this.cols;this._getColumns(),(this.isFluid||this.cols!==a)&&this._reLayout()},_reLayout:function(a){var b=this.cols;this.colYs=[];while(b--)this.colYs.push(0);this.layout(this.$bricks,a)},reloadItems:function(){this.$bricks=this._getBricks(this.element.children())},reload:function(a){this.reloadItems(),this._init(a)},appended:function(a,b,c){if(b){this._filterFindBricks(a).css({top:this.element.height()});var d=this;setTimeout(function(){d._appended(a,c)},1)}else this._appended(a,c)},_appended:function(a,b){var c=this._getBricks(a);this.$bricks=this.$bricks.add(c),this.layout(c,b)},remove:function(a){this.$bricks=this.$bricks.not(a),a.remove()},destroy:function(){this.$bricks.removeClass("masonry-brick").each(function(){this.style.position="",this.style.top="",this.style.left=""});var c=this.element[0].style;for(var d in this.originalStyle)c[d]=this.originalStyle[d];this.element.unbind(".masonry").removeClass("masonry").removeData("masonry"),b(a).unbind(".masonry")}},b.fn.imagesLoaded=function(a){function h(){a.call(c,d)}function i(a){var c=a.target;c.src!==f&&b.inArray(c,g)===-1&&(g.push(c),--e<=0&&(setTimeout(h),d.unbind(".imagesLoaded",i)))}var c=this,d=c.find("img").add(c.filter("img")),e=d.length,f="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==",g=[];return e||h(),d.bind("load.imagesLoaded error.imagesLoaded",i).each(function(){var a=this.src;this.src=f,this.src=a}),c};var f=function(b){a.console&&a.console.error(b)};b.fn.masonry=function(a){if(typeof a=="string"){var c=Array.prototype.slice.call(arguments,1);this.each(function(){var d=b.data(this,"masonry");if(!d){f("cannot call methods on masonry prior to initialization; attempted to call method '"+a+"'");return}if(!b.isFunction(d[a])||a.charAt(0)==="_"){f("no such method '"+a+"' for masonry instance");return}d[a].apply(d,c)})}else this.each(function(){var c=b.data(this,"masonry");c?(c.option(a||{}),c._init()):b.data(this,"masonry",new b.Mason(a,this))});return this}})(window,jQuery);
@@ -1,14 +1,14 @@
1
- /*! Copyright (c) 2011 Piotr Rochala (http://rocha.la)
2
- * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
3
- * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
4
- *
5
- * Version: 1.0.5
6
- *
7
- */
8
- (function(d){jQuery.fn.extend({slimScroll:function(m){var a=d.extend({wheelStep:20,width:"auto",height:"250px",size:"7px",color:"#000",position:"right",distance:"1px",start:"top",opacity:0.4,alwaysVisible:!1,disableFadeOut:!1,railVisible:!1,railColor:"#333",railOpacity:"0.2",railClass:"slimScrollRail",barClass:"slimScrollBar",wrapperClass:"slimScrollDiv",allowPageScroll:!1,scroll:0,touchScrollStep:200},m);this.each(function(){function f(h,d,f){var g=h,e=b.outerHeight()-c.outerHeight();d&&(g=parseInt(c.css("top"))+
9
- h*parseInt(a.wheelStep)/100*c.outerHeight(),g=Math.min(Math.max(g,0),e),c.css({top:g+"px"}));j=parseInt(c.css("top"))/(b.outerHeight()-c.outerHeight());g=j*(b[0].scrollHeight-b.outerHeight());f&&(g=h,h=g/b[0].scrollHeight*b.outerHeight(),h=Math.min(Math.max(h,0),e),c.css({top:h+"px"}));b.scrollTop(g);q();l()}function r(){s=Math.max(b.outerHeight()/b[0].scrollHeight*b.outerHeight(),A);c.css({height:s+"px"})}function q(){r();clearTimeout(w);j==~~j&&(n=a.allowPageScroll,x!=j&&b.trigger("slimscroll",
10
- 0==~~j?"top":"bottom"));x=j;s>=b.outerHeight()?n=!0:(c.stop(!0,!0).fadeIn("fast"),a.railVisible&&e.stop(!0,!0).fadeIn("fast"))}function l(){a.alwaysVisible||(w=setTimeout(function(){if((!a.disableFadeOut||!p)&&!t&&!u)c.fadeOut("slow"),e.fadeOut("slow")},1E3))}var p,t,u,w,y,s,j,x,A=30,n=!1,b=d(this);if(b.parent().hasClass("slimScrollDiv")){var k=b.scrollTop(),c=b.parent().find(".slimScrollBar"),e=b.parent().find(".slimScrollRail");r();m&&("scrollTo"in m?k=parseInt(a.scrollTo):"scrollBy"in m&&(k+=parseInt(a.scrollBy)),
11
- f(k,!1,!0))}else{a.height="auto"==a.height?b.parent().innerHeight():a.height;k=d("<div></div>").addClass(a.wrapperClass).css({position:"relative",overflow:"hidden",width:a.width,height:a.height});b.css({overflow:"hidden",width:a.width,height:a.height});var e=d("<div></div>").addClass(a.railClass).css({width:a.size,height:"100%",position:"absolute",top:0,display:a.alwaysVisible&&a.railVisible?"block":"none","border-radius":a.size,background:a.railColor,opacity:a.railOpacity,zIndex:90}),c=d("<div></div>").addClass(a.barClass).css({background:a.color,
12
- width:a.size,position:"absolute",top:0,opacity:a.opacity,display:a.alwaysVisible?"block":"none","border-radius":a.size,BorderRadius:a.size,MozBorderRadius:a.size,WebkitBorderRadius:a.size,zIndex:99}),z="right"==a.position?{right:a.distance}:{left:a.distance};e.css(z);c.css(z);b.wrap(k);b.parent().append(c);b.parent().append(e);c.draggable({axis:"y",containment:"parent",start:function(){u=!0},stop:function(){u=!1;l()},drag:function(){f(0,d(this).position().top,!1)}});e.hover(function(){q()},function(){l()});
13
- c.hover(function(){t=!0},function(){t=!1});b.hover(function(){p=!0;q();l()},function(){p=!1;l()});b.bind("touchstart",function(a){a.originalEvent.touches.length&&(y=a.originalEvent.touches[0].pageY)});b.bind("touchmove",function(b){b.originalEvent.preventDefault();b.originalEvent.touches.length&&f((y-b.originalEvent.touches[0].pageY)/a.touchScrollStep,!0)});var v=function(a){if(p){a=a||window.event;var b=0;a.wheelDelta&&(b=-a.wheelDelta/120);a.detail&&(b=a.detail/3);f(b,!0);a.preventDefault&&!n&&
1
+ /*! Copyright (c) 2011 Piotr Rochala (http://rocha.la)
2
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
3
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
4
+ *
5
+ * Version: 1.0.5
6
+ *
7
+ */
8
+ (function(d){jQuery.fn.extend({slimScroll:function(m){var a=d.extend({wheelStep:20,width:"auto",height:"250px",size:"7px",color:"#000",position:"right",distance:"1px",start:"top",opacity:0.4,alwaysVisible:!1,disableFadeOut:!1,railVisible:!1,railColor:"#333",railOpacity:"0.2",railClass:"slimScrollRail",barClass:"slimScrollBar",wrapperClass:"slimScrollDiv",allowPageScroll:!1,scroll:0,touchScrollStep:200},m);this.each(function(){function f(h,d,f){var g=h,e=b.outerHeight()-c.outerHeight();d&&(g=parseInt(c.css("top"))+
9
+ h*parseInt(a.wheelStep)/100*c.outerHeight(),g=Math.min(Math.max(g,0),e),c.css({top:g+"px"}));j=parseInt(c.css("top"))/(b.outerHeight()-c.outerHeight());g=j*(b[0].scrollHeight-b.outerHeight());f&&(g=h,h=g/b[0].scrollHeight*b.outerHeight(),h=Math.min(Math.max(h,0),e),c.css({top:h+"px"}));b.scrollTop(g);q();l()}function r(){s=Math.max(b.outerHeight()/b[0].scrollHeight*b.outerHeight(),A);c.css({height:s+"px"})}function q(){r();clearTimeout(w);j==~~j&&(n=a.allowPageScroll,x!=j&&b.trigger("slimscroll",
10
+ 0==~~j?"top":"bottom"));x=j;s>=b.outerHeight()?n=!0:(c.stop(!0,!0).fadeIn("fast"),a.railVisible&&e.stop(!0,!0).fadeIn("fast"))}function l(){a.alwaysVisible||(w=setTimeout(function(){if((!a.disableFadeOut||!p)&&!t&&!u)c.fadeOut("slow"),e.fadeOut("slow")},1E3))}var p,t,u,w,y,s,j,x,A=30,n=!1,b=d(this);if(b.parent().hasClass("slimScrollDiv")){var k=b.scrollTop(),c=b.parent().find(".slimScrollBar"),e=b.parent().find(".slimScrollRail");r();m&&("scrollTo"in m?k=parseInt(a.scrollTo):"scrollBy"in m&&(k+=parseInt(a.scrollBy)),
11
+ f(k,!1,!0))}else{a.height="auto"==a.height?b.parent().innerHeight():a.height;k=d("<div></div>").addClass(a.wrapperClass).css({position:"relative",overflow:"hidden",width:a.width,height:a.height});b.css({overflow:"hidden",width:a.width,height:a.height});var e=d("<div></div>").addClass(a.railClass).css({width:a.size,height:"100%",position:"absolute",top:0,display:a.alwaysVisible&&a.railVisible?"block":"none","border-radius":a.size,background:a.railColor,opacity:a.railOpacity,zIndex:90}),c=d("<div></div>").addClass(a.barClass).css({background:a.color,
12
+ width:a.size,position:"absolute",top:0,opacity:a.opacity,display:a.alwaysVisible?"block":"none","border-radius":a.size,BorderRadius:a.size,MozBorderRadius:a.size,WebkitBorderRadius:a.size,zIndex:99}),z="right"==a.position?{right:a.distance}:{left:a.distance};e.css(z);c.css(z);b.wrap(k);b.parent().append(c);b.parent().append(e);c.draggable({axis:"y",containment:"parent",start:function(){u=!0},stop:function(){u=!1;l()},drag:function(){f(0,d(this).position().top,!1)}});e.hover(function(){q()},function(){l()});
13
+ c.hover(function(){t=!0},function(){t=!1});b.hover(function(){p=!0;q();l()},function(){p=!1;l()});b.bind("touchstart",function(a){a.originalEvent.touches.length&&(y=a.originalEvent.touches[0].pageY)});b.bind("touchmove",function(b){b.originalEvent.preventDefault();b.originalEvent.touches.length&&f((y-b.originalEvent.touches[0].pageY)/a.touchScrollStep,!0)});var v=function(a){if(p){a=a||window.event;var b=0;a.wheelDelta&&(b=-a.wheelDelta/120);a.detail&&(b=a.detail/3);f(b,!0);a.preventDefault&&!n&&
14
14
  a.preventDefault();n||(a.returnValue=!1)}};(function(){window.addEventListener?(this.addEventListener("DOMMouseScroll",v,!1),this.addEventListener("mousewheel",v,!1)):document.attachEvent("onmousewheel",v)})();r();"bottom"==a.start?(c.css({top:b.outerHeight()-c.outerHeight()}),f(0,!0)):"object"==typeof a.start&&(f(d(a.start).position().top,null,!0),a.alwaysVisible||c.hide())}});return this}});jQuery.fn.extend({slimscroll:jQuery.fn.slimScroll})})(jQuery);
@@ -1,703 +1,703 @@
1
- /*global console, module*/
2
-
3
- function MediumEditor(elements, options) {
4
- 'use strict';
5
- return this.init(elements, options);
6
- }
7
-
8
- if (typeof module === 'object') {
9
- module.exports = MediumEditor;
10
- }
11
-
12
- (function (window, document) {
13
- 'use strict';
14
-
15
- function extend(b, a) {
16
- var prop;
17
- if (b === undefined) {
18
- return a;
19
- }
20
- for (prop in a) {
21
- if (a.hasOwnProperty(prop) && b.hasOwnProperty(prop) === false) {
22
- b[prop] = a[prop];
23
- }
24
- }
25
- return b;
26
- }
27
-
28
- // http://stackoverflow.com/questions/5605401/insert-link-in-contenteditable-element
29
- // by Tim Down
30
- function saveSelection() {
31
- var i,
32
- len,
33
- ranges,
34
- sel = window.getSelection();
35
- if (sel.getRangeAt && sel.rangeCount) {
36
- ranges = [];
37
- for (i = 0, len = sel.rangeCount; i < len; i += 1) {
38
- ranges.push(sel.getRangeAt(i));
39
- }
40
- return ranges;
41
- }
42
- return null;
43
- }
44
-
45
- function restoreSelection(savedSel) {
46
- var i,
47
- len,
48
- sel = window.getSelection();
49
- if (savedSel) {
50
- sel.removeAllRanges();
51
- for (i = 0, len = savedSel.length; i < len; i += 1) {
52
- sel.addRange(savedSel[i]);
53
- }
54
- }
55
- }
56
-
57
- // http://stackoverflow.com/questions/1197401/how-can-i-get-the-element-the-caret-is-in-with-javascript-when-using-contentedi
58
- // by You
59
- function getSelectionStart() {
60
- var node = document.getSelection().anchorNode,
61
- startNode = (node && node.nodeType === 3 ? node.parentNode : node);
62
- return startNode;
63
- }
64
-
65
- // http://stackoverflow.com/questions/4176923/html-of-selected-text
66
- // by Tim Down
67
- function getSelectionHtml() {
68
- var i,
69
- html = '',
70
- sel,
71
- len,
72
- container;
73
- if (window.getSelection !== undefined) {
74
- sel = window.getSelection();
75
- if (sel.rangeCount) {
76
- container = document.createElement('div');
77
- for (i = 0, len = sel.rangeCount; i < len; i += 1) {
78
- container.appendChild(sel.getRangeAt(i).cloneContents());
79
- }
80
- html = container.innerHTML;
81
- }
82
- } else if (document.selection !== undefined) {
83
- if (document.selection.type === 'Text') {
84
- html = document.selection.createRange().htmlText;
85
- }
86
- }
87
- return html;
88
- }
89
-
90
- MediumEditor.prototype = {
91
- defaults: {
92
- allowMultiParagraphSelection: true,
93
- anchorInputPlaceholder: 'Paste or type a link',
94
- buttons: ['bold', 'italic', 'underline', 'anchor', 'header1', 'header2', 'quote'],
95
- delay: 0,
96
- diffLeft: 0,
97
- diffTop: -10,
98
- disableReturn: false,
99
- disableToolbar: false,
100
- firstHeader: 'h3',
101
- forcePlainText: true,
102
- placeholder: 'Type your text',
103
- secondHeader: 'h4',
104
- targetBlank: false
105
- },
106
-
107
- init: function (elements, options) {
108
- this.elements = typeof elements === 'string' ? document.querySelectorAll(elements) : elements;
109
- if (this.elements.length === 0) {
110
- return;
111
- }
112
- this.isActive = true;
113
- this.parentElements = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre'];
114
- this.id = document.querySelectorAll('.medium-editor-toolbar').length + 1;
115
- this.options = extend(options, this.defaults);
116
- return this.initElements()
117
- .bindSelect()
118
- .bindPaste()
119
- .setPlaceholders()
120
- .bindWindowActions();
121
- },
122
-
123
- initElements: function () {
124
- var i,
125
- addToolbar = false;
126
- for (i = 0; i < this.elements.length; i += 1) {
127
- this.elements[i].setAttribute('contentEditable', true);
128
- if (!this.elements[i].getAttribute('data-placeholder')) {
129
- this.elements[i].setAttribute('data-placeholder', this.options.placeholder);
130
- }
131
- this.elements[i].setAttribute('data-medium-element', true);
132
- this.bindParagraphCreation(i).bindReturn(i).bindTab(i);
133
- if (!this.options.disableToolbar && !this.elements[i].getAttribute('data-disable-toolbar')) {
134
- addToolbar = true;
135
- }
136
- }
137
- // Init toolbar
138
- if (addToolbar) {
139
- this.initToolbar()
140
- .bindButtons()
141
- .bindAnchorForm();
142
- }
143
- return this;
144
- },
145
-
146
- serialize: function () {
147
- var i,
148
- elementid,
149
- content = {};
150
- for (i = 0; i < this.elements.length; i += 1) {
151
- elementid = (this.elements[i].id !== '') ? this.elements[i].id : 'element-' + i;
152
- content[elementid] = {
153
- value: this.elements[i].innerHTML.trim()
154
- };
155
- }
156
- return content;
157
- },
158
-
159
- bindParagraphCreation: function (index) {
160
- var self = this;
161
- this.elements[index].addEventListener('keyup', function (e) {
162
- var node = getSelectionStart(),
163
- tagName;
164
- if (node && node.getAttribute('data-medium-element') && node.children.length === 0
165
- && !(self.options.disableReturn || node.getAttribute('data-disable-return'))) {
166
- document.execCommand('formatBlock', false, 'p');
167
- }
168
- if (e.which === 13 && !e.shiftKey) {
169
- node = getSelectionStart();
170
- tagName = node.tagName.toLowerCase();
171
- if (!(self.options.disableReturn || this.getAttribute('data-disable-return'))
172
- && tagName !== 'li') {
173
- document.execCommand('formatBlock', false, 'p');
174
- if (tagName === 'a') {
175
- document.execCommand('unlink', false, null);
176
- }
177
- }
178
- }
179
- });
180
- return this;
181
- },
182
-
183
- bindReturn: function (index) {
184
- var self = this;
185
- this.elements[index].addEventListener('keypress', function (e) {
186
- if (e.which === 13 && !e.shiftKey) {
187
- if (self.options.disableReturn || this.getAttribute('data-disable-return')) {
188
- e.preventDefault();
189
- }
190
- }
191
- });
192
- return this;
193
- },
194
-
195
- bindTab: function (index) {
196
- this.elements[index].addEventListener('keydown', function (e) {
197
- if (e.which === 9) {
198
- // Override tab only for pre nodes
199
- var tag = getSelectionStart().tagName.toLowerCase();
200
- if (tag === "pre") {
201
- e.preventDefault();
202
- document.execCommand('insertHtml', null, ' ');
203
- }
204
- }
205
- });
206
- },
207
-
208
- buttonTemplate: function(btnType) {
209
- var buttonTemplates = {
210
- 'bold': '<li><button class="medium-editor-action medium-editor-action-bold" data-action="bold" data-element="b">B</button></li>',
211
- 'italic': '<li><button class="medium-editor-action medium-editor-action-italic" data-action="italic" data-element="i">I</button></li>',
212
- 'underline': '<li><button class="medium-editor-action medium-editor-action-underline" data-action="underline" data-element="u">U</button></li>',
213
- 'superscript': '<li><button class="medium-editor-action medium-editor-action-superscript" data-action="superscript" data-element="sup">x<sup>1</sup></button></li>',
214
- 'subscript': '<li><button class="medium-editor-action medium-editor-action-subscript" data-action="subscript" data-element="sub">x<sub>1</sup></button></li>',
215
- 'anchor': '<li><button class="medium-editor-action medium-editor-action-anchor" data-action="anchor" data-element="a">#</button></li>',
216
- 'header1': '<li><button class="medium-editor-action medium-editor-action-header1" data-action="append-' + this.options.firstHeader + '" data-element="' + this.options.firstHeader + '">h1</button></li>',
217
- 'header2': '<li><button class="medium-editor-action medium-editor-action-header2" data-action="append-' + this.options.secondHeader + '" data-element="' + this.options.secondHeader + '">h2</button></li>',
218
- 'quote': '<li><button class="medium-editor-action medium-editor-action-quote" data-action="append-blockquote" data-element="blockquote">&ldquo;</button></li>',
219
- 'orderedlist': '<li><button class="medium-editor-action medium-editor-action-orderedlist" data-action="insertorderedlist" data-element="ol">1.</button></li>',
220
- 'unorderedlist': '<li><button class="medium-editor-action medium-editor-action-unorderedlist" data-action="insertunorderedlist" data-element="ul">&bull;</button></li>',
221
- 'pre': '<li><button class="medium-editor-action medium-editor-action-pre" data-action="append-pre" data-element="pre">0101</button></li>'
222
- };
223
- return buttonTemplates[btnType] || false;
224
- },
225
-
226
- //TODO: actionTemplate
227
- toolbarTemplate: function () {
228
- var btns = this.options.buttons,
229
- html = '<ul id="medium-editor-toolbar-actions" class="medium-editor-toolbar-actions clearfix">',
230
- i,
231
- tpl;
232
-
233
- for (i = 0; i < btns.length; i += 1) {
234
- tpl = this.buttonTemplate(btns[i]);
235
- if (tpl) {
236
- html += tpl;
237
- }
238
- }
239
- html += '</ul>' +
240
- '<div class="medium-editor-toolbar-form-anchor" id="medium-editor-toolbar-form-anchor">' +
241
- ' <input type="text" value="" placeholder="' + this.options.anchorInputPlaceholder + '">' +
242
- ' <a href="#">&times;</a>' +
243
- '</div>';
244
- return html;
245
- },
246
-
247
- initToolbar: function () {
248
- if (this.toolbar) {
249
- return this;
250
- }
251
- this.toolbar = this.createToolbar();
252
- this.keepToolbarAlive = false;
253
- this.anchorForm = this.toolbar.querySelector('.medium-editor-toolbar-form-anchor');
254
- this.anchorInput = this.anchorForm.querySelector('input');
255
- this.toolbarActions = this.toolbar.querySelector('.medium-editor-toolbar-actions');
256
- return this;
257
- },
258
-
259
- createToolbar: function () {
260
- var toolbar = document.createElement('div');
261
- toolbar.id = 'medium-editor-toolbar-' + this.id;
262
- toolbar.className = 'medium-editor-toolbar';
263
- toolbar.innerHTML = this.toolbarTemplate();
264
- document.getElementsByTagName('body')[0].appendChild(toolbar);
265
- return toolbar;
266
- },
267
-
268
- bindSelect: function () {
269
- var self = this,
270
- timer = '',
271
- i;
272
- this.checkSelectionWrapper = function (e) {
273
- clearTimeout(timer);
274
- setTimeout(function () {
275
- self.checkSelection(e);
276
- }, self.options.delay);
277
- };
278
-
279
- document.documentElement.addEventListener('mouseup', this.checkSelectionWrapper);
280
-
281
- for (i = 0; i < this.elements.length; i += 1) {
282
- this.elements[i].addEventListener('keyup', this.checkSelectionWrapper);
283
- this.elements[i].addEventListener('blur', this.checkSelectionWrapper);
284
- }
285
- return this;
286
- },
287
-
288
- checkSelection: function () {
289
- var i,
290
- newSelection,
291
- hasMultiParagraphs,
292
- selectionHtml,
293
- selectionElement;
294
- if (this.keepToolbarAlive !== true && !this.options.disableToolbar) {
295
- newSelection = window.getSelection();
296
- selectionHtml = getSelectionHtml();
297
- selectionHtml = selectionHtml.replace(/<[\S]+><\/[\S]+>/gim, '');
298
- // Check if selection is between multi paragraph <p>.
299
- hasMultiParagraphs = selectionHtml.match(/<(p|h[0-6]|blockquote)>([\s\S]*?)<\/(p|h[0-6]|blockquote)>/g);
300
- hasMultiParagraphs = hasMultiParagraphs ? hasMultiParagraphs.length : 0;
301
- if (newSelection.toString().trim() === ''
302
- || (this.options.allowMultiParagraphSelection === false && hasMultiParagraphs)) {
303
- this.hideToolbarActions();
304
- } else {
305
- selectionElement = this.getSelectionElement();
306
- if (!selectionElement || selectionElement.getAttribute('data-disable-toolbar')) {
307
- this.hideToolbarActions();
308
- } else {
309
- this.selection = newSelection;
310
- this.selectionRange = this.selection.getRangeAt(0);
311
- for (i = 0; i < this.elements.length; i += 1) {
312
- if (this.elements[i] === selectionElement) {
313
- this.setToolbarButtonStates()
314
- .setToolbarPosition()
315
- .showToolbarActions();
316
- return;
317
- }
318
- }
319
- this.hideToolbarActions();
320
- }
321
- }
322
- }
323
- return this;
324
- },
325
-
326
- getSelectionElement: function () {
327
- var selection = window.getSelection(),
328
- range = selection.getRangeAt(0),
329
- current = range.commonAncestorContainer,
330
- parent = current.parentNode,
331
- result,
332
- getMediumElement = function(e) {
333
- var parent = e;
334
- try {
335
- while (!parent.getAttribute('data-medium-element')) {
336
- parent = parent.parentNode;
337
- }
338
- } catch (errb) {
339
- return false;
340
- }
341
- return parent;
342
- };
343
- // First try on current node
344
- try {
345
- if (current.getAttribute('data-medium-element')) {
346
- result = current;
347
- } else {
348
- result = getMediumElement(parent);
349
- }
350
- // If not search in the parent nodes.
351
- } catch (err) {
352
- result = getMediumElement(parent);
353
- }
354
- return result;
355
- },
356
-
357
- setToolbarPosition: function () {
358
- var buttonHeight = 50,
359
- selection = window.getSelection(),
360
- range = selection.getRangeAt(0),
361
- boundary = range.getBoundingClientRect(),
362
- defaultLeft = (this.options.diffLeft) - (this.toolbar.offsetWidth / 2),
363
- middleBoundary = (boundary.left + boundary.right) / 2,
364
- halfOffsetWidth = this.toolbar.offsetWidth / 2;
365
- if (boundary.top < buttonHeight) {
366
- this.toolbar.classList.add('medium-toolbar-arrow-over');
367
- this.toolbar.classList.remove('medium-toolbar-arrow-under');
368
- this.toolbar.style.top = buttonHeight + boundary.bottom - this.options.diffTop + window.pageYOffset - this.toolbar.offsetHeight + 'px';
369
- } else {
370
- this.toolbar.classList.add('medium-toolbar-arrow-under');
371
- this.toolbar.classList.remove('medium-toolbar-arrow-over');
372
- this.toolbar.style.top = boundary.top + this.options.diffTop + window.pageYOffset - this.toolbar.offsetHeight + 'px';
373
- }
374
- if (middleBoundary < halfOffsetWidth) {
375
- this.toolbar.style.left = defaultLeft + halfOffsetWidth + 'px';
376
- } else if ((window.innerWidth - middleBoundary) < halfOffsetWidth) {
377
- this.toolbar.style.left = window.innerWidth + defaultLeft - halfOffsetWidth + 'px';
378
- } else {
379
- this.toolbar.style.left = defaultLeft + middleBoundary + 'px';
380
- }
381
- return this;
382
- },
383
-
384
- setToolbarButtonStates: function () {
385
- var buttons = this.toolbarActions.querySelectorAll('button'),
386
- i;
387
- for (i = 0; i < buttons.length; i += 1) {
388
- buttons[i].classList.remove('medium-editor-button-active');
389
- }
390
- this.checkActiveButtons();
391
- return this;
392
- },
393
-
394
- checkActiveButtons: function () {
395
- var parentNode = this.selection.anchorNode;
396
- if (!parentNode.tagName) {
397
- parentNode = this.selection.anchorNode.parentNode;
398
- }
399
- while (parentNode.tagName !== undefined && this.parentElements.indexOf(parentNode.tagName) === -1) {
400
- this.activateButton(parentNode.tagName.toLowerCase());
401
- parentNode = parentNode.parentNode;
402
- }
403
- },
404
-
405
- activateButton: function (tag) {
406
- var el = this.toolbar.querySelector('[data-element="' + tag + '"]');
407
- if (el !== null && el.className.indexOf('medium-editor-button-active') === -1) {
408
- el.className += ' medium-editor-button-active';
409
- }
410
- },
411
-
412
- bindButtons: function () {
413
- var buttons = this.toolbar.querySelectorAll('button'),
414
- i,
415
- self = this,
416
- triggerAction = function (e) {
417
- e.preventDefault();
418
- e.stopPropagation();
419
- if (self.selection === undefined) {
420
- self.checkSelection(e);
421
- }
422
- if (this.className.indexOf('medium-editor-button-active') > -1) {
423
- this.classList.remove('medium-editor-button-active');
424
- } else {
425
- this.className += ' medium-editor-button-active';
426
- }
427
- console.log(this.getAttribute('data-action'));
428
- self.execAction(this.getAttribute('data-action'), e);
429
- };
430
- for (i = 0; i < buttons.length; i += 1) {
431
- buttons[i].addEventListener('click', triggerAction);
432
- }
433
- this.setFirstAndLastItems(buttons);
434
- return this;
435
- },
436
-
437
- setFirstAndLastItems: function (buttons) {
438
- buttons[0].className += ' medium-editor-button-first';
439
- buttons[buttons.length - 1].className += ' medium-editor-button-last';
440
- return this;
441
- },
442
-
443
- execAction: function (action, e) {
444
- if (action.indexOf('append-') > -1) {
445
- this.execFormatBlock(action.replace('append-', ''));
446
- this.setToolbarPosition();
447
- this.setToolbarButtonStates();
448
- } else if (action === 'anchor') {
449
- this.triggerAnchorAction(e);
450
- } else {
451
- document.execCommand(action, false, null);
452
- this.setToolbarPosition();
453
- }
454
- },
455
-
456
- triggerAnchorAction: function () {
457
- if (this.selection.anchorNode.parentNode.tagName.toLowerCase() === 'a') {
458
- document.execCommand('unlink', false, null);
459
- } else {
460
- if (this.anchorForm.style.display === 'block') {
461
- this.showToolbarActions();
462
- } else {
463
- this.showAnchorForm();
464
- }
465
- }
466
- return this;
467
- },
468
-
469
- execFormatBlock: function (el) {
470
- var selectionData = this.getSelectionData(this.selection.anchorNode);
471
- // FF handles blockquote differently on formatBlock
472
- // allowing nesting, we need to use outdent
473
- // https://developer.mozilla.org/en-US/docs/Rich-Text_Editing_in_Mozilla
474
- if (el === 'blockquote' && selectionData.el
475
- && selectionData.el.parentNode.tagName.toLowerCase() === 'blockquote') {
476
- return document.execCommand('outdent', false, null);
477
- }
478
- if (selectionData.tagName === el) {
479
- el = 'p';
480
- }
481
- return document.execCommand('formatBlock', false, el);
482
- },
483
-
484
- getSelectionData: function (el) {
485
- var tagName;
486
-
487
- if (el && el.tagName) {
488
- tagName = el.tagName.toLowerCase();
489
- }
490
-
491
- while (el && this.parentElements.indexOf(tagName) === -1) {
492
- el = el.parentNode;
493
- if (el && el.tagName) {
494
- tagName = el.tagName.toLowerCase();
495
- }
496
- }
497
-
498
- return {
499
- el: el,
500
- tagName: tagName
501
- };
502
- },
503
-
504
- getFirstChild: function (el) {
505
- var firstChild = el.firstChild;
506
- while (firstChild !== null && firstChild.nodeType !== 1) {
507
- firstChild = firstChild.nextSibling;
508
- }
509
- return firstChild;
510
- },
511
-
512
- bindElementToolbarEvents: function (el) {
513
- var self = this;
514
- el.addEventListener('mouseup', function (e) {
515
- self.checkSelection(e);
516
- });
517
- el.addEventListener('keyup', function (e) {
518
- self.checkSelection(e);
519
- });
520
- },
521
-
522
- hideToolbarActions: function () {
523
- this.keepToolbarAlive = false;
524
- this.toolbar.classList.remove('medium-editor-toolbar-active');
525
- },
526
-
527
- showToolbarActions: function () {
528
- var self = this,
529
- timer;
530
- this.anchorForm.style.display = 'none';
531
- this.toolbarActions.style.display = 'block';
532
- this.keepToolbarAlive = false;
533
- clearTimeout(timer);
534
- timer = setTimeout(function() {
535
- if (!self.toolbar.classList.contains('medium-editor-toolbar-active')) {
536
- self.toolbar.classList.add('medium-editor-toolbar-active');
537
- }
538
- }, 100);
539
- },
540
-
541
- showAnchorForm: function () {
542
- this.toolbarActions.style.display = 'none';
543
- this.savedSelection = saveSelection();
544
- this.anchorForm.style.display = 'block';
545
- this.keepToolbarAlive = true;
546
- this.anchorInput.focus();
547
- this.anchorInput.value = '';
548
- },
549
-
550
- bindAnchorForm: function () {
551
- var linkCancel = this.anchorForm.querySelector('a'),
552
- self = this;
553
- this.anchorForm.addEventListener('click', function (e) {
554
- e.stopPropagation();
555
- });
556
- this.anchorInput.addEventListener('keyup', function (e) {
557
- if (e.keyCode === 13) {
558
- e.preventDefault();
559
- self.createLink(this);
560
- }
561
- });
562
- this.anchorInput.addEventListener('blur', function (e) {
563
- self.keepToolbarAlive = false;
564
- self.checkSelection();
565
- });
566
- linkCancel.addEventListener('click', function (e) {
567
- e.preventDefault();
568
- self.showToolbarActions();
569
- restoreSelection(self.savedSelection);
570
- });
571
- return this;
572
- },
573
-
574
- setTargetBlank: function () {
575
- var el = getSelectionStart(),
576
- i;
577
- if (el.tagName.toLowerCase() === 'a') {
578
- el.target = '_blank';
579
- } else {
580
- el = el.getElementsByTagName('a');
581
- for (i = 0; i < el.length; i += 1) {
582
- el[i].target = '_blank';
583
- }
584
- }
585
- },
586
-
587
- createLink: function (input) {
588
- restoreSelection(this.savedSelection);
589
- document.execCommand('createLink', false, input.value);
590
- if (this.options.targetBlank) {
591
- this.setTargetBlank();
592
- }
593
- this.showToolbarActions();
594
- input.value = '';
595
- },
596
-
597
- bindWindowActions: function () {
598
- var timerResize,
599
- self = this;
600
- window.addEventListener('resize', function () {
601
- clearTimeout(timerResize);
602
- timerResize = setTimeout(function () {
603
- if (self.toolbar.classList.contains('medium-editor-toolbar-active')) {
604
- self.setToolbarPosition();
605
- }
606
- }, 100);
607
- });
608
- return this;
609
- },
610
-
611
- activate: function () {
612
- var i;
613
- if (this.isActive) {
614
- return;
615
- }
616
-
617
- if (this.toolbar !== undefined) {
618
- this.toolbar.style.display = 'block';
619
- }
620
-
621
- this.isActive = true;
622
- for (i = 0; i < this.elements.length; i += 1) {
623
- this.elements[i].setAttribute('contentEditable', true);
624
- }
625
- this.bindSelect();
626
- },
627
-
628
- deactivate: function () {
629
- var i;
630
- if (!this.isActive) {
631
- return;
632
- }
633
- this.isActive = false;
634
-
635
- if (this.toolbar !== undefined) {
636
- this.toolbar.style.display = 'none';
637
- }
638
-
639
- document.documentElement.removeEventListener('mouseup', this.checkSelectionWrapper);
640
-
641
- for (i = 0; i < this.elements.length; i += 1) {
642
- this.elements[i].removeEventListener('keyup', this.checkSelectionWrapper);
643
- this.elements[i].removeEventListener('blur', this.checkSelectionWrapper);
644
- this.elements[i].removeAttribute('contentEditable');
645
- }
646
- },
647
-
648
- bindPaste: function () {
649
- if (!this.options.forcePlainText) {
650
- return this;
651
- }
652
- var i,
653
- self = this,
654
- pasteWrapper = function (e) {
655
- var paragraphs,
656
- html = '',
657
- p;
658
- e.target.classList.remove('medium-editor-placeholder');
659
- if (e.clipboardData && e.clipboardData.getData) {
660
- e.preventDefault();
661
- if (!self.options.disableReturn) {
662
- paragraphs = e.clipboardData.getData('text/plain').split(/[\r\n]/g);
663
- for (p = 0; p < paragraphs.length; p += 1) {
664
- if (paragraphs[p] !== '') {
665
- html += '<p>' + paragraphs[p] + '</p>';
666
- }
667
- }
668
- document.execCommand('insertHTML', false, html);
669
- } else {
670
- document.execCommand('insertHTML', false, e.clipboardData.getData('text/plain'));
671
- }
672
- }
673
- };
674
- for (i = 0; i < this.elements.length; i += 1) {
675
- this.elements[i].addEventListener('paste', pasteWrapper);
676
- }
677
- return this;
678
- },
679
-
680
- setPlaceholders: function () {
681
- var i,
682
- activatePlaceholder = function (el) {
683
- if (el.textContent.replace(/^\s+|\s+$/g, '') === '') {
684
- el.classList.add('medium-editor-placeholder');
685
- }
686
- },
687
- placeholderWrapper = function (e) {
688
- this.classList.remove('medium-editor-placeholder');
689
- if (e.type !== 'keypress') {
690
- activatePlaceholder(this);
691
- }
692
- };
693
- for (i = 0; i < this.elements.length; i += 1) {
694
- activatePlaceholder(this.elements[i]);
695
- this.elements[i].addEventListener('blur', placeholderWrapper);
696
- this.elements[i].addEventListener('keypress', placeholderWrapper);
697
- }
698
- return this;
699
- }
700
-
701
- };
702
-
1
+ /*global console, module*/
2
+
3
+ function MediumEditor(elements, options) {
4
+ 'use strict';
5
+ return this.init(elements, options);
6
+ }
7
+
8
+ if (typeof module === 'object') {
9
+ module.exports = MediumEditor;
10
+ }
11
+
12
+ (function (window, document) {
13
+ 'use strict';
14
+
15
+ function extend(b, a) {
16
+ var prop;
17
+ if (b === undefined) {
18
+ return a;
19
+ }
20
+ for (prop in a) {
21
+ if (a.hasOwnProperty(prop) && b.hasOwnProperty(prop) === false) {
22
+ b[prop] = a[prop];
23
+ }
24
+ }
25
+ return b;
26
+ }
27
+
28
+ // http://stackoverflow.com/questions/5605401/insert-link-in-contenteditable-element
29
+ // by Tim Down
30
+ function saveSelection() {
31
+ var i,
32
+ len,
33
+ ranges,
34
+ sel = window.getSelection();
35
+ if (sel.getRangeAt && sel.rangeCount) {
36
+ ranges = [];
37
+ for (i = 0, len = sel.rangeCount; i < len; i += 1) {
38
+ ranges.push(sel.getRangeAt(i));
39
+ }
40
+ return ranges;
41
+ }
42
+ return null;
43
+ }
44
+
45
+ function restoreSelection(savedSel) {
46
+ var i,
47
+ len,
48
+ sel = window.getSelection();
49
+ if (savedSel) {
50
+ sel.removeAllRanges();
51
+ for (i = 0, len = savedSel.length; i < len; i += 1) {
52
+ sel.addRange(savedSel[i]);
53
+ }
54
+ }
55
+ }
56
+
57
+ // http://stackoverflow.com/questions/1197401/how-can-i-get-the-element-the-caret-is-in-with-javascript-when-using-contentedi
58
+ // by You
59
+ function getSelectionStart() {
60
+ var node = document.getSelection().anchorNode,
61
+ startNode = (node && node.nodeType === 3 ? node.parentNode : node);
62
+ return startNode;
63
+ }
64
+
65
+ // http://stackoverflow.com/questions/4176923/html-of-selected-text
66
+ // by Tim Down
67
+ function getSelectionHtml() {
68
+ var i,
69
+ html = '',
70
+ sel,
71
+ len,
72
+ container;
73
+ if (window.getSelection !== undefined) {
74
+ sel = window.getSelection();
75
+ if (sel.rangeCount) {
76
+ container = document.createElement('div');
77
+ for (i = 0, len = sel.rangeCount; i < len; i += 1) {
78
+ container.appendChild(sel.getRangeAt(i).cloneContents());
79
+ }
80
+ html = container.innerHTML;
81
+ }
82
+ } else if (document.selection !== undefined) {
83
+ if (document.selection.type === 'Text') {
84
+ html = document.selection.createRange().htmlText;
85
+ }
86
+ }
87
+ return html;
88
+ }
89
+
90
+ MediumEditor.prototype = {
91
+ defaults: {
92
+ allowMultiParagraphSelection: true,
93
+ anchorInputPlaceholder: 'Paste or type a link',
94
+ buttons: ['bold', 'italic', 'underline', 'anchor', 'header1', 'header2', 'quote'],
95
+ delay: 0,
96
+ diffLeft: 0,
97
+ diffTop: -10,
98
+ disableReturn: false,
99
+ disableToolbar: false,
100
+ firstHeader: 'h3',
101
+ forcePlainText: true,
102
+ placeholder: 'Type your text',
103
+ secondHeader: 'h4',
104
+ targetBlank: false
105
+ },
106
+
107
+ init: function (elements, options) {
108
+ this.elements = typeof elements === 'string' ? document.querySelectorAll(elements) : elements;
109
+ if (this.elements.length === 0) {
110
+ return;
111
+ }
112
+ this.isActive = true;
113
+ this.parentElements = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre'];
114
+ this.id = document.querySelectorAll('.medium-editor-toolbar').length + 1;
115
+ this.options = extend(options, this.defaults);
116
+ return this.initElements()
117
+ .bindSelect()
118
+ .bindPaste()
119
+ .setPlaceholders()
120
+ .bindWindowActions();
121
+ },
122
+
123
+ initElements: function () {
124
+ var i,
125
+ addToolbar = false;
126
+ for (i = 0; i < this.elements.length; i += 1) {
127
+ this.elements[i].setAttribute('contentEditable', true);
128
+ if (!this.elements[i].getAttribute('data-placeholder')) {
129
+ this.elements[i].setAttribute('data-placeholder', this.options.placeholder);
130
+ }
131
+ this.elements[i].setAttribute('data-medium-element', true);
132
+ this.bindParagraphCreation(i).bindReturn(i).bindTab(i);
133
+ if (!this.options.disableToolbar && !this.elements[i].getAttribute('data-disable-toolbar')) {
134
+ addToolbar = true;
135
+ }
136
+ }
137
+ // Init toolbar
138
+ if (addToolbar) {
139
+ this.initToolbar()
140
+ .bindButtons()
141
+ .bindAnchorForm();
142
+ }
143
+ return this;
144
+ },
145
+
146
+ serialize: function () {
147
+ var i,
148
+ elementid,
149
+ content = {};
150
+ for (i = 0; i < this.elements.length; i += 1) {
151
+ elementid = (this.elements[i].id !== '') ? this.elements[i].id : 'element-' + i;
152
+ content[elementid] = {
153
+ value: this.elements[i].innerHTML.trim()
154
+ };
155
+ }
156
+ return content;
157
+ },
158
+
159
+ bindParagraphCreation: function (index) {
160
+ var self = this;
161
+ this.elements[index].addEventListener('keyup', function (e) {
162
+ var node = getSelectionStart(),
163
+ tagName;
164
+ if (node && node.getAttribute('data-medium-element') && node.children.length === 0
165
+ && !(self.options.disableReturn || node.getAttribute('data-disable-return'))) {
166
+ document.execCommand('formatBlock', false, 'p');
167
+ }
168
+ if (e.which === 13 && !e.shiftKey) {
169
+ node = getSelectionStart();
170
+ tagName = node.tagName.toLowerCase();
171
+ if (!(self.options.disableReturn || this.getAttribute('data-disable-return'))
172
+ && tagName !== 'li') {
173
+ document.execCommand('formatBlock', false, 'p');
174
+ if (tagName === 'a') {
175
+ document.execCommand('unlink', false, null);
176
+ }
177
+ }
178
+ }
179
+ });
180
+ return this;
181
+ },
182
+
183
+ bindReturn: function (index) {
184
+ var self = this;
185
+ this.elements[index].addEventListener('keypress', function (e) {
186
+ if (e.which === 13 && !e.shiftKey) {
187
+ if (self.options.disableReturn || this.getAttribute('data-disable-return')) {
188
+ e.preventDefault();
189
+ }
190
+ }
191
+ });
192
+ return this;
193
+ },
194
+
195
+ bindTab: function (index) {
196
+ this.elements[index].addEventListener('keydown', function (e) {
197
+ if (e.which === 9) {
198
+ // Override tab only for pre nodes
199
+ var tag = getSelectionStart().tagName.toLowerCase();
200
+ if (tag === "pre") {
201
+ e.preventDefault();
202
+ document.execCommand('insertHtml', null, ' ');
203
+ }
204
+ }
205
+ });
206
+ },
207
+
208
+ buttonTemplate: function(btnType) {
209
+ var buttonTemplates = {
210
+ 'bold': '<li><button class="medium-editor-action medium-editor-action-bold" data-action="bold" data-element="b">B</button></li>',
211
+ 'italic': '<li><button class="medium-editor-action medium-editor-action-italic" data-action="italic" data-element="i">I</button></li>',
212
+ 'underline': '<li><button class="medium-editor-action medium-editor-action-underline" data-action="underline" data-element="u">U</button></li>',
213
+ 'superscript': '<li><button class="medium-editor-action medium-editor-action-superscript" data-action="superscript" data-element="sup">x<sup>1</sup></button></li>',
214
+ 'subscript': '<li><button class="medium-editor-action medium-editor-action-subscript" data-action="subscript" data-element="sub">x<sub>1</sup></button></li>',
215
+ 'anchor': '<li><button class="medium-editor-action medium-editor-action-anchor" data-action="anchor" data-element="a">#</button></li>',
216
+ 'header1': '<li><button class="medium-editor-action medium-editor-action-header1" data-action="append-' + this.options.firstHeader + '" data-element="' + this.options.firstHeader + '">h1</button></li>',
217
+ 'header2': '<li><button class="medium-editor-action medium-editor-action-header2" data-action="append-' + this.options.secondHeader + '" data-element="' + this.options.secondHeader + '">h2</button></li>',
218
+ 'quote': '<li><button class="medium-editor-action medium-editor-action-quote" data-action="append-blockquote" data-element="blockquote">&ldquo;</button></li>',
219
+ 'orderedlist': '<li><button class="medium-editor-action medium-editor-action-orderedlist" data-action="insertorderedlist" data-element="ol">1.</button></li>',
220
+ 'unorderedlist': '<li><button class="medium-editor-action medium-editor-action-unorderedlist" data-action="insertunorderedlist" data-element="ul">&bull;</button></li>',
221
+ 'pre': '<li><button class="medium-editor-action medium-editor-action-pre" data-action="append-pre" data-element="pre">0101</button></li>'
222
+ };
223
+ return buttonTemplates[btnType] || false;
224
+ },
225
+
226
+ //TODO: actionTemplate
227
+ toolbarTemplate: function () {
228
+ var btns = this.options.buttons,
229
+ html = '<ul id="medium-editor-toolbar-actions" class="medium-editor-toolbar-actions clearfix">',
230
+ i,
231
+ tpl;
232
+
233
+ for (i = 0; i < btns.length; i += 1) {
234
+ tpl = this.buttonTemplate(btns[i]);
235
+ if (tpl) {
236
+ html += tpl;
237
+ }
238
+ }
239
+ html += '</ul>' +
240
+ '<div class="medium-editor-toolbar-form-anchor" id="medium-editor-toolbar-form-anchor">' +
241
+ ' <input type="text" value="" placeholder="' + this.options.anchorInputPlaceholder + '">' +
242
+ ' <a href="#">&times;</a>' +
243
+ '</div>';
244
+ return html;
245
+ },
246
+
247
+ initToolbar: function () {
248
+ if (this.toolbar) {
249
+ return this;
250
+ }
251
+ this.toolbar = this.createToolbar();
252
+ this.keepToolbarAlive = false;
253
+ this.anchorForm = this.toolbar.querySelector('.medium-editor-toolbar-form-anchor');
254
+ this.anchorInput = this.anchorForm.querySelector('input');
255
+ this.toolbarActions = this.toolbar.querySelector('.medium-editor-toolbar-actions');
256
+ return this;
257
+ },
258
+
259
+ createToolbar: function () {
260
+ var toolbar = document.createElement('div');
261
+ toolbar.id = 'medium-editor-toolbar-' + this.id;
262
+ toolbar.className = 'medium-editor-toolbar';
263
+ toolbar.innerHTML = this.toolbarTemplate();
264
+ document.getElementsByTagName('body')[0].appendChild(toolbar);
265
+ return toolbar;
266
+ },
267
+
268
+ bindSelect: function () {
269
+ var self = this,
270
+ timer = '',
271
+ i;
272
+ this.checkSelectionWrapper = function (e) {
273
+ clearTimeout(timer);
274
+ setTimeout(function () {
275
+ self.checkSelection(e);
276
+ }, self.options.delay);
277
+ };
278
+
279
+ document.documentElement.addEventListener('mouseup', this.checkSelectionWrapper);
280
+
281
+ for (i = 0; i < this.elements.length; i += 1) {
282
+ this.elements[i].addEventListener('keyup', this.checkSelectionWrapper);
283
+ this.elements[i].addEventListener('blur', this.checkSelectionWrapper);
284
+ }
285
+ return this;
286
+ },
287
+
288
+ checkSelection: function () {
289
+ var i,
290
+ newSelection,
291
+ hasMultiParagraphs,
292
+ selectionHtml,
293
+ selectionElement;
294
+ if (this.keepToolbarAlive !== true && !this.options.disableToolbar) {
295
+ newSelection = window.getSelection();
296
+ selectionHtml = getSelectionHtml();
297
+ selectionHtml = selectionHtml.replace(/<[\S]+><\/[\S]+>/gim, '');
298
+ // Check if selection is between multi paragraph <p>.
299
+ hasMultiParagraphs = selectionHtml.match(/<(p|h[0-6]|blockquote)>([\s\S]*?)<\/(p|h[0-6]|blockquote)>/g);
300
+ hasMultiParagraphs = hasMultiParagraphs ? hasMultiParagraphs.length : 0;
301
+ if (newSelection.toString().trim() === ''
302
+ || (this.options.allowMultiParagraphSelection === false && hasMultiParagraphs)) {
303
+ this.hideToolbarActions();
304
+ } else {
305
+ selectionElement = this.getSelectionElement();
306
+ if (!selectionElement || selectionElement.getAttribute('data-disable-toolbar')) {
307
+ this.hideToolbarActions();
308
+ } else {
309
+ this.selection = newSelection;
310
+ this.selectionRange = this.selection.getRangeAt(0);
311
+ for (i = 0; i < this.elements.length; i += 1) {
312
+ if (this.elements[i] === selectionElement) {
313
+ this.setToolbarButtonStates()
314
+ .setToolbarPosition()
315
+ .showToolbarActions();
316
+ return;
317
+ }
318
+ }
319
+ this.hideToolbarActions();
320
+ }
321
+ }
322
+ }
323
+ return this;
324
+ },
325
+
326
+ getSelectionElement: function () {
327
+ var selection = window.getSelection(),
328
+ range = selection.getRangeAt(0),
329
+ current = range.commonAncestorContainer,
330
+ parent = current.parentNode,
331
+ result,
332
+ getMediumElement = function(e) {
333
+ var parent = e;
334
+ try {
335
+ while (!parent.getAttribute('data-medium-element')) {
336
+ parent = parent.parentNode;
337
+ }
338
+ } catch (errb) {
339
+ return false;
340
+ }
341
+ return parent;
342
+ };
343
+ // First try on current node
344
+ try {
345
+ if (current.getAttribute('data-medium-element')) {
346
+ result = current;
347
+ } else {
348
+ result = getMediumElement(parent);
349
+ }
350
+ // If not search in the parent nodes.
351
+ } catch (err) {
352
+ result = getMediumElement(parent);
353
+ }
354
+ return result;
355
+ },
356
+
357
+ setToolbarPosition: function () {
358
+ var buttonHeight = 50,
359
+ selection = window.getSelection(),
360
+ range = selection.getRangeAt(0),
361
+ boundary = range.getBoundingClientRect(),
362
+ defaultLeft = (this.options.diffLeft) - (this.toolbar.offsetWidth / 2),
363
+ middleBoundary = (boundary.left + boundary.right) / 2,
364
+ halfOffsetWidth = this.toolbar.offsetWidth / 2;
365
+ if (boundary.top < buttonHeight) {
366
+ this.toolbar.classList.add('medium-toolbar-arrow-over');
367
+ this.toolbar.classList.remove('medium-toolbar-arrow-under');
368
+ this.toolbar.style.top = buttonHeight + boundary.bottom - this.options.diffTop + window.pageYOffset - this.toolbar.offsetHeight + 'px';
369
+ } else {
370
+ this.toolbar.classList.add('medium-toolbar-arrow-under');
371
+ this.toolbar.classList.remove('medium-toolbar-arrow-over');
372
+ this.toolbar.style.top = boundary.top + this.options.diffTop + window.pageYOffset - this.toolbar.offsetHeight + 'px';
373
+ }
374
+ if (middleBoundary < halfOffsetWidth) {
375
+ this.toolbar.style.left = defaultLeft + halfOffsetWidth + 'px';
376
+ } else if ((window.innerWidth - middleBoundary) < halfOffsetWidth) {
377
+ this.toolbar.style.left = window.innerWidth + defaultLeft - halfOffsetWidth + 'px';
378
+ } else {
379
+ this.toolbar.style.left = defaultLeft + middleBoundary + 'px';
380
+ }
381
+ return this;
382
+ },
383
+
384
+ setToolbarButtonStates: function () {
385
+ var buttons = this.toolbarActions.querySelectorAll('button'),
386
+ i;
387
+ for (i = 0; i < buttons.length; i += 1) {
388
+ buttons[i].classList.remove('medium-editor-button-active');
389
+ }
390
+ this.checkActiveButtons();
391
+ return this;
392
+ },
393
+
394
+ checkActiveButtons: function () {
395
+ var parentNode = this.selection.anchorNode;
396
+ if (!parentNode.tagName) {
397
+ parentNode = this.selection.anchorNode.parentNode;
398
+ }
399
+ while (parentNode.tagName !== undefined && this.parentElements.indexOf(parentNode.tagName) === -1) {
400
+ this.activateButton(parentNode.tagName.toLowerCase());
401
+ parentNode = parentNode.parentNode;
402
+ }
403
+ },
404
+
405
+ activateButton: function (tag) {
406
+ var el = this.toolbar.querySelector('[data-element="' + tag + '"]');
407
+ if (el !== null && el.className.indexOf('medium-editor-button-active') === -1) {
408
+ el.className += ' medium-editor-button-active';
409
+ }
410
+ },
411
+
412
+ bindButtons: function () {
413
+ var buttons = this.toolbar.querySelectorAll('button'),
414
+ i,
415
+ self = this,
416
+ triggerAction = function (e) {
417
+ e.preventDefault();
418
+ e.stopPropagation();
419
+ if (self.selection === undefined) {
420
+ self.checkSelection(e);
421
+ }
422
+ if (this.className.indexOf('medium-editor-button-active') > -1) {
423
+ this.classList.remove('medium-editor-button-active');
424
+ } else {
425
+ this.className += ' medium-editor-button-active';
426
+ }
427
+ console.log(this.getAttribute('data-action'));
428
+ self.execAction(this.getAttribute('data-action'), e);
429
+ };
430
+ for (i = 0; i < buttons.length; i += 1) {
431
+ buttons[i].addEventListener('click', triggerAction);
432
+ }
433
+ this.setFirstAndLastItems(buttons);
434
+ return this;
435
+ },
436
+
437
+ setFirstAndLastItems: function (buttons) {
438
+ buttons[0].className += ' medium-editor-button-first';
439
+ buttons[buttons.length - 1].className += ' medium-editor-button-last';
440
+ return this;
441
+ },
442
+
443
+ execAction: function (action, e) {
444
+ if (action.indexOf('append-') > -1) {
445
+ this.execFormatBlock(action.replace('append-', ''));
446
+ this.setToolbarPosition();
447
+ this.setToolbarButtonStates();
448
+ } else if (action === 'anchor') {
449
+ this.triggerAnchorAction(e);
450
+ } else {
451
+ document.execCommand(action, false, null);
452
+ this.setToolbarPosition();
453
+ }
454
+ },
455
+
456
+ triggerAnchorAction: function () {
457
+ if (this.selection.anchorNode.parentNode.tagName.toLowerCase() === 'a') {
458
+ document.execCommand('unlink', false, null);
459
+ } else {
460
+ if (this.anchorForm.style.display === 'block') {
461
+ this.showToolbarActions();
462
+ } else {
463
+ this.showAnchorForm();
464
+ }
465
+ }
466
+ return this;
467
+ },
468
+
469
+ execFormatBlock: function (el) {
470
+ var selectionData = this.getSelectionData(this.selection.anchorNode);
471
+ // FF handles blockquote differently on formatBlock
472
+ // allowing nesting, we need to use outdent
473
+ // https://developer.mozilla.org/en-US/docs/Rich-Text_Editing_in_Mozilla
474
+ if (el === 'blockquote' && selectionData.el
475
+ && selectionData.el.parentNode.tagName.toLowerCase() === 'blockquote') {
476
+ return document.execCommand('outdent', false, null);
477
+ }
478
+ if (selectionData.tagName === el) {
479
+ el = 'p';
480
+ }
481
+ return document.execCommand('formatBlock', false, el);
482
+ },
483
+
484
+ getSelectionData: function (el) {
485
+ var tagName;
486
+
487
+ if (el && el.tagName) {
488
+ tagName = el.tagName.toLowerCase();
489
+ }
490
+
491
+ while (el && this.parentElements.indexOf(tagName) === -1) {
492
+ el = el.parentNode;
493
+ if (el && el.tagName) {
494
+ tagName = el.tagName.toLowerCase();
495
+ }
496
+ }
497
+
498
+ return {
499
+ el: el,
500
+ tagName: tagName
501
+ };
502
+ },
503
+
504
+ getFirstChild: function (el) {
505
+ var firstChild = el.firstChild;
506
+ while (firstChild !== null && firstChild.nodeType !== 1) {
507
+ firstChild = firstChild.nextSibling;
508
+ }
509
+ return firstChild;
510
+ },
511
+
512
+ bindElementToolbarEvents: function (el) {
513
+ var self = this;
514
+ el.addEventListener('mouseup', function (e) {
515
+ self.checkSelection(e);
516
+ });
517
+ el.addEventListener('keyup', function (e) {
518
+ self.checkSelection(e);
519
+ });
520
+ },
521
+
522
+ hideToolbarActions: function () {
523
+ this.keepToolbarAlive = false;
524
+ this.toolbar.classList.remove('medium-editor-toolbar-active');
525
+ },
526
+
527
+ showToolbarActions: function () {
528
+ var self = this,
529
+ timer;
530
+ this.anchorForm.style.display = 'none';
531
+ this.toolbarActions.style.display = 'block';
532
+ this.keepToolbarAlive = false;
533
+ clearTimeout(timer);
534
+ timer = setTimeout(function() {
535
+ if (!self.toolbar.classList.contains('medium-editor-toolbar-active')) {
536
+ self.toolbar.classList.add('medium-editor-toolbar-active');
537
+ }
538
+ }, 100);
539
+ },
540
+
541
+ showAnchorForm: function () {
542
+ this.toolbarActions.style.display = 'none';
543
+ this.savedSelection = saveSelection();
544
+ this.anchorForm.style.display = 'block';
545
+ this.keepToolbarAlive = true;
546
+ this.anchorInput.focus();
547
+ this.anchorInput.value = '';
548
+ },
549
+
550
+ bindAnchorForm: function () {
551
+ var linkCancel = this.anchorForm.querySelector('a'),
552
+ self = this;
553
+ this.anchorForm.addEventListener('click', function (e) {
554
+ e.stopPropagation();
555
+ });
556
+ this.anchorInput.addEventListener('keyup', function (e) {
557
+ if (e.keyCode === 13) {
558
+ e.preventDefault();
559
+ self.createLink(this);
560
+ }
561
+ });
562
+ this.anchorInput.addEventListener('blur', function (e) {
563
+ self.keepToolbarAlive = false;
564
+ self.checkSelection();
565
+ });
566
+ linkCancel.addEventListener('click', function (e) {
567
+ e.preventDefault();
568
+ self.showToolbarActions();
569
+ restoreSelection(self.savedSelection);
570
+ });
571
+ return this;
572
+ },
573
+
574
+ setTargetBlank: function () {
575
+ var el = getSelectionStart(),
576
+ i;
577
+ if (el.tagName.toLowerCase() === 'a') {
578
+ el.target = '_blank';
579
+ } else {
580
+ el = el.getElementsByTagName('a');
581
+ for (i = 0; i < el.length; i += 1) {
582
+ el[i].target = '_blank';
583
+ }
584
+ }
585
+ },
586
+
587
+ createLink: function (input) {
588
+ restoreSelection(this.savedSelection);
589
+ document.execCommand('createLink', false, input.value);
590
+ if (this.options.targetBlank) {
591
+ this.setTargetBlank();
592
+ }
593
+ this.showToolbarActions();
594
+ input.value = '';
595
+ },
596
+
597
+ bindWindowActions: function () {
598
+ var timerResize,
599
+ self = this;
600
+ window.addEventListener('resize', function () {
601
+ clearTimeout(timerResize);
602
+ timerResize = setTimeout(function () {
603
+ if (self.toolbar.classList.contains('medium-editor-toolbar-active')) {
604
+ self.setToolbarPosition();
605
+ }
606
+ }, 100);
607
+ });
608
+ return this;
609
+ },
610
+
611
+ activate: function () {
612
+ var i;
613
+ if (this.isActive) {
614
+ return;
615
+ }
616
+
617
+ if (this.toolbar !== undefined) {
618
+ this.toolbar.style.display = 'block';
619
+ }
620
+
621
+ this.isActive = true;
622
+ for (i = 0; i < this.elements.length; i += 1) {
623
+ this.elements[i].setAttribute('contentEditable', true);
624
+ }
625
+ this.bindSelect();
626
+ },
627
+
628
+ deactivate: function () {
629
+ var i;
630
+ if (!this.isActive) {
631
+ return;
632
+ }
633
+ this.isActive = false;
634
+
635
+ if (this.toolbar !== undefined) {
636
+ this.toolbar.style.display = 'none';
637
+ }
638
+
639
+ document.documentElement.removeEventListener('mouseup', this.checkSelectionWrapper);
640
+
641
+ for (i = 0; i < this.elements.length; i += 1) {
642
+ this.elements[i].removeEventListener('keyup', this.checkSelectionWrapper);
643
+ this.elements[i].removeEventListener('blur', this.checkSelectionWrapper);
644
+ this.elements[i].removeAttribute('contentEditable');
645
+ }
646
+ },
647
+
648
+ bindPaste: function () {
649
+ if (!this.options.forcePlainText) {
650
+ return this;
651
+ }
652
+ var i,
653
+ self = this,
654
+ pasteWrapper = function (e) {
655
+ var paragraphs,
656
+ html = '',
657
+ p;
658
+ e.target.classList.remove('medium-editor-placeholder');
659
+ if (e.clipboardData && e.clipboardData.getData) {
660
+ e.preventDefault();
661
+ if (!self.options.disableReturn) {
662
+ paragraphs = e.clipboardData.getData('text/plain').split(/[\r\n]/g);
663
+ for (p = 0; p < paragraphs.length; p += 1) {
664
+ if (paragraphs[p] !== '') {
665
+ html += '<p>' + paragraphs[p] + '</p>';
666
+ }
667
+ }
668
+ document.execCommand('insertHTML', false, html);
669
+ } else {
670
+ document.execCommand('insertHTML', false, e.clipboardData.getData('text/plain'));
671
+ }
672
+ }
673
+ };
674
+ for (i = 0; i < this.elements.length; i += 1) {
675
+ this.elements[i].addEventListener('paste', pasteWrapper);
676
+ }
677
+ return this;
678
+ },
679
+
680
+ setPlaceholders: function () {
681
+ var i,
682
+ activatePlaceholder = function (el) {
683
+ if (el.textContent.replace(/^\s+|\s+$/g, '') === '') {
684
+ el.classList.add('medium-editor-placeholder');
685
+ }
686
+ },
687
+ placeholderWrapper = function (e) {
688
+ this.classList.remove('medium-editor-placeholder');
689
+ if (e.type !== 'keypress') {
690
+ activatePlaceholder(this);
691
+ }
692
+ };
693
+ for (i = 0; i < this.elements.length; i += 1) {
694
+ activatePlaceholder(this.elements[i]);
695
+ this.elements[i].addEventListener('blur', placeholderWrapper);
696
+ this.elements[i].addEventListener('keypress', placeholderWrapper);
697
+ }
698
+ return this;
699
+ }
700
+
701
+ };
702
+
703
703
  }(window, document));