Created
November 18, 2014 18:34
-
-
Save shadeglare/f7f571557015c97fb23a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Frame = Frame || {}; | |
(function($, _, Backbone, Frame) { | |
var renderTemplate = function(template) { | |
var templateHtml = template.html(); | |
return _.template(templateHtml, {}); | |
}; | |
var renderTemplateWithModel = function(template, model) { | |
var templateHtml = template.html(); | |
return _.template(templateHtml, model.toJSON()); | |
}; | |
$(function() { | |
var EntityType = { | |
Dictionary: 0, | |
Array: 1 | |
}; | |
var JsonScalar = Backbone.Model.extend({ | |
defaults: function() { | |
return { | |
data: null | |
}; | |
} | |
}); | |
var JsonEntity = Backbone.Model.extend({ | |
defaults: function() { | |
return { }; | |
}, | |
initialize: function() { | |
this._index = 0; | |
this._jsonSerializers[EntityType.Array] = this._arrayToJson; | |
this._jsonSerializers[EntityType.Dictionary] = this._objectToJson; | |
this.setEntityType(EntityType.Dictionary); | |
}, | |
setPair: function(key, value) { | |
if (!key && this.getEntityType() === EntityType.Dictionary) { | |
throw new Error("key must be set for the dictionary entity"); | |
} else { | |
if (key !== 0) { | |
key = key || this._index++; | |
} | |
this.set(key, value); | |
} | |
return key; | |
}, | |
unsetPair: function(key) { | |
switch(this.getEntityType()) { | |
case EntityType.Dictionary: | |
break; | |
case EntityType.Array: | |
break; | |
} | |
this.unset(key); | |
}, | |
setEntityType: function(entityType) { | |
this._entityType = entityType; | |
}, | |
getEntityType: function() { | |
return this._entityType; | |
}, | |
toJSON: function() { | |
return this._jsonSerializers[this._entityType].apply(this); | |
}, | |
_arrayToJson: function() { | |
var self = this; | |
var result = []; | |
var keys = Object.getOwnPropertyNames(this.attributes); | |
keys.forEach(function(key) { | |
var value = self.get(key); | |
var isJsonValue = value instanceof JsonScalar; | |
value = isJsonValue ? value.get("data") : value; | |
result.push(value); | |
}); | |
return result; | |
}, | |
_objectToJson: function() { | |
var self = this; | |
var result = {}; | |
var keys = Object.getOwnPropertyNames(this.attributes); | |
keys.forEach(function(key) { | |
var value = self.get(key); | |
var isJsonValue = value instanceof JsonScalar; | |
value = isJsonValue ? value.get("data") : value; | |
result[key] = value; | |
}); | |
return result; | |
}, | |
_jsonSerializers: {} | |
}, { | |
checkType: function(data) { | |
var type = ""; | |
if (_.isArray(data)) { | |
type = EntityType.Array; | |
} else if (_.isObject(data)) { | |
type = EntityType.Dictionary; | |
} else { | |
throw new Error("Invalid entity type"); | |
} | |
return type; | |
}, | |
fromJSON: function(json) { | |
var jsonEntity = new JsonEntity(); | |
if (_.isArray(json)) { | |
jsonEntity.setEntityType(EntityType.Array); | |
_.each(json, function(value) { | |
JsonEntity._setPair(jsonEntity, null, value); | |
}); | |
} else if (_.isObject(json)) { | |
jsonEntity.setEntityType(EntityType.Dictionary); | |
_.each(json, function(value, key) { | |
JsonEntity._setPair(jsonEntity, key, value) | |
}); | |
} else { | |
throw new Error("Invalid object type"); | |
} | |
return jsonEntity; | |
}, | |
_setPair: function(rootEntity, key, value) { | |
var isScalar = | |
_.isNumber(value) || | |
_.isBoolean(value) || | |
_.isString(value) || | |
_.isNull(value); | |
if (isScalar) { | |
var jsonScalar = new JsonScalar({ data: value }); | |
rootEntity.setPair(key, jsonScalar); | |
} else { | |
var jsonEntity = JsonEntity.fromJSON(value); | |
rootEntity.setPair(key, jsonEntity); | |
} | |
} | |
}); | |
var Pair = Backbone.Model.extend({ | |
defaults: function() { | |
return { | |
key: "", | |
value: null | |
}; | |
}, | |
initialize: function() { | |
this.isReference = true; | |
} | |
}); | |
var JsonStringView = Backbone.View.extend({ | |
className: "json-string", | |
template: $("#json-string-template"), | |
initialize: function() { | |
this._valueInputChanged = _.bind(this._valueInputChanged, this); | |
}, | |
render: function() { | |
var $renderedTemplate = $(renderTemplateWithModel(this.template, this.model)); | |
this.$el.append($renderedTemplate); | |
this.$valueInput = this.$el.find(".value"); | |
this.$valueInput.on("input", this._valueInputChanged); | |
}, | |
dispose: function() { | |
this.$valueInput.off("input"); | |
this.remove(); | |
this.unbind(); | |
}, | |
_valueInputChanged: function(event) { | |
this.model.set("data", event.target.value); | |
} | |
}); | |
var JsonNumberView = Backbone.View.extend({ | |
className: "json-number", | |
template: $("#json-number-template"), | |
initialize: function() { | |
this._valueInputChanged = _.bind(this._valueInputChanged, this); | |
}, | |
render: function() { | |
var $renderedTemplate = $(renderTemplateWithModel(this.template, this.model)); | |
this.$el.append($renderedTemplate); | |
this.$valueInput = this.$el.find(".value"); | |
this.$valueInput.on("input", this._valueInputChanged); | |
this.$valueInput.on("keypress", this._valueInputKeypressed) | |
}, | |
dispose: function() { | |
this.$valueInput.off("input"); | |
this.$valueInput.off("keypress"); | |
this.remove(); | |
this.unbind(); | |
}, | |
_valueInputChanged: function(event) { | |
this.model.set("data", parseFloat(event.target.value)); | |
}, | |
_valueInputKeypressed: function(event) { | |
var notDelimiter = !(event.which === 46 && event.target.value.indexOf('.') === -1); | |
var notDigit = event.which < 48 || event.which > 57; | |
if (notDelimiter && notDigit) { | |
event.preventDefault(); | |
} | |
} | |
}); | |
var JsonBooleanView = Backbone.View.extend({ | |
className: "json-boolean", | |
template: $("#json-boolean-template"), | |
initialize: function() { | |
this._valueSelectChanged = _.bind(this._valueSelectChanged, this); | |
}, | |
render: function() { | |
var $renderedTemplate = $(renderTemplateWithModel(this.template, this.model)); | |
this.$el.append($renderedTemplate); | |
this.$valueInput = this.$el.find(".value"); | |
this.$valueInput.on("change", this._valueSelectChanged); | |
this.$valueInput | |
.find("option[value=" + this.model.get("data") +"]") | |
.prop("selected", true); | |
}, | |
dispose: function() { | |
this.$valueInput.off("change"); | |
this.remove(); | |
this.unbind(); | |
}, | |
_valueSelectChanged: function(event) { | |
this.model.set("data", event.target.value === "true"); | |
} | |
}); | |
var JsonReferenceView = Backbone.View.extend({ | |
className: "json-reference", | |
template: $("#json-reference-template"), | |
initialize: function() { | |
}, | |
render: function() { | |
var $renderedTemplate = $(renderTemplateWithModel(this.template, this.model)); | |
this.$el.append($renderedTemplate); | |
this.$href = this.$el.find(".value"); | |
this.$editReferenceButton = this.$el.find(".edit-reference-button"); | |
this.$editReferenceButton.click(function() { | |
alert("test"); | |
}); | |
}, | |
dispose: function() { | |
this.$editReferenceButton.off("click"); | |
this.remove(); | |
this.unbind(); | |
} | |
}); | |
var JsonEntityView = Backbone.View.extend({ | |
tagName: "div", | |
className: "json-entity", | |
template: $("#json-entity-template"), | |
initialize: function() { | |
this.key = null; | |
this._pairViewCollection = {}; | |
this._itemNameChanged = _.bind(this._itemNameChanged, this); | |
this._addItemButtonClick = _.bind(this._addItemButtonClick, this); | |
this._collectJsonButtonClick = _.bind(this._collectJsonButtonClick, this); | |
this._addPairView = _.bind(this._addPairView, this); | |
this._removePairView = _.bind(this._removePairView, this); | |
}, | |
render: function() { | |
var $renderedTemplate = $(renderTemplateWithModel(this.template, this.model)); | |
this.$el.append($renderedTemplate); | |
this.$content = this.$el.find(".content-holder"); | |
this.$dictionary = this.$el.find(".dictionary"); | |
this.$itemName = this.$el.find(".item-name"); | |
this.$addItemButton = this.$el.find(".add-item-button"); | |
this.$collectJsonButton = this.$el.find(".collect-json-button"); | |
this.$itemName.on("input", this._itemNameChanged); | |
this.$addItemButton.on("click", this._addItemButtonClick); | |
this.$collectJsonButton.on("click", this._collectJsonButtonClick); | |
this.entityType = this.model.getEntityType(); | |
if (this.entityType !== EntityType.Dictionary) { | |
this.$dictionary.css("display", "none"); | |
} | |
}, | |
renderJson: function(data) { | |
var self = this; | |
var type = JsonEntity.checkType(data); | |
this.model = new JsonEntity(); | |
this.model.setEntityType(type); | |
this.render(); | |
_.each(data, function(value, key) { | |
self._renderPair(new Pair(), key, value); | |
}); | |
}, | |
dispose: function() { | |
this.trigger("dispose"); | |
this.$itemName.off("input"); | |
this.$addItemButton.off("click"); | |
this.$collectJsonButton.off("click"); | |
this.remove(); | |
this.unbind(); | |
}, | |
_itemNameChanged: function(event) { | |
this.key = event.target.value; | |
}, | |
_checkItemExists: function() { | |
return this.entityType === EntityType.Dictionary && | |
(!this.key || this._pairViewCollection.hasOwnProperty(this.key)); | |
}, | |
_addItemButtonClick: function() { | |
this._renderPair(); | |
}, | |
_collectJsonButtonClick: function() { | |
alert(JSON.stringify(this.model, null, 4)); | |
}, | |
_renderPair: function(pair, key, data) { | |
pair = pair || new Pair(); | |
this.key = key || this.key; | |
if (this._checkItemExists()) { | |
alert("Could not add duplicate or null"); | |
return; | |
} | |
this._attachPairToModel(pair); | |
this._addPairView(pair, data); | |
}, | |
_attachPairToModel: function(pair) { | |
var self = this; | |
if (this.entityType === EntityType.Array) { | |
this.key = this.model.setPair(null || pair.get("key"), pair.get("value")); | |
} else { | |
this.model.setPair(this.key, pair.get("value")); | |
} | |
pair.set("key", this.key); | |
pair.on("change", function() { | |
self.model.setPair(pair.get("key"), pair.get("value")); | |
}); | |
pair.listenToOnce(this, "dispose", function() { | |
pair.off("change"); | |
}); | |
}, | |
_addPairView: function(pair, data) { | |
var self = this; | |
var pairView = new PairView({ model: pair }); | |
pairView.render(); | |
pairView.loadData(data); | |
pairView.on("dispose", function() { | |
var key = pair.get("key"); | |
self.model.unsetPair(key); | |
self._removePairView(key); | |
}); | |
this.$content.append(pairView.$el); | |
this._pairViewCollection[pair.get("key")] = pairView; | |
}, | |
_removePairView: function(key) { | |
delete this._pairViewCollection[key]; | |
} | |
}); | |
var PairView = Backbone.View.extend({ | |
tagName: "div", | |
className: "pair", | |
template: $("#pair-template"), | |
initialize: function() { | |
this._valueEditorView = null; | |
this._foldingButtonClick = _.bind(this._foldingButtonClick, this); | |
this._deletePairButtonClick = _.bind(this._deletePairButtonClick, this); | |
this._typeSelectChanged = _.bind(this._typeSelectChanged, this); | |
}, | |
render: function() { | |
var $renderedTemplate = $(renderTemplateWithModel(this.template, this.model)); | |
this.$el.append($renderedTemplate); | |
this.$settings = this.$el.find(".settings"); | |
this.$foldingHolder = this.$el.find(".folding-holder"); | |
this.$foldingButton = this.$el.find(".folding-button"); | |
this.$jsonScalarHolder = this.$el.find(".json-scalar-holder"); | |
this.$jsonEntityHolder = this.$el.find(".json-entity-holder"); | |
this.$deletePairButton = this.$el.find(".delete-pair-button"); | |
this.$typeSelect = this.$el.find("select.type"); | |
this.$foldingButton.on("click", this._foldingButtonClick); | |
this.$deletePairButton.on("click", this._deletePairButtonClick); | |
this.$typeSelect.on("change", this._typeSelectChanged); | |
this._updateTypeSelectOptions(); | |
}, | |
loadData: function(data) { | |
if (typeof(data) !== typeof(undefined)) { | |
var type = this._getDataType(data); | |
this.$typeSelect | |
.find("option[value=" + type +"]") | |
.prop("selected", true); | |
this._switchValueEditorView(type, data); | |
} | |
}, | |
dispose: function() { | |
this.trigger("dispose"); | |
this.$foldingButton.off("click"); | |
this.$deletePairButton.off("click"); | |
this.$typeSelect.off("change"); | |
this.model.off(); | |
this.model.stopListening(); | |
this.remove(); | |
this.unbind(); | |
}, | |
_updateTypeSelectOptions: function() { | |
if (this.model.isReference) { | |
this.$typeSelect.find("option[value='String']").remove(); | |
this.$typeSelect.find("option[value='Number']").remove(); | |
this.$typeSelect.find("option[value='Boolean']").remove(); | |
} else { | |
this.$typeSelect.find("option[value='Reference']").remove(); | |
} | |
}, | |
_getDataType: function(data) { | |
var type = ""; | |
if (_.isArray(data)) { | |
type = "Array"; | |
} else if (_.isObject(data)) { | |
type = "Dictionary"; | |
} else if (_.isNumber(data)) { | |
type = "Number"; | |
} else if (_.isString(data)) { | |
type = "String"; | |
} else if(_.isBoolean(data)) { | |
type = "Boolean"; | |
} else { | |
type = "Null"; | |
} | |
return type; | |
}, | |
_foldingButtonClick: function() { | |
if (this.$jsonEntityHolder.css("display") === "none") { | |
this.$jsonEntityHolder.css("display", "block"); | |
this.$foldingButton.text("-"); | |
this.$settings.css("margin-bottom", "0"); | |
} else { | |
this.$jsonEntityHolder.css("display", "none"); | |
this.$foldingButton.text("+"); | |
this.$settings.css("margin-bottom", "0.5em"); | |
} | |
}, | |
_deletePairButtonClick: function() { | |
this.dispose(); | |
}, | |
_typeSelectChanged: function(event) { | |
var $selectType = $(event.target); | |
var type = $selectType.find("option:selected").val(); | |
this._switchValueEditorView(type); | |
}, | |
_switchValueEditorView: function(type, data) { | |
this._removeValueEditorView(); | |
switch(type) { | |
case "Array": | |
case "Dictionary": | |
this.$foldingHolder.css("display", "table-cell"); | |
this._addJsonEntityView(type, data); | |
break; | |
case "Reference": | |
this.$foldingHolder.css("display", "none"); | |
this._addJsonReferenceView(data); | |
break; | |
case "String": | |
this.$foldingHolder.css("display", "none"); | |
this._addJsonStringView(data); | |
break; | |
case "Number": | |
this.$foldingHolder.css("display", "none"); | |
this._addJsonNumberView(data); | |
break; | |
case "Boolean": | |
this.$foldingHolder.css("display", "none"); | |
this._addJsonBooleanView(data); | |
break; | |
default: | |
break; | |
} | |
}, | |
_removeValueEditorView: function() { | |
if (this._valueEditorView) { | |
this.model.set("value", null); | |
this._valueEditorView.dispose(); | |
this._valueEditorView = null; | |
} | |
}, | |
_addJsonEntityView: function(type, data) { | |
var jsonEntityView = new JsonEntityView(); | |
var jsonEntity; | |
if (data) { | |
jsonEntityView = new JsonEntityView(); | |
jsonEntityView.renderJson(data); | |
jsonEntity = jsonEntityView.model; | |
} else { | |
jsonEntity = new JsonEntity(); | |
jsonEntity.setEntityType(EntityType[type]); | |
jsonEntityView.model = jsonEntity; | |
jsonEntityView.render(); | |
} | |
this.model.set("value", jsonEntity); | |
this.$jsonEntityHolder.append(jsonEntityView.$el); | |
this._valueEditorView = jsonEntityView; | |
}, | |
_addJsonStringView: function(data) { | |
var jsonScalar = new JsonScalar(); | |
jsonScalar.set("data", data || ""); | |
this.model.set("value", jsonScalar); | |
var jsonStringView = new JsonStringView({ model: jsonScalar }); | |
jsonStringView.render(); | |
this.$jsonScalarHolder.append(jsonStringView.$el); | |
this._valueEditorView = jsonStringView; | |
}, | |
_addJsonNumberView: function(data) { | |
var jsonScalar = new JsonScalar(); | |
jsonScalar.set("data", data || 0); | |
this.model.set("value", jsonScalar); | |
var jsonNumberView = new JsonNumberView({ model: jsonScalar }); | |
jsonNumberView.render(); | |
this.$jsonScalarHolder.append(jsonNumberView.$el); | |
this._valueEditorView = jsonNumberView; | |
}, | |
_addJsonBooleanView: function(data) { | |
var jsonScalar = new JsonScalar(); | |
jsonScalar.set("data", data || false); | |
this.model.set("value", jsonScalar); | |
var jsonBooleanView = new JsonBooleanView({ model: jsonScalar }); | |
jsonBooleanView.render(); | |
this.$jsonScalarHolder.append(jsonBooleanView.$el); | |
this._valueEditorView = jsonBooleanView; | |
}, | |
_addJsonReferenceView: function(data) { | |
var jsonScalar = new JsonScalar(); | |
jsonScalar.set("data", data || ""); | |
this.model.set("value", jsonScalar); | |
var jsonReferenceView = new JsonReferenceView({ model: jsonScalar }); | |
jsonReferenceView.render(); | |
this.$jsonScalarHolder.append(jsonReferenceView.$el); | |
this._valueEditorView = jsonReferenceView; | |
} | |
}); | |
Frame.JsonBuilder = { | |
JsonEntity: JsonEntity, | |
JsonScalar: JsonScalar, | |
EntityType: EntityType, | |
Pair: Pair, | |
JsonStringView: JsonStringView, | |
JsonNumberView: JsonNumberView, | |
JsonBooleanView: JsonBooleanView, | |
JsonEntityView: JsonEntityView, | |
PairView: PairView | |
}; | |
}); | |
})($, _, Backbone, Frame); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment