CoffeeScript is a little language that compiles into JavaScript. Underneath that awkward Java-esque patina, JavaScript has always had a gorgeous heart. CoffeeScript is an attempt to expose the good parts of JavaScript in a simple way. [1]
class BaseFeedModel extends Model
...
fromServer: (attributes, share) ->
@id = attributes.id
@type = attributes.type
@shareToken = attributes.share_token
shareParams = {}
if share
@share = true
shareParams = {
'share_id': @id
'share_token': @shareToken
'share_type': "activity"
}
The next few slides, we have some examples from our code base. First is what they look like in CoffeeScript, then I converted them to JavaScript. Let's compare!
angular.module('common.services')
.factory('helpers', ->
camelize: (str) ->
str = str.replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) ->
if index == 0
letter.toLowerCase()
else
letter.toUpperCase())
return str.split(" ").join("")
snakify: (str) ->
regexp = /[A-Z]/g
separator = '_'
str = str.replace(regexp, (letter, index) ->
if index == 0
return letter.toLowerCase()
else
return separator + letter.toLowerCase()
)
return str
angular.module('common.services').factory('helpers', function() {
return {
camelize: function(str) {
str = str.replace(/(?:^\w|[A-Z]|\b\w)/g, function(letter, index) {
if (index === 0) {
return letter.toLowerCase();
} else {
return letter.toUpperCase();
}
});
return str.split(" ").join("");
},
snakify: function(str) {
var regexp, separator;
regexp = /[A-Z]/g;
separator = '_';
str = str.replace(regexp, function(letter, index) {
if (index === 0) {
return letter.toLowerCase();
} else {
return separator + letter.toLowerCase();
}
});
return str;
}
};
});
angular.module('common.services')
.service('browser', ['$window', ($window) ->
get: ->
userAgent = $window.navigator.userAgent
browsers =
chrome: /chrome/i,
safari: /safari/i,
firefox: /firefox/i,
ie: /internet explorer/i
for key of browsers
return key if browsers[key].test(userAgent)
return 'unknown'
])
angular.module('common.services').service('browser', [
'$window', function($window) {
return {
get: function() {
var browsers, key, userAgent;
userAgent = $window.navigator.userAgent;
browsers = {
chrome: /chrome/i,
safari: /safari/i,
firefox: /firefox/i,
ie: /internet explorer/i
};
for (key in browsers) {
if (browsers[key].test(userAgent)) {
return key;
}
}
return 'unknown';
}
};
}
]);
angular.module('tags.services')
.factory('Tag', (Model) ->
class Tag extends Model
@field 'tagName', default: null
@field 'count', default: 0
@field 'id', default: null
@field 'isDeleted', default: false
fromServer: (attributes) ->
@tagName = attributes.name
if attributes.count
@count = attributes.count
@id = attributes.id
@isDeleted = attributes.marked_as_deleted
return Tag
)
var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
angular.module('tags.services').factory('Tag', function(Model) {
var Tag;
Tag = (function(superClass) {
extend(Tag, superClass);
function Tag() {
return Tag.__super__.constructor.apply(this, arguments);
}
Tag.field('tagName', {
"default": null
});
Tag.field('count', {
"default": 0
});
...
...
Tag.field('id', {
"default": null
});
Tag.field('isDeleted', {
"default": false
});
Tag.prototype.fromServer = function(attributes) {
this.tagName = attributes.name;
if (attributes.count) {
this.count = attributes.count;
}
this.id = attributes.id;
return this.isDeleted = attributes.marked_as_deleted;
};
return Tag;
})(Model);
return Tag;
});
With the adoption of ES2015 and the promise of more regular updates to the EcmaScript standards, I took it upon myself to research how we would go about switching to ES2015 without rewriting our whole application.
This was challenging...
Thanks Google! (and Meteor...)
That was pretty cool!
Here are some examples of why the switch from CoffeeScript to ES2015 is the way forward.
angular.module('common.filters')
.filter('humanizeNumber', ->
(number) ->
[before_decimal, after_decimal] = String(number).split(".")
length = before_decimal.length
if not number?
value = 0
text = ''
else if 4 <= length <= 5
text = 'K'
value = (number/1000).toFixed(1)
else if 6 <= length < 7
text = 'K'
value = (number/1000).toFixed()
else if length >= 7
text = 'M'
value = (number/1000000).toFixed(1)
else if before_decimal == '0' or length == 0
text = ''
if after_decimal and after_decimal.length > 0
value = parseFloat(number).toFixed(2)
else
value = Math.round(number, 0)
else
text = ''
value = Math.round(number, 1)
return "#{value}#{text}"
)
angular.module('common.filters')
.filter('humanizeNumber', function() {
return function(number) {
let [before_decimal, after_decimal] = String(number).split(".");
let length = before_decimal.length;
let value = 0.0;
let text = '';
if (!(typeof number !== "undefined" && number !== null)) {
value = 0.0;
text = '';
} else if (4 <= length && length <= 5) {
text = 'K';
value = (number/1000).toFixed(1);
} else if (6 <= length && length < 7) {
text = 'K';
value = (number/1000).toFixed();
} else if (length >= 7) {
text = 'M';
value = (number/1000000).toFixed(1);
} else if (before_decimal === '0' || length === 0) {
text = '';
if (after_decimal && after_decimal.length > 0) {
value = parseFloat(number).toFixed(2);
} else {
value = Math.round(number, 0);
}
} else {
text = '';
value = Math.round(number, 1);
}
return `${value}${text}`;
};
});
angular.module('common.filters')
.filter('linkify', ($sanitize) ->
(text, serviceName) ->
regexs =
link:
regex: /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig,
template: '<a href=\"$1\" target="_blank">$1</a>'
...
services =
twitter:
hash:
regex: /(^|\s)#(\w+)/g,
template: "$1<a href='http://twitter.com/#!/search?q=%23$2' target='_blank'>#$2</a>"
user:
regex: /((?:^|[^a-zA-Z0-9_!#$%&*@@]|RT:?))([@@])([a-zA-Z0-9_]{1,20})(\/[a-zA-Z][a-zA-Z0-9_-]{0,24})?/g,
template: "$1<a href='http://twitter.com/#!/$3$4' target='_blank'>@$3$4</a>"
...
if text
for name, type of regexs
text = text.replace(type.regex, type.template)
service = services[serviceName]
if service
for name, type of service
text = text.replace(type.regex, type.template)
return text
)
angular.module('common.filters')
.filter('linkify', function($sanitize) {
return function(text, serviceName) {
let regexs = {
link: {
regex: /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig,
template: '<a href=\"$1\" target="_blank">$1</a>'
},
...
};
let services = {
twitter: {
hash: {
regex: /(^|\s)#(\w+)/g,
template: "$1<a href='http://twitter.com/#!/search?q=%23$2' target='_blank'>#$2</a>"
},
user: {
regex: /((?:^|[^a-zA-Z0-9_!#$%&*@@]|RT:?))([@@])([a-zA-Z0-9_]{1,20})(\/[a-zA-Z][a-zA-Z0-9_-]{0,24})?/g,
template: "$1<a href='http://twitter.com/#!/$3$4' target='_blank'>@$3$4</a>"
}
},
...
};
if (text) {
let type = '';
for (let name in regexs) {
type = regexs[name];
text = text.replace(type.regex, type.template);
}
let service = services[serviceName];
if (service) {
for (let name in service) {
type = service[name];
text = text.replace(type.regex, type.template);
}
}
}
return text;
};
});
angular.module('common.services')
.factory("Paginator", ->
class Paginator
constructor: (itemsPerPage=1, totalPages=3) ->
@page = 0
@itemsPerPage = itemsPerPage
@totalPages = totalPages
previousPage: ->
if @page > 0
@page -= 1
nextPage: ->
if @page < (@totalPages - 1)
@page += 1
atStart: ->
@page == 0
atEnd: ->
@page + 1 >= @totalPages
return Paginator
)
angular.module('common.services')
.factory("Paginator", function() {
class Paginator {
constructor(itemsPerPage=1, totalPages=3) {
this.page = 0;
this.itemsPerPage = itemsPerPage;
this.totalPages = totalPages;
}
previousPage() {
if (this.page > 0) {
return this.page -= 1;
}
}
nextPage() {
if (this.page < (this.totalPages - 1)) {
return this.page += 1;
}
}
atStart(){
return this.page === 0;
}
atEnd(){
return this.page + 1 >= this.totalPages;
}
}
return Paginator;
});
Find me! @joshfinnie (almost) everywhere on the internetz...