diff --git a/ghost/admin/components/gh-codemirror.js b/ghost/admin/components/gh-codemirror.js index 6e289344ab..2febdbfc2f 100644 --- a/ghost/admin/components/gh-codemirror.js +++ b/ghost/admin/components/gh-codemirror.js @@ -13,11 +13,11 @@ var onChangeHandler = function (cm, changeObj) { // fill array with a range of numbers for (line = changeObj.from.line; line < changeObj.from.line + changeObj.text.length; line += 1) { - component.checkLine(line, changeObj.origin); + component.checkLine.call(component, line, changeObj.origin); } // Is this a line which may have had a marker on it? - component.checkMarkers(); + component.checkMarkers.call(component); cm.component.set('value', cm.getValue()); @@ -50,7 +50,10 @@ var Codemirror = Ember.TextArea.extend(MarkerManager, { }, afterRenderEvent: function () { - var initMarkers = _.bind(this.initMarkers, this); + var self = this; + function initMarkers () { + self.initMarkers.apply(self, arguments); + } // replaces CodeMirror with TouchEditor only if we're on mobile mobileCodeMirror.createIfMobile(); diff --git a/ghost/admin/components/gh-markdown.js b/ghost/admin/components/gh-markdown.js index 39d6101c20..f949e5d71f 100644 --- a/ghost/admin/components/gh-markdown.js +++ b/ghost/admin/components/gh-markdown.js @@ -15,7 +15,7 @@ var Markdown = Ember.Component.extend({ // fire off 'enable' API function from uploadManager // might need to make sure markdown has been processed first reInitDropzones: function () { - Ember.run.scheduleOnce('afterRender', this, function () { + function handleDropzoneEvents() { var dropzones = $('.js-drop-zone'); uploader.call(dropzones, { @@ -23,11 +23,13 @@ var Markdown = Ember.Component.extend({ fileStorage: this.get('config.fileStorage') }); - dropzones.on('uploadstart', _.bind(this.sendAction, this, 'uploadStarted')); - dropzones.on('uploadfailure', _.bind(this.sendAction, this, 'uploadFinished')); - dropzones.on('uploadsuccess', _.bind(this.sendAction, this, 'uploadFinished')); - dropzones.on('uploadsuccess', _.bind(this.sendAction, this, 'uploadSuccess')); - }); + dropzones.on('uploadstart', Ember.run.bind(this, 'sendAction', 'uploadStarted')); + dropzones.on('uploadfailure', Ember.run.bind(this, 'sendAction', 'uploadFinished')); + dropzones.on('uploadsuccess', Ember.run.bind(this, 'sendAction', 'uploadFinished')); + dropzones.on('uploadsuccess', Ember.run.bind(this, 'sendAction', 'uploadSuccess')); + } + + Ember.run.scheduleOnce('afterRender', this, handleDropzoneEvents); }.observes('markdown') }); diff --git a/ghost/admin/controllers/post-settings-menu.js b/ghost/admin/controllers/post-settings-menu.js index cac3c9ed0c..0808619992 100644 --- a/ghost/admin/controllers/post-settings-menu.js +++ b/ghost/admin/controllers/post-settings-menu.js @@ -2,6 +2,7 @@ import {parseDateString, formatDate} from 'ghost/utils/date-formatting'; import SlugGenerator from 'ghost/models/slug-generator'; import boundOneWay from 'ghost/utils/bound-one-way'; +import isNumber from 'ghost/utils/isNumber'; var PostSettingsMenuController = Ember.ObjectController.extend({ //State for if the user is viewing a tab's pane. @@ -135,7 +136,7 @@ var PostSettingsMenuController = Ember.ObjectController.extend({ el = $('.rendered-markdown'); // Get rendered markdown - if (!_.isUndefined(el) && el.length > 0) { + if (el !== undefined && el.length > 0) { html = el.clone(); html.find('.image-uploader').remove(); html = html[0].innerHTML; @@ -274,7 +275,7 @@ var PostSettingsMenuController = Ember.ObjectController.extend({ // if the candidate slug is the same as the existing slug except // for the incrementor then the existing slug should be used - if (_.isNumber(check) && check > 0) { + if (isNumber(check) && check > 0) { if (slug === slugTokens.join('-') && serverSlug !== newSlug) { self.set('slugValue', slug); diff --git a/ghost/admin/controllers/settings/users/user.js b/ghost/admin/controllers/settings/users/user.js index 95d5214b84..dc641a6235 100644 --- a/ghost/admin/controllers/settings/users/user.js +++ b/ghost/admin/controllers/settings/users/user.js @@ -1,4 +1,5 @@ import SlugGenerator from 'ghost/models/slug-generator'; +import isNumber from 'ghost/utils/isNumber'; var SettingsUserController = Ember.ObjectController.extend({ @@ -207,7 +208,7 @@ var SettingsUserController = Ember.ObjectController.extend({ // if the candidate slug is the same as the existing slug except // for the incrementor then the existing slug should be used - if (_.isNumber(check) && check > 0) { + if (isNumber(check) && check > 0) { if (slug === slugTokens.join('-') && serverSlug !== newSlug) { self.set('slugValue', slug); diff --git a/ghost/admin/mixins/pagination-route.js b/ghost/admin/mixins/pagination-route.js index 874e7a7cc7..192fb15f13 100644 --- a/ghost/admin/mixins/pagination-route.js +++ b/ghost/admin/mixins/pagination-route.js @@ -12,7 +12,13 @@ var PaginationRoute = Ember.Mixin.create({ setupPagination: function (settings) { settings = settings || {}; - settings = _.defaults(settings, defaultPaginationSettings); + for (var key in defaultPaginationSettings) { + if (defaultPaginationSettings.hasOwnProperty(key)) { + if (!settings.hasOwnProperty(key)) { + settings[key] = defaultPaginationSettings[key]; + } + } + } this.set('paginationSettings', settings); this.controller.set('paginationSettings', settings); diff --git a/ghost/admin/models/user.js b/ghost/admin/models/user.js index de05236b82..520576c417 100644 --- a/ghost/admin/models/user.js +++ b/ghost/admin/models/user.js @@ -87,13 +87,15 @@ var User = DS.Model.extend(NProgressSaveMixin, SelectiveSaveMixin, ValidationEng isPasswordValid: Ember.computed.empty('passwordValidationErrors.[]'), - active: Ember.computed('status', function () { - return _.contains(['active', 'warn-1', 'warn-2', 'warn-3', 'warn-4', 'locked'], this.get('status')); - }), - invited: Ember.computed('status', function () { - return _.contains(['invited', 'invited-pending'], this.get('status')); - }), - pending: Ember.computed.equal('status', 'invited-pending') + active: function () { + return ['active', 'warn-1', 'warn-2', 'warn-3', 'warn-4', 'locked'].indexOf(this.get('status')) > -1; + }.property('status'), + + invited: function () { + return ['invited', 'invited-pending'].indexOf(this.get('status')) > -1; + }.property('status'), + + pending: Ember.computed.equal('status', 'invited-pending').property('status') }); export default User; diff --git a/ghost/admin/routes/editor/edit.js b/ghost/admin/routes/editor/edit.js index 208714259b..6d987bd81d 100644 --- a/ghost/admin/routes/editor/edit.js +++ b/ghost/admin/routes/editor/edit.js @@ -1,4 +1,6 @@ import base from 'ghost/mixins/editor-route-base'; +import isNumber from 'ghost/utils/isNumber'; +import isFinite from 'ghost/utils/isFinite'; var EditorEditRoute = Ember.Route.extend(SimpleAuth.AuthenticatedRouteMixin, base, { classNames: ['editor'], @@ -11,7 +13,7 @@ var EditorEditRoute = Ember.Route.extend(SimpleAuth.AuthenticatedRouteMixin, bas postId = Number(params.post_id); - if (!_.isNumber(postId) || !_.isFinite(postId) || postId % 1 !== 0 || postId <= 0) { + if (!isNumber(postId) || !isFinite(postId) || postId % 1 !== 0 || postId <= 0) { return this.transitionTo('error404', 'editor/' + params.post_id); } diff --git a/ghost/admin/routes/posts/post.js b/ghost/admin/routes/posts/post.js index b75f1ee2fa..c493f6c130 100644 --- a/ghost/admin/routes/posts/post.js +++ b/ghost/admin/routes/posts/post.js @@ -1,5 +1,7 @@ import loadingIndicator from 'ghost/mixins/loading-indicator'; import ShortcutsRoute from 'ghost/mixins/shortcuts-route'; +import isNumber from 'ghost/utils/isNumber'; +import isFinite from 'ghost/utils/isFinite'; var PostsPostRoute = Ember.Route.extend(SimpleAuth.AuthenticatedRouteMixin, loadingIndicator, ShortcutsRoute, { model: function (params) { @@ -10,7 +12,7 @@ var PostsPostRoute = Ember.Route.extend(SimpleAuth.AuthenticatedRouteMixin, load postId = Number(params.post_id); - if (!_.isNumber(postId) || !_.isFinite(postId) || postId % 1 !== 0 || postId <= 0) + if (!isNumber(postId) || !isFinite(postId) || postId % 1 !== 0 || postId <= 0) { return this.transitionTo('error404', params.post_id); } diff --git a/ghost/admin/utils/bind.js b/ghost/admin/utils/bind.js new file mode 100644 index 0000000000..ef0fcf6448 --- /dev/null +++ b/ghost/admin/utils/bind.js @@ -0,0 +1,15 @@ +var slice = Array.prototype.slice; + +function bind(/* func, args, thisArg */) { + var args = slice.call(arguments), + func = args.shift(), + thisArg = args.pop(); + + function bound() { + return func.apply(thisArg, args); + } + + return bound; +} + +export default bind; diff --git a/ghost/admin/utils/isFinite.js b/ghost/admin/utils/isFinite.js new file mode 100644 index 0000000000..808669ba5a --- /dev/null +++ b/ghost/admin/utils/isFinite.js @@ -0,0 +1,9 @@ +/* globals window */ + +// isFinite function from lodash + +function isFinite(value) { + return window.isFinite(value) && !window.isNaN(parseFloat(value)); +} + +export default isFinite; diff --git a/ghost/admin/utils/isNumber.js b/ghost/admin/utils/isNumber.js new file mode 100644 index 0000000000..01e7e54722 --- /dev/null +++ b/ghost/admin/utils/isNumber.js @@ -0,0 +1,10 @@ +// isNumber function from lodash + +var toString = Object.prototype.toString; + +function isNumber(value) { + return typeof value === 'number' || + value && typeof value === 'object' && toString.call(value) === '[object Number]' || false; +} + +export default isNumber; diff --git a/ghost/admin/utils/validator-extensions.js b/ghost/admin/utils/validator-extensions.js index 3319034f3f..7da53d14ae 100644 --- a/ghost/admin/utils/validator-extensions.js +++ b/ghost/admin/utils/validator-extensions.js @@ -6,7 +6,7 @@ function init() { }); validator.extend('notContains', function (str, badString) { - return !_.contains(str, badString); + return str.indexOf(badString) === -1; }); } diff --git a/ghost/admin/validators/user.js b/ghost/admin/validators/user.js index 5c386c7e36..2c1fae5777 100644 --- a/ghost/admin/validators/user.js +++ b/ghost/admin/validators/user.js @@ -50,7 +50,7 @@ var UserValidator = Ember.Object.create({ validationErrors.push({ message: 'Location is too long' }); } - if (!_.isEmpty(website) && + if (!Ember.isEmpty(website) && (!validator.isURL(website, { protocols: ['http', 'https'], require_protocol: true }) || !validator.isLength(website, 0, 2000))) { diff --git a/ghost/admin/views/application.js b/ghost/admin/views/application.js index 4f1f5db79c..f9dece9b78 100644 --- a/ghost/admin/views/application.js +++ b/ghost/admin/views/application.js @@ -1,4 +1,5 @@ import mobileQuery from 'ghost/utils/mobile'; +import bind from 'ghost/utils/bind'; var ApplicationView = Ember.View.extend({ elementId: 'container', @@ -43,7 +44,7 @@ var ApplicationView = Ember.View.extend({ }.observes('controller.showGlobalMobileNav'), setupCloseNavOnDesktop: function () { - this.set('closeGlobalMobileNavOnDesktop', _.bind(function closeGlobalMobileNavOnDesktop(mq) { + this.set('closeGlobalMobileNavOnDesktop', bind(function closeGlobalMobileNavOnDesktop(mq) { if (!mq.matches) { // Is desktop sized this.set('controller.showGlobalMobileNav', false);