Commit 16b3ad011718deeb12d683fef4e899df265fdb92

Authored by Rodrigo Souto
1 parent 580afdd7

[media-panel-improvements] Error handling

app/controllers/my_profile/cms_controller.rb
... ... @@ -323,10 +323,13 @@ class CmsController < MyProfileController
323 323 def media_upload
324 324 parent = check_parent(params[:parent_id])
325 325 if request.post?
326   - @file = UploadedFile.create(:uploaded_data => params[:file], :profile => profile, :parent => parent) unless params[:file] == ''
  326 + begin
  327 + @file = UploadedFile.create!(:uploaded_data => params[:file], :profile => profile, :parent => parent) unless params[:file] == ''
  328 + @file = FilePresenter.for(@file)
  329 + rescue Exception => exception
  330 + render :text => exception.to_s, :status => :bad_request
  331 + end
327 332 end
328   - @file = FilePresenter.for(@file)
329   - #render :text => article_list_to_json([file]), :content_type => 'text/plain'
330 333 end
331 334  
332 335 def published_media_items
... ...
app/views/cms/_text_editor_sidebar.html.erb
... ... @@ -30,7 +30,7 @@
30 30 </div>
31 31  
32 32 <script id="template-upload" type="text/x-tmpl">
33   - <div class="upload">
  33 + <div id="{%= S(o.name).slugify().s %}" class="upload">
34 34 {%=o.name%}<span class="percentage"></span>
35 35 <div class="progress"><div class="bar" style="width: 0%;"></div></div>
36 36 </div>
... ...
app/views/cms/media_upload.js.erb
1   -<% if @file.valid? %>
2   - <% klass = @file.class.name.split('::').last.to_css_class %>
3   - jQuery("#published-media .items .<%= klass.pluralize %> .section-title").after("<%= j render :partial => "cms/media_panel/#{klass}" %>");
4   - jQuery("#published-media .items .<%= klass.pluralize %>").show();
5   -<% else %>
6   - alert("Failed to upload file: <%= j @file.errors.full_messages.join(', ').html_safe %>");
7   -<% end %>
  1 +<% klass = @file.class.name.split('::').last.to_css_class %>
  2 +jQuery("#published-media .items .<%= klass.pluralize %> .section-title").after("<%= j render :partial => "cms/media_panel/#{klass}" %>");
  3 +jQuery("#published-media .items .<%= klass.pluralize %>").show();
... ...
app/views/layouts/_javascript.html.erb
... ... @@ -3,7 +3,7 @@
3 3 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'colorbox',
4 4 'jquery-ui-1.10.4/js/jquery-ui-1.10.4.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery-validation/jquery.validate',
5 5 'jquery.cookie', 'jquery.ba-bbq.min.js', 'reflection', 'jquery.tokeninput', 'jquery.typewatch', 'jquery.textchange',
6   -'add-and-join', 'report-abuse', 'catalog', 'manage-products', 'autogrow',
  6 +'add-and-join', 'report-abuse', 'catalog', 'manage-products', 'autogrow', 'string.js',
7 7 'jquery-timepicker-addon/dist/jquery-ui-timepicker-addon', 'application.js', 'rails.js', :cache => 'cache/application' %>
8 8  
9 9 <% language = FastGettext.locale %>
... ...
public/javascripts/media-panel.js
... ... @@ -10,6 +10,11 @@ jQuery(&#39;#file&#39;).fileupload({
10 10 data.context.find('.bar').css('width', progress + '%');
11 11 data.context.find('.percentage').text(progress + '%');
12 12 }
  13 + },
  14 + fail: function(e, data){
  15 + var file_id = '#'+S(data.files[0].name).slugify().s;
  16 + jQuery(file_id).find('.progress .bar').addClass('error');
  17 + jQuery(file_id).append("<div class='error-message'>" + data.jqXHR.responseText + "</div>")
13 18 }
14 19 });
15 20  
... ...
public/javascripts/string.js 0 → 100644
... ... @@ -0,0 +1,987 @@
  1 +/*
  2 +string.js - Copyright (C) 2012-2014, JP Richardson <jprichardson@gmail.com>
  3 +*/
  4 +
  5 +!(function() {
  6 + "use strict";
  7 +
  8 + var VERSION = '1.9.0';
  9 +
  10 + var ENTITIES = {};
  11 +
  12 +//******************************************************************************
  13 +// Added an initialize function which is essentially the code from the S
  14 +// constructor. Now, the S constructor calls this and a new method named
  15 +// setValue calls it as well. The setValue function allows constructors for
  16 +// modules that extend string.js to set the initial value of an object without
  17 +// knowing the internal workings of string.js.
  18 +//
  19 +// Also, all methods which return a new S object now call:
  20 +//
  21 +// return new this.constructor(s);
  22 +//
  23 +// instead of:
  24 +//
  25 +// return new S(s);
  26 +//
  27 +// This allows extended objects to keep their proper instanceOf and constructor.
  28 +//******************************************************************************
  29 +
  30 + function initialize (object, s) {
  31 + if (s !== null && s !== undefined) {
  32 + if (typeof s === 'string')
  33 + object.s = s;
  34 + else
  35 + object.s = s.toString();
  36 + } else {
  37 + object.s = s; //null or undefined
  38 + }
  39 +
  40 + object.orig = s; //original object, currently only used by toCSV() and toBoolean()
  41 +
  42 + if (s !== null && s !== undefined) {
  43 + if (object.__defineGetter__) {
  44 + object.__defineGetter__('length', function() {
  45 + return object.s.length;
  46 + })
  47 + } else {
  48 + object.length = s.length;
  49 + }
  50 + } else {
  51 + object.length = -1;
  52 + }
  53 + }
  54 +
  55 + function S(s) {
  56 + initialize(this, s);
  57 + }
  58 +
  59 + var __nsp = String.prototype;
  60 + var __sp = S.prototype = {
  61 +
  62 + between: function(left, right) {
  63 + var s = this.s;
  64 + var startPos = s.indexOf(left);
  65 + var endPos = s.indexOf(right, startPos + left.length);
  66 + if (endPos == -1 && right != null)
  67 + return new this.constructor('')
  68 + else if (endPos == -1 && right == null)
  69 + return new this.constructor(s.substring(startPos + left.length))
  70 + else
  71 + return new this.constructor(s.slice(startPos + left.length, endPos));
  72 + },
  73 +
  74 + //# modified slightly from https://github.com/epeli/underscore.string
  75 + camelize: function() {
  76 + var s = this.trim().s.replace(/(\-|_|\s)+(.)?/g, function(mathc, sep, c) {
  77 + return (c ? c.toUpperCase() : '');
  78 + });
  79 + return new this.constructor(s);
  80 + },
  81 +
  82 + capitalize: function() {
  83 + return new this.constructor(this.s.substr(0, 1).toUpperCase() + this.s.substring(1).toLowerCase());
  84 + },
  85 +
  86 + charAt: function(index) {
  87 + return this.s.charAt(index);
  88 + },
  89 +
  90 + chompLeft: function(prefix) {
  91 + var s = this.s;
  92 + if (s.indexOf(prefix) === 0) {
  93 + s = s.slice(prefix.length);
  94 + return new this.constructor(s);
  95 + } else {
  96 + return this;
  97 + }
  98 + },
  99 +
  100 + chompRight: function(suffix) {
  101 + if (this.endsWith(suffix)) {
  102 + var s = this.s;
  103 + s = s.slice(0, s.length - suffix.length);
  104 + return new this.constructor(s);
  105 + } else {
  106 + return this;
  107 + }
  108 + },
  109 +
  110 + //#thanks Google
  111 + collapseWhitespace: function() {
  112 + var s = this.s.replace(/[\s\xa0]+/g, ' ').replace(/^\s+|\s+$/g, '');
  113 + return new this.constructor(s);
  114 + },
  115 +
  116 + contains: function(ss) {
  117 + return this.s.indexOf(ss) >= 0;
  118 + },
  119 +
  120 + count: function(ss) {
  121 + var count = 0
  122 + , pos = this.s.indexOf(ss)
  123 +
  124 + while (pos >= 0) {
  125 + count += 1
  126 + pos = this.s.indexOf(ss, pos + 1)
  127 + }
  128 +
  129 + return count
  130 + },
  131 +
  132 + //#modified from https://github.com/epeli/underscore.string
  133 + dasherize: function() {
  134 + var s = this.trim().s.replace(/[_\s]+/g, '-').replace(/([A-Z])/g, '-$1').replace(/-+/g, '-').toLowerCase();
  135 + return new this.constructor(s);
  136 + },
  137 +
  138 + decodeHtmlEntities: function() { //https://github.com/substack/node-ent/blob/master/index.js
  139 + var s = this.s;
  140 + s = s.replace(/&#(\d+);?/g, function (_, code) {
  141 + return String.fromCharCode(code);
  142 + })
  143 + .replace(/&#[xX]([A-Fa-f0-9]+);?/g, function (_, hex) {
  144 + return String.fromCharCode(parseInt(hex, 16));
  145 + })
  146 + .replace(/&([^;\W]+;?)/g, function (m, e) {
  147 + var ee = e.replace(/;$/, '');
  148 + var target = ENTITIES[e] || (e.match(/;$/) && ENTITIES[ee]);
  149 +
  150 + if (typeof target === 'number') {
  151 + return String.fromCharCode(target);
  152 + }
  153 + else if (typeof target === 'string') {
  154 + return target;
  155 + }
  156 + else {
  157 + return m;
  158 + }
  159 + })
  160 +
  161 + return new this.constructor(s);
  162 + },
  163 +
  164 + endsWith: function(suffix) {
  165 + var l = this.s.length - suffix.length;
  166 + return l >= 0 && this.s.indexOf(suffix, l) === l;
  167 + },
  168 +
  169 + escapeHTML: function() { //from underscore.string
  170 + return new this.constructor(this.s.replace(/[&<>"']/g, function(m){ return '&' + reversedEscapeChars[m] + ';'; }));
  171 + },
  172 +
  173 + ensureLeft: function(prefix) {
  174 + var s = this.s;
  175 + if (s.indexOf(prefix) === 0) {
  176 + return this;
  177 + } else {
  178 + return new this.constructor(prefix + s);
  179 + }
  180 + },
  181 +
  182 + ensureRight: function(suffix) {
  183 + var s = this.s;
  184 + if (this.endsWith(suffix)) {
  185 + return this;
  186 + } else {
  187 + return new this.constructor(s + suffix);
  188 + }
  189 + },
  190 +
  191 + humanize: function() { //modified from underscore.string
  192 + if (this.s === null || this.s === undefined)
  193 + return new this.constructor('')
  194 + var s = this.underscore().replace(/_id$/,'').replace(/_/g, ' ').trim().capitalize()
  195 + return new this.constructor(s)
  196 + },
  197 +
  198 + isAlpha: function() {
  199 + return !/[^a-z\xC0-\xFF]/.test(this.s.toLowerCase());
  200 + },
  201 +
  202 + isAlphaNumeric: function() {
  203 + return !/[^0-9a-z\xC0-\xFF]/.test(this.s.toLowerCase());
  204 + },
  205 +
  206 + isEmpty: function() {
  207 + return this.s === null || this.s === undefined ? true : /^[\s\xa0]*$/.test(this.s);
  208 + },
  209 +
  210 + isLower: function() {
  211 + return this.isAlpha() && this.s.toLowerCase() === this.s;
  212 + },
  213 +
  214 + isNumeric: function() {
  215 + return !/[^0-9]/.test(this.s);
  216 + },
  217 +
  218 + isUpper: function() {
  219 + return this.isAlpha() && this.s.toUpperCase() === this.s;
  220 + },
  221 +
  222 + left: function(N) {
  223 + if (N >= 0) {
  224 + var s = this.s.substr(0, N);
  225 + return new this.constructor(s);
  226 + } else {
  227 + return this.right(-N);
  228 + }
  229 + },
  230 +
  231 + lines: function() { //convert windows newlines to unix newlines then convert to an Array of lines
  232 + return this.replaceAll('\r\n', '\n').s.split('\n');
  233 + },
  234 +
  235 + pad: function(len, ch) { //https://github.com/component/pad
  236 + if (ch == null) ch = ' ';
  237 + if (this.s.length >= len) return new this.constructor(this.s);
  238 + len = len - this.s.length;
  239 + var left = Array(Math.ceil(len / 2) + 1).join(ch);
  240 + var right = Array(Math.floor(len / 2) + 1).join(ch);
  241 + return new this.constructor(left + this.s + right);
  242 + },
  243 +
  244 + padLeft: function(len, ch) { //https://github.com/component/pad
  245 + if (ch == null) ch = ' ';
  246 + if (this.s.length >= len) return new this.constructor(this.s);
  247 + return new this.constructor(Array(len - this.s.length + 1).join(ch) + this.s);
  248 + },
  249 +
  250 + padRight: function(len, ch) { //https://github.com/component/pad
  251 + if (ch == null) ch = ' ';
  252 + if (this.s.length >= len) return new this.constructor(this.s);
  253 + return new this.constructor(this.s + Array(len - this.s.length + 1).join(ch));
  254 + },
  255 +
  256 + parseCSV: function(delimiter, qualifier, escape, lineDelimiter) { //try to parse no matter what
  257 + delimiter = delimiter || ',';
  258 + escape = escape || '\\'
  259 + if (typeof qualifier == 'undefined')
  260 + qualifier = '"';
  261 +
  262 + var i = 0, fieldBuffer = [], fields = [], len = this.s.length, inField = false, self = this;
  263 + var ca = function(i){return self.s.charAt(i)};
  264 + if (typeof lineDelimiter !== 'undefined') var rows = [];
  265 +
  266 + if (!qualifier)
  267 + inField = true;
  268 +
  269 + while (i < len) {
  270 + var current = ca(i);
  271 + switch (current) {
  272 + case escape:
  273 + //fix for issues #32 and #35
  274 + if (inField && ((escape !== qualifier) || ca(i+1) === qualifier)) {
  275 + i += 1;
  276 + fieldBuffer.push(ca(i));
  277 + break;
  278 + }
  279 + if (escape !== qualifier) break;
  280 + case qualifier:
  281 + inField = !inField;
  282 + break;
  283 + case delimiter:
  284 + if (inField && qualifier)
  285 + fieldBuffer.push(current);
  286 + else {
  287 + fields.push(fieldBuffer.join(''))
  288 + fieldBuffer.length = 0;
  289 + }
  290 + break;
  291 + case lineDelimiter:
  292 + if (inField) {
  293 + fieldBuffer.push(current);
  294 + } else {
  295 + if (rows) {
  296 + fields.push(fieldBuffer.join(''))
  297 + rows.push(fields);
  298 + fields = [];
  299 + fieldBuffer.length = 0;
  300 + }
  301 + }
  302 + break;
  303 + default:
  304 + if (inField)
  305 + fieldBuffer.push(current);
  306 + break;
  307 + }
  308 + i += 1;
  309 + }
  310 +
  311 + fields.push(fieldBuffer.join(''));
  312 + if (rows) {
  313 + rows.push(fields);
  314 + return rows;
  315 + }
  316 + return fields;
  317 + },
  318 +
  319 + replaceAll: function(ss, r) {
  320 + //var s = this.s.replace(new RegExp(ss, 'g'), r);
  321 + var s = this.s.split(ss).join(r)
  322 + return new this.constructor(s);
  323 + },
  324 +
  325 + right: function(N) {
  326 + if (N >= 0) {
  327 + var s = this.s.substr(this.s.length - N, N);
  328 + return new this.constructor(s);
  329 + } else {
  330 + return this.left(-N);
  331 + }
  332 + },
  333 +
  334 + setValue: function (s) {
  335 + initialize(this, s);
  336 + return this;
  337 + },
  338 +
  339 + slugify: function() {
  340 + var sl = (new S(this.s.replace(/[^\w\s-]/g, '').toLowerCase())).dasherize().s;
  341 + if (sl.charAt(0) === '-')
  342 + sl = sl.substr(1);
  343 + return new this.constructor(sl);
  344 + },
  345 +
  346 + startsWith: function(prefix) {
  347 + return this.s.lastIndexOf(prefix, 0) === 0;
  348 + },
  349 +
  350 + stripPunctuation: function() {
  351 + //return new this.constructor(this.s.replace(/[\.,-\/#!$%\^&\*;:{}=\-_`~()]/g,""));
  352 + return new this.constructor(this.s.replace(/[^\w\s]|_/g, "").replace(/\s+/g, " "));
  353 + },
  354 +
  355 + stripTags: function() { //from sugar.js
  356 + var s = this.s, args = arguments.length > 0 ? arguments : [''];
  357 + multiArgs(args, function(tag) {
  358 + s = s.replace(RegExp('<\/?' + tag + '[^<>]*>', 'gi'), '');
  359 + });
  360 + return new this.constructor(s);
  361 + },
  362 +
  363 + template: function(values, opening, closing) {
  364 + var s = this.s
  365 + var opening = opening || Export.TMPL_OPEN
  366 + var closing = closing || Export.TMPL_CLOSE
  367 +
  368 + var open = opening.replace(/[-[\]()*\s]/g, "\\$&").replace(/\$/g, '\\$')
  369 + var close = closing.replace(/[-[\]()*\s]/g, "\\$&").replace(/\$/g, '\\$')
  370 + var r = new RegExp(open + '(.+?)' + close, 'g')
  371 + //, r = /\{\{(.+?)\}\}/g
  372 + var matches = s.match(r) || [];
  373 +
  374 + matches.forEach(function(match) {
  375 + var key = match.substring(opening.length, match.length - closing.length);//chop {{ and }}
  376 + if (typeof values[key] != 'undefined')
  377 + s = s.replace(match, values[key]);
  378 + });
  379 + return new this.constructor(s);
  380 + },
  381 +
  382 + times: function(n) {
  383 + return new this.constructor(new Array(n + 1).join(this.s));
  384 + },
  385 +
  386 + toBoolean: function() {
  387 + if (typeof this.orig === 'string') {
  388 + var s = this.s.toLowerCase();
  389 + return s === 'true' || s === 'yes' || s === 'on' || s === '1';
  390 + } else
  391 + return this.orig === true || this.orig === 1;
  392 + },
  393 +
  394 + toFloat: function(precision) {
  395 + var num = parseFloat(this.s)
  396 + if (precision)
  397 + return parseFloat(num.toFixed(precision))
  398 + else
  399 + return num
  400 + },
  401 +
  402 + toInt: function() { //thanks Google
  403 + // If the string starts with '0x' or '-0x', parse as hex.
  404 + return /^\s*-?0x/i.test(this.s) ? parseInt(this.s, 16) : parseInt(this.s, 10)
  405 + },
  406 +
  407 + trim: function() {
  408 + var s;
  409 + if (typeof __nsp.trim === 'undefined')
  410 + s = this.s.replace(/(^\s*|\s*$)/g, '')
  411 + else
  412 + s = this.s.trim()
  413 + return new this.constructor(s);
  414 + },
  415 +
  416 + trimLeft: function() {
  417 + var s;
  418 + if (__nsp.trimLeft)
  419 + s = this.s.trimLeft();
  420 + else
  421 + s = this.s.replace(/(^\s*)/g, '');
  422 + return new this.constructor(s);
  423 + },
  424 +
  425 + trimRight: function() {
  426 + var s;
  427 + if (__nsp.trimRight)
  428 + s = this.s.trimRight();
  429 + else
  430 + s = this.s.replace(/\s+$/, '');
  431 + return new this.constructor(s);
  432 + },
  433 +
  434 + truncate: function(length, pruneStr) { //from underscore.string, author: github.com/rwz
  435 + var str = this.s;
  436 +
  437 + length = ~~length;
  438 + pruneStr = pruneStr || '...';
  439 +
  440 + if (str.length <= length) return new this.constructor(str);
  441 +
  442 + var tmpl = function(c){ return c.toUpperCase() !== c.toLowerCase() ? 'A' : ' '; },
  443 + template = str.slice(0, length+1).replace(/.(?=\W*\w*$)/g, tmpl); // 'Hello, world' -> 'HellAA AAAAA'
  444 +
  445 + if (template.slice(template.length-2).match(/\w\w/))
  446 + template = template.replace(/\s*\S+$/, '');
  447 + else
  448 + template = new S(template.slice(0, template.length-1)).trimRight().s;
  449 +
  450 + return (template+pruneStr).length > str.length ? new S(str) : new S(str.slice(0, template.length)+pruneStr);
  451 + },
  452 +
  453 + toCSV: function() {
  454 + var delim = ',', qualifier = '"', escape = '\\', encloseNumbers = true, keys = false;
  455 + var dataArray = [];
  456 +
  457 + function hasVal(it) {
  458 + return it !== null && it !== '';
  459 + }
  460 +
  461 + if (typeof arguments[0] === 'object') {
  462 + delim = arguments[0].delimiter || delim;
  463 + delim = arguments[0].separator || delim;
  464 + qualifier = arguments[0].qualifier || qualifier;
  465 + encloseNumbers = !!arguments[0].encloseNumbers;
  466 + escape = arguments[0].escape || escape;
  467 + keys = !!arguments[0].keys;
  468 + } else if (typeof arguments[0] === 'string') {
  469 + delim = arguments[0];
  470 + }
  471 +
  472 + if (typeof arguments[1] === 'string')
  473 + qualifier = arguments[1];
  474 +
  475 + if (arguments[1] === null)
  476 + qualifier = null;
  477 +
  478 + if (this.orig instanceof Array)
  479 + dataArray = this.orig;
  480 + else { //object
  481 + for (var key in this.orig)
  482 + if (this.orig.hasOwnProperty(key))
  483 + if (keys)
  484 + dataArray.push(key);
  485 + else
  486 + dataArray.push(this.orig[key]);
  487 + }
  488 +
  489 + var rep = escape + qualifier;
  490 + var buildString = [];
  491 + for (var i = 0; i < dataArray.length; ++i) {
  492 + var shouldQualify = hasVal(qualifier)
  493 + if (typeof dataArray[i] == 'number')
  494 + shouldQualify &= encloseNumbers;
  495 +
  496 + if (shouldQualify)
  497 + buildString.push(qualifier);
  498 +
  499 + if (dataArray[i] !== null && dataArray[i] !== undefined) {
  500 + var d = new S(dataArray[i]).replaceAll(qualifier, rep).s;
  501 + buildString.push(d);
  502 + } else
  503 + buildString.push('')
  504 +
  505 + if (shouldQualify)
  506 + buildString.push(qualifier);
  507 +
  508 + if (delim)
  509 + buildString.push(delim);
  510 + }
  511 +
  512 + //chop last delim
  513 + //console.log(buildString.length)
  514 + buildString.length = buildString.length - 1;
  515 + return new this.constructor(buildString.join(''));
  516 + },
  517 +
  518 + toString: function() {
  519 + return this.s;
  520 + },
  521 +
  522 + //#modified from https://github.com/epeli/underscore.string
  523 + underscore: function() {
  524 + var s = this.trim().s.replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/[-\s]+/g, '_').toLowerCase();
  525 + if ((new S(this.s.charAt(0))).isUpper()) {
  526 + s = '_' + s;
  527 + }
  528 + return new this.constructor(s);
  529 + },
  530 +
  531 + unescapeHTML: function() { //from underscore.string
  532 + return new this.constructor(this.s.replace(/\&([^;]+);/g, function(entity, entityCode){
  533 + var match;
  534 +
  535 + if (entityCode in escapeChars) {
  536 + return escapeChars[entityCode];
  537 + } else if (match = entityCode.match(/^#x([\da-fA-F]+)$/)) {
  538 + return String.fromCharCode(parseInt(match[1], 16));
  539 + } else if (match = entityCode.match(/^#(\d+)$/)) {
  540 + return String.fromCharCode(~~match[1]);
  541 + } else {
  542 + return entity;
  543 + }
  544 + }));
  545 + },
  546 +
  547 + valueOf: function() {
  548 + return this.s.valueOf();
  549 + },
  550 +
  551 + //#Added a New Function called wrapHTML.
  552 + wrapHTML: function (tagName, tagAttrs) {
  553 + var s = this.s, el = (tagName == null) ? 'span' : tagName, elAttr = '', wrapped = '';
  554 + if(typeof tagAttrs == 'object') for(var prop in tagAttrs) elAttr += ' ' + prop + '="' + tagAttrs[prop] + '"';
  555 + s = wrapped.concat('<', el, elAttr, '>', this, '</', el, '>');
  556 + return new this.constructor(s);
  557 + }
  558 + }
  559 +
  560 + var methodsAdded = [];
  561 + function extendPrototype() {
  562 + for (var name in __sp) {
  563 + (function(name){
  564 + var func = __sp[name];
  565 + if (!__nsp.hasOwnProperty(name)) {
  566 + methodsAdded.push(name);
  567 + __nsp[name] = function() {
  568 + String.prototype.s = this;
  569 + return func.apply(this, arguments);
  570 + }
  571 + }
  572 + })(name);
  573 + }
  574 + }
  575 +
  576 + function restorePrototype() {
  577 + for (var i = 0; i < methodsAdded.length; ++i)
  578 + delete String.prototype[methodsAdded[i]];
  579 + methodsAdded.length = 0;
  580 + }
  581 +
  582 +
  583 +/*************************************
  584 +/* Attach Native JavaScript String Properties
  585 +/*************************************/
  586 +
  587 + var nativeProperties = getNativeStringProperties();
  588 + for (var name in nativeProperties) {
  589 + (function(name) {
  590 + var stringProp = __nsp[name];
  591 + if (typeof stringProp == 'function') {
  592 + //console.log(stringProp)
  593 + if (!__sp[name]) {
  594 + if (nativeProperties[name] === 'string') {
  595 + __sp[name] = function() {
  596 + //console.log(name)
  597 + return new this.constructor(stringProp.apply(this, arguments));
  598 + }
  599 + } else {
  600 + __sp[name] = stringProp;
  601 + }
  602 + }
  603 + }
  604 + })(name);
  605 + }
  606 +
  607 +
  608 +/*************************************
  609 +/* Function Aliases
  610 +/*************************************/
  611 +
  612 + __sp.repeat = __sp.times;
  613 + __sp.include = __sp.contains;
  614 + __sp.toInteger = __sp.toInt;
  615 + __sp.toBool = __sp.toBoolean;
  616 + __sp.decodeHTMLEntities = __sp.decodeHtmlEntities //ensure consistent casing scheme of 'HTML'
  617 +
  618 +
  619 +//******************************************************************************
  620 +// Set the constructor. Without this, string.js objects are instances of
  621 +// Object instead of S.
  622 +//******************************************************************************
  623 +
  624 + __sp.constructor = S;
  625 +
  626 +
  627 +/*************************************
  628 +/* Private Functions
  629 +/*************************************/
  630 +
  631 + function getNativeStringProperties() {
  632 + var names = getNativeStringPropertyNames();
  633 + var retObj = {};
  634 +
  635 + for (var i = 0; i < names.length; ++i) {
  636 + var name = names[i];
  637 + var func = __nsp[name];
  638 + try {
  639 + var type = typeof func.apply('teststring', []);
  640 + retObj[name] = type;
  641 + } catch (e) {}
  642 + }
  643 + return retObj;
  644 + }
  645 +
  646 + function getNativeStringPropertyNames() {
  647 + var results = [];
  648 + if (Object.getOwnPropertyNames) {
  649 + results = Object.getOwnPropertyNames(__nsp);
  650 + results.splice(results.indexOf('valueOf'), 1);
  651 + results.splice(results.indexOf('toString'), 1);
  652 + return results;
  653 + } else { //meant for legacy cruft, this could probably be made more efficient
  654 + var stringNames = {};
  655 + var objectNames = [];
  656 + for (var name in String.prototype)
  657 + stringNames[name] = name;
  658 +
  659 + for (var name in Object.prototype)
  660 + delete stringNames[name];
  661 +
  662 + //stringNames['toString'] = 'toString'; //this was deleted with the rest of the object names
  663 + for (var name in stringNames) {
  664 + results.push(name);
  665 + }
  666 + return results;
  667 + }
  668 + }
  669 +
  670 + function Export(str) {
  671 + return new S(str);
  672 + };
  673 +
  674 + //attach exports to StringJSWrapper
  675 + Export.extendPrototype = extendPrototype;
  676 + Export.restorePrototype = restorePrototype;
  677 + Export.VERSION = VERSION;
  678 + Export.TMPL_OPEN = '{{';
  679 + Export.TMPL_CLOSE = '}}';
  680 + Export.ENTITIES = ENTITIES;
  681 +
  682 +
  683 +
  684 +/*************************************
  685 +/* Exports
  686 +/*************************************/
  687 +
  688 + if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
  689 + module.exports = Export;
  690 +
  691 + } else {
  692 +
  693 + if(typeof define === "function" && define.amd) {
  694 + define([], function() {
  695 + return Export;
  696 + });
  697 + } else {
  698 + window.S = Export;
  699 + }
  700 + }
  701 +
  702 +
  703 +/*************************************
  704 +/* 3rd Party Private Functions
  705 +/*************************************/
  706 +
  707 + //from sugar.js
  708 + function multiArgs(args, fn) {
  709 + var result = [], i;
  710 + for(i = 0; i < args.length; i++) {
  711 + result.push(args[i]);
  712 + if(fn) fn.call(args, args[i], i);
  713 + }
  714 + return result;
  715 + }
  716 +
  717 + //from underscore.string
  718 + var escapeChars = {
  719 + lt: '<',
  720 + gt: '>',
  721 + quot: '"',
  722 + apos: "'",
  723 + amp: '&'
  724 + };
  725 +
  726 + //from underscore.string
  727 + var reversedEscapeChars = {};
  728 + for(var key in escapeChars){ reversedEscapeChars[escapeChars[key]] = key; }
  729 +
  730 + ENTITIES = {
  731 + "amp" : "&",
  732 + "gt" : ">",
  733 + "lt" : "<",
  734 + "quot" : "\"",
  735 + "apos" : "'",
  736 + "AElig" : 198,
  737 + "Aacute" : 193,
  738 + "Acirc" : 194,
  739 + "Agrave" : 192,
  740 + "Aring" : 197,
  741 + "Atilde" : 195,
  742 + "Auml" : 196,
  743 + "Ccedil" : 199,
  744 + "ETH" : 208,
  745 + "Eacute" : 201,
  746 + "Ecirc" : 202,
  747 + "Egrave" : 200,
  748 + "Euml" : 203,
  749 + "Iacute" : 205,
  750 + "Icirc" : 206,
  751 + "Igrave" : 204,
  752 + "Iuml" : 207,
  753 + "Ntilde" : 209,
  754 + "Oacute" : 211,
  755 + "Ocirc" : 212,
  756 + "Ograve" : 210,
  757 + "Oslash" : 216,
  758 + "Otilde" : 213,
  759 + "Ouml" : 214,
  760 + "THORN" : 222,
  761 + "Uacute" : 218,
  762 + "Ucirc" : 219,
  763 + "Ugrave" : 217,
  764 + "Uuml" : 220,
  765 + "Yacute" : 221,
  766 + "aacute" : 225,
  767 + "acirc" : 226,
  768 + "aelig" : 230,
  769 + "agrave" : 224,
  770 + "aring" : 229,
  771 + "atilde" : 227,
  772 + "auml" : 228,
  773 + "ccedil" : 231,
  774 + "eacute" : 233,
  775 + "ecirc" : 234,
  776 + "egrave" : 232,
  777 + "eth" : 240,
  778 + "euml" : 235,
  779 + "iacute" : 237,
  780 + "icirc" : 238,
  781 + "igrave" : 236,
  782 + "iuml" : 239,
  783 + "ntilde" : 241,
  784 + "oacute" : 243,
  785 + "ocirc" : 244,
  786 + "ograve" : 242,
  787 + "oslash" : 248,
  788 + "otilde" : 245,
  789 + "ouml" : 246,
  790 + "szlig" : 223,
  791 + "thorn" : 254,
  792 + "uacute" : 250,
  793 + "ucirc" : 251,
  794 + "ugrave" : 249,
  795 + "uuml" : 252,
  796 + "yacute" : 253,
  797 + "yuml" : 255,
  798 + "copy" : 169,
  799 + "reg" : 174,
  800 + "nbsp" : 160,
  801 + "iexcl" : 161,
  802 + "cent" : 162,
  803 + "pound" : 163,
  804 + "curren" : 164,
  805 + "yen" : 165,
  806 + "brvbar" : 166,
  807 + "sect" : 167,
  808 + "uml" : 168,
  809 + "ordf" : 170,
  810 + "laquo" : 171,
  811 + "not" : 172,
  812 + "shy" : 173,
  813 + "macr" : 175,
  814 + "deg" : 176,
  815 + "plusmn" : 177,
  816 + "sup1" : 185,
  817 + "sup2" : 178,
  818 + "sup3" : 179,
  819 + "acute" : 180,
  820 + "micro" : 181,
  821 + "para" : 182,
  822 + "middot" : 183,
  823 + "cedil" : 184,
  824 + "ordm" : 186,
  825 + "raquo" : 187,
  826 + "frac14" : 188,
  827 + "frac12" : 189,
  828 + "frac34" : 190,
  829 + "iquest" : 191,
  830 + "times" : 215,
  831 + "divide" : 247,
  832 + "OElig;" : 338,
  833 + "oelig;" : 339,
  834 + "Scaron;" : 352,
  835 + "scaron;" : 353,
  836 + "Yuml;" : 376,
  837 + "fnof;" : 402,
  838 + "circ;" : 710,
  839 + "tilde;" : 732,
  840 + "Alpha;" : 913,
  841 + "Beta;" : 914,
  842 + "Gamma;" : 915,
  843 + "Delta;" : 916,
  844 + "Epsilon;" : 917,
  845 + "Zeta;" : 918,
  846 + "Eta;" : 919,
  847 + "Theta;" : 920,
  848 + "Iota;" : 921,
  849 + "Kappa;" : 922,
  850 + "Lambda;" : 923,
  851 + "Mu;" : 924,
  852 + "Nu;" : 925,
  853 + "Xi;" : 926,
  854 + "Omicron;" : 927,
  855 + "Pi;" : 928,
  856 + "Rho;" : 929,
  857 + "Sigma;" : 931,
  858 + "Tau;" : 932,
  859 + "Upsilon;" : 933,
  860 + "Phi;" : 934,
  861 + "Chi;" : 935,
  862 + "Psi;" : 936,
  863 + "Omega;" : 937,
  864 + "alpha;" : 945,
  865 + "beta;" : 946,
  866 + "gamma;" : 947,
  867 + "delta;" : 948,
  868 + "epsilon;" : 949,
  869 + "zeta;" : 950,
  870 + "eta;" : 951,
  871 + "theta;" : 952,
  872 + "iota;" : 953,
  873 + "kappa;" : 954,
  874 + "lambda;" : 955,
  875 + "mu;" : 956,
  876 + "nu;" : 957,
  877 + "xi;" : 958,
  878 + "omicron;" : 959,
  879 + "pi;" : 960,
  880 + "rho;" : 961,
  881 + "sigmaf;" : 962,
  882 + "sigma;" : 963,
  883 + "tau;" : 964,
  884 + "upsilon;" : 965,
  885 + "phi;" : 966,
  886 + "chi;" : 967,
  887 + "psi;" : 968,
  888 + "omega;" : 969,
  889 + "thetasym;" : 977,
  890 + "upsih;" : 978,
  891 + "piv;" : 982,
  892 + "ensp;" : 8194,
  893 + "emsp;" : 8195,
  894 + "thinsp;" : 8201,
  895 + "zwnj;" : 8204,
  896 + "zwj;" : 8205,
  897 + "lrm;" : 8206,
  898 + "rlm;" : 8207,
  899 + "ndash;" : 8211,
  900 + "mdash;" : 8212,
  901 + "lsquo;" : 8216,
  902 + "rsquo;" : 8217,
  903 + "sbquo;" : 8218,
  904 + "ldquo;" : 8220,
  905 + "rdquo;" : 8221,
  906 + "bdquo;" : 8222,
  907 + "dagger;" : 8224,
  908 + "Dagger;" : 8225,
  909 + "bull;" : 8226,
  910 + "hellip;" : 8230,
  911 + "permil;" : 8240,
  912 + "prime;" : 8242,
  913 + "Prime;" : 8243,
  914 + "lsaquo;" : 8249,
  915 + "rsaquo;" : 8250,
  916 + "oline;" : 8254,
  917 + "frasl;" : 8260,
  918 + "euro;" : 8364,
  919 + "image;" : 8465,
  920 + "weierp;" : 8472,
  921 + "real;" : 8476,
  922 + "trade;" : 8482,
  923 + "alefsym;" : 8501,
  924 + "larr;" : 8592,
  925 + "uarr;" : 8593,
  926 + "rarr;" : 8594,
  927 + "darr;" : 8595,
  928 + "harr;" : 8596,
  929 + "crarr;" : 8629,
  930 + "lArr;" : 8656,
  931 + "uArr;" : 8657,
  932 + "rArr;" : 8658,
  933 + "dArr;" : 8659,
  934 + "hArr;" : 8660,
  935 + "forall;" : 8704,
  936 + "part;" : 8706,
  937 + "exist;" : 8707,
  938 + "empty;" : 8709,
  939 + "nabla;" : 8711,
  940 + "isin;" : 8712,
  941 + "notin;" : 8713,
  942 + "ni;" : 8715,
  943 + "prod;" : 8719,
  944 + "sum;" : 8721,
  945 + "minus;" : 8722,
  946 + "lowast;" : 8727,
  947 + "radic;" : 8730,
  948 + "prop;" : 8733,
  949 + "infin;" : 8734,
  950 + "ang;" : 8736,
  951 + "and;" : 8743,
  952 + "or;" : 8744,
  953 + "cap;" : 8745,
  954 + "cup;" : 8746,
  955 + "int;" : 8747,
  956 + "there4;" : 8756,
  957 + "sim;" : 8764,
  958 + "cong;" : 8773,
  959 + "asymp;" : 8776,
  960 + "ne;" : 8800,
  961 + "equiv;" : 8801,
  962 + "le;" : 8804,
  963 + "ge;" : 8805,
  964 + "sub;" : 8834,
  965 + "sup;" : 8835,
  966 + "nsub;" : 8836,
  967 + "sube;" : 8838,
  968 + "supe;" : 8839,
  969 + "oplus;" : 8853,
  970 + "otimes;" : 8855,
  971 + "perp;" : 8869,
  972 + "sdot;" : 8901,
  973 + "lceil;" : 8968,
  974 + "rceil;" : 8969,
  975 + "lfloor;" : 8970,
  976 + "rfloor;" : 8971,
  977 + "lang;" : 9001,
  978 + "rang;" : 9002,
  979 + "loz;" : 9674,
  980 + "spades;" : 9824,
  981 + "clubs;" : 9827,
  982 + "hearts;" : 9829,
  983 + "diams;" : 9830
  984 + }
  985 +
  986 +
  987 +}).call(this);
... ...
public/stylesheets/application.css
... ... @@ -3401,10 +3401,20 @@ div.with_media_panel .formfield input[type=&quot;checkbox&quot;] {
3401 3401 border-radius: 3px;
3402 3402 }
3403 3403  
  3404 +#media-upload-form .bar.error {
  3405 + background: #ef2929;
  3406 +}
  3407 +
3404 3408 #media-upload-form input#file {
3405 3409 width: 100%;
3406 3410 }
3407 3411  
  3412 +#media-upload-form .error-message {
  3413 + color: #a40000;
  3414 + font-size: 10px;
  3415 +
  3416 +}
  3417 +
3408 3418 .text-editor-sidebar {
3409 3419 position: absolute;
3410 3420 width: 280px;
... ...