define(['backbone', 'underscore'], function(Backbone, _) { var LicenseModel = Backbone.Model.extend({ defaults: { type: null, options: {}, custom: false // either `false`, or a string }, initialize: function(attributes) { if (attributes && attributes.asString) { this.setFromString(attributes.asString); this.unset('asString'); } }, toString: function() { var custom = this.get('custom'); if (custom) { return custom; } var type = this.get('type'), options = this.get('options'); if (_.isEmpty(options)) { return type || ''; } // options are where it gets tricky var optionStrings = _.map(options, function(value, key) { if (_.isBoolean(value)) { return value ? key : null; } else { return key + '=' + value; } }); // filter out nulls optionStrings = _.filter(optionStrings, _.identity); // build license string and return return type + ': ' + optionStrings.join(' '); }, setFromString: function(string, options) { if (!string) { // reset to defaults return this.set(this.defaults, options); } var colonIndex = string.indexOf(':'), spaceIndex = string.indexOf(' '); // a string without a colon could be a custom license, or a license // type without options if (colonIndex == -1) { if (spaceIndex == -1) { // if there's no space, it's a license type without options return this.set({ type: string, options: {}, custom: false }, options); } else { // if there is a space, it's a custom license return this.set({ type: null, options: {}, custom: string }, options); } } // there is a colon, which indicates a license type with options. var type = string.substring(0, colonIndex), optionsObj = {}, optionsString = string.substring(colonIndex + 1); _.each(optionsString.split(' '), function(optionString) { if (_.isEmpty(optionString)) { return; } var eqIndex = optionString.indexOf('='); if (eqIndex == -1) { // this is a boolean flag optionsObj[optionString] = true; } else { // this is a key-value pair var optionKey = optionString.substring(0, eqIndex); var optionVal = optionString.substring(eqIndex + 1); optionsObj[optionKey] = optionVal; } }); return this.set({ type: type, options: optionsObj, custom: false }, options); } }); return LicenseModel; });