Merge pull request #2141 from riking/correctness

Lots of JS correctness fixes
This commit is contained in:
Robin Ward 2014-03-19 11:20:15 -04:00
commit 51e3d72461
50 changed files with 99 additions and 102 deletions

View File

@ -25,7 +25,7 @@ Discourse.AdminApiController = Ember.ArrayController.extend({
Creates an API key instance with internal user object Creates an API key instance with internal user object
@method regenerateKey @method regenerateKey
@param {Discourse.ApiKey} the key to regenerate @param {Discourse.ApiKey} key the key to regenerate
**/ **/
regenerateKey: function(key) { regenerateKey: function(key) {
bootbox.confirm(I18n.t("admin.api.confirm_regen"), I18n.t("no_value"), I18n.t("yes_value"), function(result) { bootbox.confirm(I18n.t("admin.api.confirm_regen"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
@ -39,7 +39,7 @@ Discourse.AdminApiController = Ember.ArrayController.extend({
Revokes an API key Revokes an API key
@method revokeKey @method revokeKey
@param {Discourse.ApiKey} the key to revoke @param {Discourse.ApiKey} key the key to revoke
**/ **/
revokeKey: function(key) { revokeKey: function(key) {
var self = this; var self = this;

View File

@ -1,5 +1,5 @@
Discourse.AdminBackupsController = Ember.ObjectController.extend({ Discourse.AdminBackupsController = Ember.ObjectController.extend({
noOperationIsRunning: Em.computed.not("isOperationRunning"), noOperationIsRunning: Em.computed.not("isOperationRunning"),
rollbackEnabled: Em.computed.and("canRollback", "restoreEnabled", "noOperationIsRunning"), rollbackEnabled: Em.computed.and("canRollback", "restoreEnabled", "noOperationIsRunning"),
rollbackDisabled: Em.computed.not("rollbackEnabled"), rollbackDisabled: Em.computed.not("rollbackEnabled")
}); });

View File

@ -71,6 +71,5 @@ Discourse.AdminBackupsIndexController = Ember.ArrayController.extend({
}).then(function() { }).then(function() {
Discourse.set("isReadOnly", enable); Discourse.set("isReadOnly", enable);
}); });
}, }
}); });

View File

@ -1,4 +1,4 @@
Discourse.AdminBackupsLogsController = Ember.ArrayController.extend({ Discourse.AdminBackupsLogsController = Ember.ArrayController.extend({
needs: ["adminBackups"], needs: ["adminBackups"],
status: Em.computed.alias("controllers.adminBackups"), status: Em.computed.alias("controllers.adminBackups")
}); });

View File

@ -13,8 +13,7 @@ Discourse.AdminEmailSentController = Discourse.Controller.extend({
Discourse.EmailLog.findAll(this.get("filter")).then(function(logs) { Discourse.EmailLog.findAll(this.get("filter")).then(function(logs) {
self.set("model", logs); self.set("model", logs);
}); });
}, 250).observes("filter.user", "filter.address", "filter.type", "filter.reply_key"), }, 250).observes("filter.user", "filter.address", "filter.type", "filter.reply_key")
}); });
/** /**
@ -32,8 +31,7 @@ Discourse.AdminEmailSkippedController = Discourse.Controller.extend({
Discourse.EmailLog.findAll(this.get("filter")).then(function(logs) { Discourse.EmailLog.findAll(this.get("filter")).then(function(logs) {
self.set("model", logs); self.set("model", logs);
}); });
}, 250).observes("filter.user", "filter.address", "filter.type", "filter.skipped_reason"), }, 250).observes("filter.user", "filter.address", "filter.type", "filter.skipped_reason")
}); });

View File

@ -40,7 +40,7 @@ Discourse.ApiKey.reopenClass({
Creates an API key instance with internal user object Creates an API key instance with internal user object
@method create @method create
@param {Object} the properties to create @param {...} var_args the properties to initialize this with
@returns {Discourse.ApiKey} the ApiKey instance @returns {Discourse.ApiKey} the ApiKey instance
**/ **/
create: function() { create: function() {

View File

@ -85,6 +85,5 @@ Discourse.Backup.reopenClass({
window.location.pathname = Discourse.getURL("/"); window.location.pathname = Discourse.getURL("/");
} }
}); });
}, }
}); });

View File

@ -12,6 +12,5 @@ Discourse.BackupStatus = Discourse.Model.extend({
restoreEnabled: function() { restoreEnabled: function() {
return Discourse.SiteSettings.allow_restore && !this.get("isOperationRunning"); return Discourse.SiteSettings.allow_restore && !this.get("isOperationRunning");
}.property("isOperationRunning"), }.property("isOperationRunning")
}); });

View File

@ -31,7 +31,7 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
}).then(function (status) { }).then(function (status) {
return Discourse.BackupStatus.create({ return Discourse.BackupStatus.create({
isOperationRunning: status.is_operation_running, isOperationRunning: status.is_operation_running,
canRollback: status.can_rollback, canRollback: status.can_rollback
}); });
}); });
}, },
@ -69,7 +69,7 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
Destroys a backup Destroys a backup
@method destroyBackup @method destroyBackup
@param {Discourse.Backup} the backup to destroy @param {Discourse.Backup} backup the backup to destroy
**/ **/
destroyBackup: function(backup) { destroyBackup: function(backup) {
var self = this; var self = this;
@ -91,7 +91,7 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
Start a restore and redirect the user to the logs tab Start a restore and redirect the user to the logs tab
@method startRestore @method startRestore
@param {Discourse.Backup} the backup to restore @param {Discourse.Backup} backup the backup to restore
**/ **/
startRestore: function(backup) { startRestore: function(backup) {
var self = this; var self = this;
@ -160,7 +160,6 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
uploadError: function(filename, message) { uploadError: function(filename, message) {
bootbox.alert(I18n.t("admin.backups.upload.error", { filename: filename, message: message })); bootbox.alert(I18n.t("admin.backups.upload.error", { filename: filename, message: message }));
}, }
} }
}); });

View File

@ -22,7 +22,7 @@ Discourse.AdminCustomizeView = Discourse.View.extend({
selectStylesheet: function() { this.set('selected', 'stylesheet'); }, selectStylesheet: function() { this.set('selected', 'stylesheet'); },
selectMobileHeader: function() { this.set('selected', 'mobileHeader'); }, selectMobileHeader: function() { this.set('selected', 'mobileHeader'); },
selectMobileStylesheet: function() { this.set('selected', 'mobileStylesheet'); }, selectMobileStylesheet: function() { this.set('selected', 'mobileStylesheet'); }
}, },
didInsertElement: function() { didInsertElement: function() {

View File

@ -8,7 +8,7 @@ Discourse.AutoCloseFormComponent = Ember.Component.extend({
autoCloseChanged: function() { autoCloseChanged: function() {
if( this.get('autoCloseTime') && this.get('autoCloseTime').length > 0 ) { if( this.get('autoCloseTime') && this.get('autoCloseTime').length > 0 ) {
this.set('autoCloseTime', this.get('autoCloseTime').replace(/[^\d-\s\:]/g, '') ); this.set('autoCloseTime', this.get('autoCloseTime').replace(/[^:\d-\s]/g, '') );
} }
this.set('autoCloseValid', this.isAutoCloseValid()); this.set('autoCloseValid', this.isAutoCloseValid());
}.observes('autoCloseTime'), }.observes('autoCloseTime'),

View File

@ -20,5 +20,5 @@ Discourse.DiscoveryController = Discourse.ObjectController.extend({
showMoreDailyUrl: function() { return this.showMoreUrl('daily'); }.property('category', 'noSubcategories'), showMoreDailyUrl: function() { return this.showMoreUrl('daily'); }.property('category', 'noSubcategories'),
showMoreWeeklyUrl: function() { return this.showMoreUrl('weekly'); }.property('category', 'noSubcategories'), showMoreWeeklyUrl: function() { return this.showMoreUrl('weekly'); }.property('category', 'noSubcategories'),
showMoreMonthlyUrl: function() { return this.showMoreUrl('monthly'); }.property('category', 'noSubcategories'), showMoreMonthlyUrl: function() { return this.showMoreUrl('monthly'); }.property('category', 'noSubcategories'),
showMoreYearlyUrl: function() { return this.showMoreUrl('yearly'); }.property('category', 'noSubcategories'), showMoreYearlyUrl: function() { return this.showMoreUrl('yearly'); }.property('category', 'noSubcategories')
}); });

View File

@ -12,6 +12,6 @@ Discourse.GroupController = Discourse.ObjectController.extend({
// It would be nice if bootstrap marked action lists as selected when their links // It would be nice if bootstrap marked action lists as selected when their links
// were 'active' not the `li` tags. // were 'active' not the `li` tags.
showingIndex: Em.computed.equal('showing', 'index'), showingIndex: Em.computed.equal('showing', 'index'),
showingMembers: Em.computed.equal('showing', 'members'), showingMembers: Em.computed.equal('showing', 'members')
}); });

View File

@ -44,8 +44,7 @@ Discourse.UserInvitedController = Ember.ArrayController.extend({
@property showSearch @property showSearch
**/ **/
showSearch: function() { showSearch: function() {
if (Em.isNone(this.get('searchTerm')) && this.get('model.length') === 0) { return false; } return !(Em.isNone(this.get('searchTerm')) && this.get('model.length') === 0);
return true;
}.property('searchTerm', 'model.length'), }.property('searchTerm', 'model.length'),
/** /**

View File

@ -4,7 +4,14 @@
@method replaceBBCode @method replaceBBCode
@param {tag} tag the tag we want to match @param {tag} tag the tag we want to match
@param {function} emitter the function that creates JsonML for the tag @param {function} emitter the function that creates JsonML for the tag
@param {Object} hash of options to pass to `inlineBetween` @param {Object} opts options to pass to Discourse.Dialect.inlineBetween
@param {Function} [opts.emitter] The function that will be called with the contents and returns JsonML.
@param {String} [opts.start] The starting token we want to find
@param {String} [opts.stop] The ending token we want to find
@param {String} [opts.between] A shortcut for when the `start` and `stop` are the same.
@param {Boolean} [opts.rawContents] If true, the contents between the tokens will not be parsed.
@param {Boolean} [opts.wordBoundary] If true, the match must be on a word boundary
@param {Boolean} [opts.spaceBoundary] If true, the match must be on a sppace boundary
**/ **/
function replaceBBCode(tag, emitter, opts) { function replaceBBCode(tag, emitter, opts) {
opts = opts || {}; opts = opts || {};

View File

@ -120,10 +120,10 @@ function parseTree(tree, path, insideCounts) {
**/ **/
function invalidBoundary(args, prev) { function invalidBoundary(args, prev) {
if (!args.wordBoundary && !args.spaceBoundary) { return; } if (!args.wordBoundary && !args.spaceBoundary) { return false; }
var last = prev[prev.length - 1]; var last = prev[prev.length - 1];
if (typeof last !== "string") { return; } if (typeof last !== "string") { return false; }
if (args.wordBoundary && (last.match(/(\w|\/)$/))) { return true; } if (args.wordBoundary && (last.match(/(\w|\/)$/))) { return true; }
if (args.spaceBoundary && (!last.match(/\s$/))) { return true; } if (args.spaceBoundary && (!last.match(/\s$/))) { return true; }
@ -143,15 +143,15 @@ Discourse.Dialect = {
@method cook @method cook
@param {String} text the raw text to cook @param {String} text the raw text to cook
@param {Object} opts hash of options
@returns {String} the cooked text @returns {String} the cooked text
**/ **/
cook: function(text, opts) { cook: function(text, opts) {
if (!initialized) { initializeDialects(); } if (!initialized) { initializeDialects(); }
dialect.options = opts; dialect.options = opts;
var tree = parser.toHTMLTree(text, 'Discourse'), var tree = parser.toHTMLTree(text, 'Discourse');
html = parser.renderJsonML(parseTree(tree));
return html; return parser.renderJsonML(parseTree(tree));
}, },
/** /**
@ -288,9 +288,8 @@ Discourse.Dialect = {
the other helpers such as `replaceBlock` so consider using them first! the other helpers such as `replaceBlock` so consider using them first!
@method registerBlock @method registerBlock
@param {String} the name of the block handler @param {String} name the name of the block handler
@param {Function} the handler @param {Function} handler the handler
**/ **/
registerBlock: function(name, handler) { registerBlock: function(name, handler) {
dialect.block[name] = handler; dialect.block[name] = handler;

View File

@ -271,7 +271,7 @@ $.fn.autocomplete = function(options) {
}); });
return $(this).keydown(function(e) { return $(this).keydown(function(e) {
var c, caretPosition, i, initial, next, nextIsGood, prev, prevIsGood, stopFound, term, total, userToComplete; var c, caretPosition, i, initial, next, prev, prevIsGood, stopFound, term, total, userToComplete;
if(options.allowAny){ if(options.allowAny){
// saves us wiring up a change event as well, keypress is while its pressed // saves us wiring up a change event as well, keypress is while its pressed
@ -298,7 +298,6 @@ $.fn.autocomplete = function(options) {
if ((completeStart === null) && e.which === 8 && options.key) { if ((completeStart === null) && e.which === 8 && options.key) {
c = Discourse.Utilities.caretPosition(me[0]); c = Discourse.Utilities.caretPosition(me[0]);
next = me[0].value[c]; next = me[0].value[c];
nextIsGood = next === void 0 || /\s/.test(next);
c -= 1; c -= 1;
initial = c; initial = c;
prevIsGood = true; prevIsGood = true;

View File

@ -71,8 +71,6 @@ $.fn.caretPosition = function(options) {
"line-height": important("line-height") "line-height": important("line-height")
}); });
before = void 0;
after = void 0;
pos = options && (options.pos || options.pos === 0) ? options.pos : getCaret(textarea[0]); pos = options && (options.pos || options.pos === 0) ? options.pos : getCaret(textarea[0]);
val = textarea.val().replace("\r", ""); val = textarea.val().replace("\r", "");
if (options && options.key) { if (options && options.key) {

View File

@ -110,6 +110,6 @@ Discourse.computed = {
}); });
}); });
return computed.property.apply(computed, args); return computed.property.apply(computed, args);
}, }
}; };

View File

@ -21,7 +21,6 @@ Discourse.Eyeline.prototype.update = function() {
docViewBottom = docViewTop + windowHeight, docViewBottom = docViewTop + windowHeight,
$elements = $(this.selector), $elements = $(this.selector),
atBottom = false, atBottom = false,
foundElement = false,
bottomOffset = $elements.last().offset(), bottomOffset = $elements.last().offset(),
self = this; self = this;
@ -29,9 +28,6 @@ Discourse.Eyeline.prototype.update = function() {
atBottom = (bottomOffset.top <= docViewBottom) && (bottomOffset.top >= docViewTop); atBottom = (bottomOffset.top <= docViewBottom) && (bottomOffset.top >= docViewTop);
} }
// Whether we've seen any elements in this search
foundElement = false;
return $elements.each(function(i, elem) { return $elements.each(function(i, elem) {
var $elem = $(elem), var $elem = $(elem),
elemTop = $elem.offset().top, elemTop = $elem.offset().top,

View File

@ -237,7 +237,7 @@ relativeAgeMediumSpan = function(distance, leaveAgo) {
relativeAgeMedium = function(date, options){ relativeAgeMedium = function(date, options){
var displayDate, fiveDaysAgo, oneMinuteAgo, fullReadable, leaveAgo; var displayDate, fiveDaysAgo, oneMinuteAgo, fullReadable, leaveAgo;
var wrapInSpan = options.wrapInSpan === false ? false : true; var wrapInSpan = options.wrapInSpan !== false;
leaveAgo = options.leaveAgo; leaveAgo = options.leaveAgo;
var distance = Math.round((new Date() - date) / 1000); var distance = Math.round((new Date() - date) / 1000);

View File

@ -13,10 +13,10 @@ Discourse.Markdown = {
validIframes: [], validIframes: [],
/** /**
Whitelists classes for sanitization Whitelists more classes for sanitization.
@param {...String} var_args Classes to whitelist
@method whiteListClass @method whiteListClass
@param {String} val The value to whitelist. Can supply more than one argument
**/ **/
whiteListClass: function() { whiteListClass: function() {
var args = Array.prototype.slice.call(arguments), var args = Array.prototype.slice.call(arguments),
@ -113,7 +113,10 @@ Discourse.Markdown = {
Checks to see if a URL is allowed in the cooked content Checks to see if a URL is allowed in the cooked content
@method urlAllowed @method urlAllowed
@param {String} url Url to check @param {String} uri Url to check
@param {Number} effect ignored
@param {Number} ltype ignored
@param {Object} hints an object with hints, used to check if this url is from an iframe
@return {String} url to insert in the cooked content @return {String} url to insert in the cooked content
**/ **/
urlAllowed: function (uri, effect, ltype, hints) { urlAllowed: function (uri, effect, ltype, hints) {

View File

@ -14,7 +14,7 @@ Discourse.Mobile = {
this.mobileView = $html.hasClass('mobile-view'); this.mobileView = $html.hasClass('mobile-view');
if (localStorage && localStorage.mobileView) { if (localStorage && localStorage.mobileView) {
var savedValue = (localStorage.mobileView === 'true' ? true : false); var savedValue = (localStorage.mobileView === 'true');
if (savedValue !== this.mobileView) { if (savedValue !== this.mobileView) {
this.reloadPage(savedValue); this.reloadPage(savedValue);
} }

View File

@ -22,7 +22,8 @@ Discourse.PageTracker = Ember.Object.extend(Ember.Evented, {
self = this; self = this;
router.on('didTransition', function() { router.on('didTransition', function() {
self.trigger('change', this.get('url')); var router = this;
self.trigger('change', router.get('url'));
}); });
this.set('started', true); this.set('started', true);
} }

View File

@ -35,7 +35,7 @@ Discourse.UserSearch = {
var promise = Ember.Deferred.create(); var promise = Ember.Deferred.create();
// TODO site setting for allowed regex in username // TODO site setting for allowed regex in username
if (term.match(/[^a-zA-Z0-9\_\.]/)) { if (term.match(/[^a-zA-Z0-9_\.]/)) {
promise.resolve([]); promise.resolve([]);
return promise; return promise;
} }
@ -54,8 +54,7 @@ Discourse.UserSearch = {
users.push(u); users.push(u);
results.push(u); results.push(u);
} }
if (results.length > limit) return false; return results.length <= limit;
return true;
}); });
_.each(r.groups,function(g) { _.each(r.groups,function(g) {

View File

@ -210,7 +210,7 @@ Discourse.Utilities = {
Check the extension of the file against the list of authorized extensions Check the extension of the file against the list of authorized extensions
@method isAuthorizedUpload @method isAuthorizedUpload
@param {File} files The file we want to upload @param {File} file The file we want to upload
**/ **/
isAuthorizedUpload: function(file) { isAuthorizedUpload: function(file) {
var extensions = Discourse.SiteSettings.authorized_extensions; var extensions = Discourse.SiteSettings.authorized_extensions;

View File

@ -13,7 +13,8 @@ Discourse.ModalFunctionality = Em.Mixin.create({
Flash a message at the top of the modal Flash a message at the top of the modal
@method blank @method blank
@param {String} name the name of the property we want to check @param {String} message I18n name of the message
@param {String} messageClass CSS class to apply
@return {Boolean} @return {Boolean}
**/ **/
flash: function(message, messageClass) { flash: function(message, messageClass) {

View File

@ -12,7 +12,6 @@ Discourse.ScrollTop = Em.Mixin.create({
Em.run.schedule('afterRender', function() { Em.run.schedule('afterRender', function() {
$(document).scrollTop(0); $(document).scrollTop(0);
}); });
}.on('didInsertElement'), }.on('didInsertElement')
}); });

View File

@ -66,7 +66,7 @@ Discourse.CategoryList.reopenClass({
can_create_category: result.category_list.can_create_category, can_create_category: result.category_list.can_create_category,
can_create_topic: result.category_list.can_create_topic, can_create_topic: result.category_list.can_create_topic,
draft_key: result.category_list.draft_key, draft_key: result.category_list.draft_key,
draft_sequence: result.category_list.draft_sequence, draft_sequence: result.category_list.draft_sequence
}); });
}); });
} }

View File

@ -126,14 +126,10 @@ Discourse.Composer = Discourse.Model.extend({
// reply is always required // reply is always required
if (this.get('missingReplyCharacters') > 0) return true; if (this.get('missingReplyCharacters') > 0) return true;
if (this.get('canCategorize') && return this.get('canCategorize') &&
!Discourse.SiteSettings.allow_uncategorized_topics && !Discourse.SiteSettings.allow_uncategorized_topics &&
!this.get('categoryId') && !this.get('categoryId') &&
!Discourse.User.currentProp('staff')) { !Discourse.User.currentProp('staff');
return true;
}
return false;
}.property('loading', 'canEditTitle', 'titleLength', 'targetUsernames', 'replyLength', 'categoryId', 'missingReplyCharacters'), }.property('loading', 'canEditTitle', 'titleLength', 'targetUsernames', 'replyLength', 'categoryId', 'missingReplyCharacters'),
/** /**

View File

@ -103,11 +103,10 @@ Discourse.Post = Discourse.Model.extend({
}.property('updated_at'), }.property('updated_at'),
flagsAvailable: function() { flagsAvailable: function() {
var post = this, var post = this;
flags = Discourse.Site.currentProp('flagTypes').filter(function(item) { return Discourse.Site.currentProp('flagTypes').filter(function(item) {
return post.get("actionByName." + (item.get('name_key')) + ".can_act"); return post.get("actionByName." + (item.get('name_key')) + ".can_act");
}); });
return flags;
}.property('actions_summary.@each.can_act'), }.property('actions_summary.@each.can_act'),
actionsHistory: function() { actionsHistory: function() {

View File

@ -179,7 +179,6 @@ Discourse.PostStream = Em.Object.extend({
Cancel any active filters on the stream. Cancel any active filters on the stream.
@method cancelFilter @method cancelFilter
@returns {Ember.Deferred} a promise that resolves when the filter has been cancelled.
**/ **/
cancelFilter: function() { cancelFilter: function() {
this.set('summary', false); this.set('summary', false);
@ -373,8 +372,8 @@ Discourse.PostStream = Em.Object.extend({
`undoPost` when it fails. `undoPost` when it fails.
@method stagePost @method stagePost
@param {Discourse.Post} the post to stage in the stream @param {Discourse.Post} post the post to stage in the stream
@param {Discourse.User} the user creating the post @param {Discourse.User} user the user creating the post
**/ **/
stagePost: function(post, user) { stagePost: function(post, user) {

View File

@ -19,7 +19,7 @@ Discourse.TopList.reopenClass({
can_create_topic: result.can_create_topic, can_create_topic: result.can_create_topic,
draft: result.draft, draft: result.draft,
draft_key: result.draft_key, draft_key: result.draft_key,
draft_sequence: result.draft_sequence, draft_sequence: result.draft_sequence
}); });
Discourse.Site.currentProp('periods').forEach(function(period) { Discourse.Site.currentProp('periods').forEach(function(period) {

View File

@ -168,8 +168,7 @@ Discourse.Topic = Discourse.Model.extend({
if (!wordCount) return; if (!wordCount) return;
// Avg for 500 words per minute when you account for skimming // Avg for 500 words per minute when you account for skimming
var minutes = Math.floor(wordCount / 500.0); return Math.floor(wordCount / 500.0);
return minutes;
}.property('word_count'), }.property('word_count'),
toggleStar: function() { toggleStar: function() {

View File

@ -162,7 +162,7 @@ Discourse.TopicList.reopenClass({
Stitch together side loaded topic data Stitch together side loaded topic data
@method topicsFrom @method topicsFrom
@param {Object} JSON object with topic data @param {Object} result JSON object with topic data
@returns {Array} the list of topics @returns {Array} the list of topics
**/ **/
topicsFrom: function(result) { topicsFrom: function(result) {
@ -204,8 +204,8 @@ Discourse.TopicList.reopenClass({
Lists topics on a given menu item Lists topics on a given menu item
@method list @method list
@param {Object} The menu item to filter to @param {Object} filter The menu item to filter to
@param {Object} Any additional params @param {Object} params Any additional params to pass to TopicList.find()
@returns {Promise} a promise that resolves to the list of topics @returns {Promise} a promise that resolves to the list of topics
**/ **/
list: function(filter, params) { list: function(filter, params) {

View File

@ -461,6 +461,7 @@ Discourse.User.reopenClass(Discourse.Singleton, {
@method checkUsername @method checkUsername
@param {String} username A username to check @param {String} username A username to check
@param {String} email An email address to check @param {String} email An email address to check
@param {Number} forUserId user id - provide when changing username
**/ **/
checkUsername: function(username, email, forUserId) { checkUsername: function(username, email, forUserId) {
return Discourse.ajax('/users/check_username', { return Discourse.ajax('/users/check_username', {
@ -472,7 +473,7 @@ Discourse.User.reopenClass(Discourse.Singleton, {
Groups the user's statistics Groups the user's statistics
@method groupStats @method groupStats
@param {Array} Given stats @param {Array} stats Given stats
@returns {Object} @returns {Object}
**/ **/
groupStats: function(stats) { groupStats: function(stats) {
@ -507,6 +508,7 @@ Discourse.User.reopenClass(Discourse.Singleton, {
@param {String} name This user's name @param {String} name This user's name
@param {String} email This user's email @param {String} email This user's email
@param {String} password This user's password @param {String} password This user's password
@param {String} username This user's username
@param {String} passwordConfirm This user's confirmed password @param {String} passwordConfirm This user's confirmed password
@param {String} challenge @param {String} challenge
@returns Result of ajax call @returns Result of ajax call

View File

@ -45,5 +45,5 @@ Discourse.DiscoveryCategoriesRoute = Discourse.Route.extend({
})); }));
this.controllerFor('editCategory').set('selectedTab', 'general'); this.controllerFor('editCategory').set('selectedTab', 'general');
} }
}, }
}); });

View File

@ -57,7 +57,7 @@ Discourse.PreferencesRoute = Discourse.RestrictedUserRoute.extend({
Discourse.PreferencesIndexRoute = Discourse.RestrictedUserRoute.extend({ Discourse.PreferencesIndexRoute = Discourse.RestrictedUserRoute.extend({
renderTemplate: function() { renderTemplate: function() {
this.render('preferences', { into: 'user', outlet: 'userOutlet', controller: 'preferences' }); this.render('preferences', { into: 'user', outlet: 'userOutlet', controller: 'preferences' });
}, }
}); });
/** /**

View File

@ -12,6 +12,7 @@
<div class='controls'> <div class='controls'>
<label class='radio'> <label class='radio'>
<input type='radio' id="choose-topic-{{unbound id}}" name='choose_topic_id' {{action chooseTopic this target="view"}}>{{title}} <input type='radio' id="choose-topic-{{unbound id}}" name='choose_topic_id' {{action chooseTopic this target="view"}}>{{title}}
</label>
</div> </div>
{{/each}} {{/each}}
</ul> </ul>

View File

@ -7,9 +7,9 @@
</a> </a>
{{else}} {{else}}
{{#if noSubcategories}} {{#if noSubcategories}}
<a href='#' {{action expand}} class='badge-category home' {{bind-attr style="badgeStyle"}}>{{i18n categories.no_subcategory}}</i></a> <a href='#' {{action expand}} class='badge-category home' {{bind-attr style="badgeStyle"}}>{{i18n categories.no_subcategory}}</a>
{{else}} {{else}}
<a href='#' {{action expand}} class='badge-category home' {{bind-attr style="badgeStyle"}}>{{allCategoriesLabel}}</i></a> <a href='#' {{action expand}} class='badge-category home' {{bind-attr style="badgeStyle"}}>{{allCategoriesLabel}}</a>
{{/if}} {{/if}}
{{/if}} {{/if}}

View File

@ -1,5 +1,5 @@
<a href='#' {{action closeMessage this}} class='close'><i class='fa fa-times-circle'></i></a> <a href='#' {{action closeMessage this}} class='close'><i class='fa fa-times-circle'></i></a>
<h3>{{i18n composer.similar_topics}}<h3> <h3>{{i18n composer.similar_topics}}</h3>
<ul class='topics'> <ul class='topics'>
{{#each similarTopics}} {{#each similarTopics}}

View File

@ -47,6 +47,7 @@ Discourse.ActionsHistoryComponent = Em.Component.extend({
var key = 'post.actions.people.' + c.get('actionType.name_key'); var key = 'post.actions.people.' + c.get('actionType.name_key');
if (postUrl) { key = key + "_with_url"; } if (postUrl) { key = key + "_with_url"; }
// TODO postUrl might be uninitialized? pick a good default
buffer.push(" " + I18n.t(key, { icons: iconsHtml, postUrl: postUrl}) + "."); buffer.push(" " + I18n.t(key, { icons: iconsHtml, postUrl: postUrl}) + ".");
} }
renderActionIf('usersCollapsed', 'who-acted', c.get('description')); renderActionIf('usersCollapsed', 'who-acted', c.get('description'));

View File

@ -504,7 +504,7 @@ Discourse.ComposerView = Discourse.View.extend(Ember.Evented, {
var $uploadTarget = $('#reply-control'); var $uploadTarget = $('#reply-control');
$uploadTarget.fileupload('destroy'); $uploadTarget.fileupload('destroy');
$uploadTarget.off(); $uploadTarget.off();
}, }
}); });
// not sure if this is the right way, keeping here for now, we could use a mixin perhaps // not sure if this is the right way, keeping here for now, we could use a mixin perhaps

View File

@ -14,7 +14,7 @@ Discourse.ContainerView = Ember.ContainerView.extend(Discourse.Presence, {
@method attachViewWithArgs @method attachViewWithArgs
@param {Object} viewArgs The arguments to pass when creating the view @param {Object} viewArgs The arguments to pass when creating the view
@param {Class} klass The view class we want to create @param {Class} viewClass The view class we want to create
**/ **/
attachViewWithArgs: function(viewArgs, viewClass) { attachViewWithArgs: function(viewArgs, viewClass) {
if (!viewClass) { viewClass = Ember.View.extend(); } if (!viewClass) { viewClass = Ember.View.extend(); }
@ -26,7 +26,7 @@ Discourse.ContainerView = Ember.ContainerView.extend(Discourse.Presence, {
Attaches a view with no arguments and wires up the container properly Attaches a view with no arguments and wires up the container properly
@method attachViewClass @method attachViewClass
@param {Class} klass The view class we want to create @param {Class} viewClass The view class we want to add
**/ **/
attachViewClass: function(viewClass) { attachViewClass: function(viewClass) {
this.attachViewWithArgs(null, viewClass); this.attachViewWithArgs(null, viewClass);

View File

@ -12,9 +12,10 @@ Discourse.ModalBodyView = Discourse.View.extend({
didInsertElement: function() { didInsertElement: function() {
var self = this; var self = this;
$('#discourse-modal').modal('show'); var $discourseModal = $('#discourse-modal');
$('#discourse-modal').one("hide", function () { $discourseModal.modal('show');
$discourseModal.one("hide", function () {
self.get("controller").send("closeModal"); self.get("controller").send("closeModal");
}); });

View File

@ -207,7 +207,8 @@ Discourse.PostMenuView = Discourse.View.extend({
} }
buffer.push("<button title=\"" + tooltip + buffer.push("<button title=\"" + tooltip +
"\" data-action=\"bookmark\" class='bookmark'><div class='" + iconClass + "\" data-action=\"bookmark\" class='" + buttonClass +
"'><div class='" + iconClass +
"'></div></button>"); "'></div></button>");
}, },

View File

@ -39,8 +39,9 @@ Discourse.ShareView = Discourse.View.extend({
}.observes('controller.link'), }.observes('controller.link'),
didInsertElement: function() { didInsertElement: function() {
var shareView = this; var shareView = this,
$('html').on('mousedown.outside-share-link', function(e) { $html = $('html');
$html.on('mousedown.outside-share-link', function(e) {
// Use mousedown instead of click so this event is handled before routing occurs when a // Use mousedown instead of click so this event is handled before routing occurs when a
// link is clicked (which is a click event) while the share dialog is showing. // link is clicked (which is a click event) while the share dialog is showing.
if (shareView.$().has(e.target).length !== 0) { return; } if (shareView.$().has(e.target).length !== 0) { return; }
@ -49,9 +50,10 @@ Discourse.ShareView = Discourse.View.extend({
return true; return true;
}); });
$('html').on('click.discoure-share-link', '[data-share-url]', function(e) { $html.on('click.discoure-share-link', '[data-share-url]', function(e) {
e.preventDefault(); e.preventDefault();
var $currentTarget = $(e.currentTarget); var $currentTarget = $(e.currentTarget),
$shareLink = $('#share-link');
var url = $currentTarget.data('share-url'); var url = $currentTarget.data('share-url');
var postNumber = $currentTarget.data('post-number'); var postNumber = $currentTarget.data('post-number');
// Relative urls // Relative urls
@ -60,7 +62,7 @@ Discourse.ShareView = Discourse.View.extend({
url = window.location.protocol + "//" + window.location.host + url; url = window.location.protocol + "//" + window.location.host + url;
} }
var shareLinkWidth = $('#share-link').width(); var shareLinkWidth = $shareLink.width();
var x = e.pageX - (shareLinkWidth / 2); var x = e.pageX - (shareLinkWidth / 2);
if (x < 25) { if (x < 25) {
x = 25; x = 25;
@ -70,12 +72,12 @@ Discourse.ShareView = Discourse.View.extend({
} }
var header = $('.d-header'); var header = $('.d-header');
var y = e.pageY - ($('#share-link').height() + 20); var y = e.pageY - ($shareLink.height() + 20);
if (y < header.offset().top + header.height()) { if (y < header.offset().top + header.height()) {
y = e.pageY + 10; y = e.pageY + 10;
} }
$('#share-link').css({ $shareLink.css({
left: "" + x + "px", left: "" + x + "px",
top: "" + y + "px" top: "" + y + "px"
}); });
@ -84,7 +86,7 @@ Discourse.ShareView = Discourse.View.extend({
return false; return false;
}); });
$('html').on('keydown.share-view', function(e){ $html.on('keydown.share-view', function(e){
if (e.keyCode === 27) { if (e.keyCode === 27) {
shareView.get('controller').send('close'); shareView.get('controller').send('close');
} }
@ -92,9 +94,10 @@ Discourse.ShareView = Discourse.View.extend({
}, },
willDestroyElement: function() { willDestroyElement: function() {
$('html').off('click.discoure-share-link'); var $html = $('html');
$('html').off('mousedown.outside-share-link'); $html.off('click.discoure-share-link');
$('html').off('keydown.share-view'); $html.off('mousedown.outside-share-link');
$html.off('keydown.share-view');
} }
}); });

View File

@ -7,4 +7,4 @@ I18n.pluralizationRules['cs'] = function (n) {
if (n == 1) return "one"; if (n == 1) return "one";
if (n >= 2 && n <= 4) return "few"; if (n >= 2 && n <= 4) return "few";
return "other"; return "other";
} };

View File

@ -7,4 +7,4 @@ I18n.pluralizationRules['ru'] = function (n) {
if (n % 10 == 1 && n % 100 != 11) return "one"; if (n % 10 == 1 && n % 100 != 11) return "one";
if (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)) return "few"; if (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)) return "few";
return "many"; return "many";
} };

View File

@ -22,7 +22,7 @@ module Jobs
begin begin
mail_string = mail.pop mail_string = mail.pop
Email::Receiver.new(mail_string).process Email::Receiver.new(mail_string).process
rescue Email::Receiver::UserNotSufficientTrustLevelError => e rescue Email::Receiver::UserNotSufficientTrustLevelError
# inform the user about the rejection # inform the user about the rejection
@message = Mail::Message.new(mail_string) @message = Mail::Message.new(mail_string)
clientMessage = RejectionMailer.send_trust_level(@message.from, @message.body) clientMessage = RejectionMailer.send_trust_level(@message.from, @message.body)