-
{% trans %}Save all changes{% endtrans %}
+
+
+ {# set loading state, this will be removed once backbone application is fully loaded #}
+
+ {% trans %}Loading database documentary fields ...{% endtrans %}
-
-{# bootstrap admin field application #}
+
+{# bootstrap admin field backbone application #}
diff --git a/templates/web/admin/fields/templates.html.twig b/templates/web/admin/fields/templates.html.twig
index 479137c158..78467ce9f5 100644
--- a/templates/web/admin/fields/templates.html.twig
+++ b/templates/web/admin/fields/templates.html.twig
@@ -3,7 +3,13 @@
<%= msg %>
-
+
+
-
+
+
diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Admin/FieldsTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Admin/FieldsTest.php
index abfed82d78..8e22f2be75 100644
--- a/tests/Alchemy/Tests/Phrasea/Controller/Admin/FieldsTest.php
+++ b/tests/Alchemy/Tests/Phrasea/Controller/Admin/FieldsTest.php
@@ -7,6 +7,25 @@ use Alchemy\Phrasea\Vocabulary\Controller as VocabularyController;
class ControllerFieldsTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
{
+ public function testRoot()
+ {
+ $databoxes = self::$DI['app']['phraseanet.appbox']->get_databoxes();
+ $databox = array_shift($databoxes);
+
+ self::$DI['client']->request("GET", "/admin/fields/" . $databox->get_sbas_id());
+
+ $this->assertTrue(self::$DI['client']->getResponse()->isOk());
+ }
+
+ public function testLanguage()
+ {
+ self::$DI['client']->request("GET", "/admin/fields/language.json");
+ $response = self::$DI['client']->getResponse();
+
+ $this->assertTrue($response->isOk());
+ $this->assertEquals("application/json", $response->headers->get("content-type"));
+ }
+
public function testGetTag()
{
$tag = new ObjectName();
@@ -100,6 +119,99 @@ class ControllerFieldsTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
}
}
+ public function testUpdateFields()
+ {
+ $databoxes = self::$DI['app']['phraseanet.appbox']->get_databoxes();
+ $databox = array_shift($databoxes);
+ $fieldObjects = array();
+ // create two fields
+ $fields = array(
+ array(
+ 'sbas-id' => $databox->get_sbas_id(),
+ 'name' => 'testfield' . mt_rand(),
+ 'multi' => true,
+ 'thumbtitle' => false,
+ 'tag' => 'XMP:XMP',
+ 'business' => false,
+ 'indexable' => true,
+ 'required' => true,
+ 'separator' => '=;',
+ 'readonly' => false,
+ 'type' => 'string',
+ 'tbranch' => '',
+ 'report' => true,
+ 'dces-element' => null,
+ 'vocabulary-type' => null,
+ 'vocabulary-restricted' => false,
+ ), array(
+ 'sbas-id' => $databox->get_sbas_id(),
+ 'name' => 'testfield' . mt_rand(),
+ 'multi' => true,
+ 'thumbtitle' => false,
+ 'tag' => 'XMP:XMP',
+ 'business' => false,
+ 'indexable' => true,
+ 'required' => true,
+ 'separator' => '=;',
+ 'readonly' => false,
+ 'type' => 'string',
+ 'tbranch' => '',
+ 'report' => true,
+ 'dces-element' => null,
+ 'vocabulary-type' => null,
+ 'vocabulary-restricted' => false,
+ ));
+
+ foreach($fields as $fieldData) {
+ $field = \databox_field::create(self::$DI['app'], $databox, $fieldData['name'], $fieldData['multi']);
+ $field
+ ->set_thumbtitle($fieldData['thumbtitle'])
+ ->set_tag(\databox_field::loadClassFromTagName($fieldData['tag']))
+ ->set_business($fieldData['business'])
+ ->set_indexable($fieldData['indexable'])
+ ->set_required($fieldData['required'])
+ ->set_separator($fieldData['separator'])
+ ->set_readonly($fieldData['readonly'])
+ ->set_type($fieldData['type'])
+ ->set_tbranch($fieldData['tbranch'])
+ ->set_report($fieldData['report'])
+ ->setVocabularyControl(null)
+ ->setVocabularyRestricted(false);
+ $field->save();
+ $fieldObjects[] = $field;
+ }
+
+ // get body
+ $body = $databox->get_meta_structure()->toArray();
+
+ // change some body data
+ $body[count($body) - 2]['business'] = true;
+ $body[count($body) - 2]['indexable'] = false;
+ $body[count($body) - 1]['readonly'] = true;
+ $body[count($body) - 1]['required'] = false;
+
+ self::$DI['client']->request("PUT", sprintf("/admin/fields/%d/fields", $databox->get_sbas_id()), array(), array(), array(), json_encode($body));
+
+ $response = self::$DI['client']->getResponse()->getContent();
+
+ $this->assertEquals("application/json", self::$DI['client']->getResponse()->headers->get("content-type"));
+
+ $data = json_decode($response, true);
+
+ $this->assertArrayHasKey('success', $data);
+ $this->assertArrayHasKey('messages', $data);
+ $this->assertArrayHasKey('fields', $data);
+
+ // expect last 2 fields from body equals last 2 fields from response
+ $this->assertEquals(array_splice($body, -2), array_splice($data['fields'], -2));
+
+ // delete created fields
+ foreach($fieldObjects as $field) {
+ $field->delete();
+ }
+
+ }
+
public function testCreateField()
{
$databoxes = self::$DI['app']['phraseanet.appbox']->get_databoxes();
diff --git a/www/scripts/apps/admin/fields/app.js b/www/scripts/apps/admin/fields/app.js
index 1d911ff724..49606f80e0 100644
--- a/www/scripts/apps/admin/fields/app.js
+++ b/www/scripts/apps/admin/fields/app.js
@@ -1,47 +1,83 @@
define([
- 'jquery',
- 'underscore',
- 'backbone',
- 'i18n',
- 'apps/admin/fields/collections/fields',
- 'apps/admin/fields/collections/vocabularies',
- 'apps/admin/fields/collections/dcFields',
- 'apps/admin/fields/views/list'
-], function($, _, Backbone, i18n, FieldsCollection, VocabulariesCollection, DcFieldsCollection, FieldListView) {
+ "jquery",
+ "underscore",
+ "backbone",
+ "i18n",
+ "apps/admin/fields/collections/fields",
+ "apps/admin/fields/collections/vocabularies",
+ "apps/admin/fields/collections/dcFields",
+ "apps/admin/fields/views/list",
+ "apps/admin/fields/views/save",
+ "apps/admin/fields/views/fieldError",
+ "apps/admin/fields/errors/errorManager"
+], function(
+ $, _, Backbone, i18n, FieldsCollection, VocabulariesCollection,
+ DcFieldsCollection, FieldListView, SaveView, FieldErrorView, ErrorManager) {
var initialize = function() {
- window.AdminFieldApp = {};
+ AdminFieldApp = {
+ $window : $(window),
+ $scope : $("#admin-field-app"),
+ $top : $(".row-top", this.$scope),
+ $bottom : $(".row-bottom", this.$scope),
+ $leftBlock : $(".left-block", this.$bottom),
+ $rightBlock : $(".right-block", this.$bottom),
+ resizeListBlock: function () {
+ var listBlock = $(".list-block", AdminFieldApp.$leftBlock);
+ listBlock.height(AdminFieldApp.$window.height() - listBlock.offset().top - 10);
+ },
+ resize: function () {
+ AdminFieldApp.resizeListBlock();
+ AdminFieldApp.$rightBlock.height(AdminFieldApp.$window.height() - AdminFieldApp.$rightBlock.offset().top - 10);
+ }
+ };
- window.AdminFieldApp.sbas_id = $('input[name=current_sbas_id]').val();
+ // bind resize
+ AdminFieldApp.$window.bind("resize", AdminFieldApp.resize);
- var fieldsCollection = new FieldsCollection(null, {
- sbas_id : window.AdminFieldApp.sbas_id
+ // current sbas id
+ AdminFieldApp.sbas_id = $("input[name=current_sbas_id]", AdminFieldApp.scope).val();
+
+ // global errors
+ AdminFieldApp.errorManager = new ErrorManager();
+ _.extend(AdminFieldApp.errorManager, Backbone.Events);
+
+ // initiliaze collections
+ AdminFieldApp.fieldsCollection = new FieldsCollection(null, {
+ sbas_id : AdminFieldApp.sbas_id
});
+ AdminFieldApp.vocabularyCollection = new VocabulariesCollection();
+ AdminFieldApp.dcFieldsCollection = new DcFieldsCollection();
- var vocabulariesCollection = new VocabulariesCollection();
- var dcFieldsCollection = new DcFieldsCollection();
+ // load strings
+ i18n.init({ resGetPath: "/admin/fields/language.json"});
- // load strings synchronously
- i18n.init({ resGetPath: '/admin/fields/language.json', getAsync: false });
-
- var requests = [
- fieldsCollection.fetch(),
- vocabulariesCollection.fetch(),
- dcFieldsCollection.fetch()
- ];
-
- $.when.apply($, requests).done(
+ // load all collections
+ $.when.apply($, [
+ AdminFieldApp.fieldsCollection.fetch(),
+ AdminFieldApp.vocabularyCollection.fetch(),
+ AdminFieldApp.dcFieldsCollection.fetch()
+ ]).done(
function() {
- window.AdminFieldApp.vocabularyCollection = vocabulariesCollection;
- window.AdminFieldApp.dcFieldsCollection = dcFieldsCollection;
-
- window.AdminFieldApp.fieldListView = new FieldListView({
- collection: fieldsCollection,
- el: $('.left-block')[0]
+ // register views
+ AdminFieldApp.saveView = new SaveView({
+ el: $(".save-block", AdminFieldApp.scope)
});
+ AdminFieldApp.fieldErrorView = new FieldErrorView();
+ AdminFieldApp.fieldListView = new FieldListView({
+ collection: AdminFieldApp.fieldsCollection,
+ el: AdminFieldApp.$leftBlock
+ });
+ // render views
+ AdminFieldApp.saveView.render();
+ AdminFieldApp.fieldListView.render();
+
+ // show bottom
+ AdminFieldApp.$bottom.removeClass("hidden");
+
+ AdminFieldApp.$window.trigger("resize");
- window.AdminFieldApp.fieldListView.render();
// click on first item list
- _.first(window.AdminFieldApp.fieldListView.itemViews).clickAction().animate();
+ _.first(AdminFieldApp.fieldListView.itemViews).clickAction().animate();
}
);
};
diff --git a/www/scripts/apps/admin/fields/collections/dcFields.js b/www/scripts/apps/admin/fields/collections/dcFields.js
index e4837e013b..f3478d16ff 100644
--- a/www/scripts/apps/admin/fields/collections/dcFields.js
+++ b/www/scripts/apps/admin/fields/collections/dcFields.js
@@ -1,12 +1,12 @@
define([
- 'underscore',
- 'backbone',
- 'models/dcField'
+ "underscore",
+ "backbone",
+ "models/dcField"
], function(_, Backbone, DcFieldModel) {
var DcFieldCollection = Backbone.Collection.extend({
model: DcFieldModel,
url: function() {
- return '/admin/fields/dc-fields';
+ return "/admin/fields/dc-fields";
},
comparator: function(item) {
return item.get("label");
diff --git a/www/scripts/apps/admin/fields/collections/fields.js b/www/scripts/apps/admin/fields/collections/fields.js
index 4326571c0f..7e97779abd 100644
--- a/www/scripts/apps/admin/fields/collections/fields.js
+++ b/www/scripts/apps/admin/fields/collections/fields.js
@@ -1,7 +1,7 @@
define([
- 'underscore',
- 'backbone',
- 'models/field'
+ "underscore",
+ "backbone",
+ "models/field"
], function(_, Backbone, FieldModel) {
var FieldCollection = Backbone.Collection.extend({
initialize: function(models, options) {
@@ -12,7 +12,7 @@ define([
},
model: FieldModel,
url: function() {
- return '/admin/fields/' + this.sbasId + '/fields';
+ return "/admin/fields/" + this.sbasId + "/fields";
},
search: function(letters) {
if (letters === "")
@@ -52,6 +52,10 @@ define([
}
return index - 1;
+ },
+ // save all collection
+ save: function(options) {
+ return Backbone.sync("update", this, options || {});
}
});
diff --git a/www/scripts/apps/admin/fields/collections/vocabularies.js b/www/scripts/apps/admin/fields/collections/vocabularies.js
index 2b86d4109d..290348ce41 100644
--- a/www/scripts/apps/admin/fields/collections/vocabularies.js
+++ b/www/scripts/apps/admin/fields/collections/vocabularies.js
@@ -1,12 +1,12 @@
define([
- 'underscore',
- 'backbone',
- 'models/vocabulary'
+ "underscore",
+ "backbone",
+ "models/vocabulary"
], function(_, Backbone, VocabularyModel) {
var VocabularyCollection = Backbone.Collection.extend({
model: VocabularyModel,
url: function() {
- return '/admin/fields/vocabularies';
+ return "/admin/fields/vocabularies";
},
comparator: function(item) {
return item.get("name");
diff --git a/www/scripts/apps/admin/fields/errors/error.js b/www/scripts/apps/admin/fields/errors/error.js
new file mode 100644
index 0000000000..97d234a5ce
--- /dev/null
+++ b/www/scripts/apps/admin/fields/errors/error.js
@@ -0,0 +1,13 @@
+define([
+ "jquery",
+ "underscore"
+], function($, _) {
+
+ var Error = function (model, fieldId, message) {
+ this.model = model;
+ this.fieldId = fieldId;
+ this.message = message;
+ };
+
+ return Error;
+});
diff --git a/www/scripts/apps/admin/fields/errors/errorManager.js b/www/scripts/apps/admin/fields/errors/errorManager.js
new file mode 100644
index 0000000000..aea7bc4067
--- /dev/null
+++ b/www/scripts/apps/admin/fields/errors/errorManager.js
@@ -0,0 +1,125 @@
+define([
+ "jquery",
+ "underscore",
+ "backbone",
+ "apps/admin/fields/errors/errorModel"
+], function($, _, Backbone, ErrorModel) {
+
+ var ErrorManager = function() {
+ this.errors = {};
+ _.extend(this, Backbone.Events);
+ };
+
+ ErrorManager.prototype = {
+ addModelError: function (model) {
+ return this.errors[model.get("id")] = new ErrorModel(model.get("id"));
+ },
+ getModelError: function (model) {
+ if (this.containsModelError(model)) {
+ return this.errors[model.get("id")];
+ }
+
+ return null;
+ },
+ removeModelError: function (model) {
+ if (this.containsModelError(model)) {
+ delete this.errors[model.get("id")];
+ }
+ },
+ containsModelError: function (model) {
+ return "undefined" !== typeof this.errors[model.get("id")];
+ },
+ addModelFieldError: function(error) {
+ if (! error instanceof Error) {
+ throw "Item must be an error object";
+ }
+
+ var model = error.model;
+ var fieldId = error.fieldId;
+
+ if (!this.containsModelError(model)) {
+ this.addModelError(model);
+ }
+
+ this.getModelError(model).add(fieldId, error);
+
+ this.trigger("add-error", error);
+
+ return this;
+ },
+ removeModelFieldError: function(model, fieldId) {
+ var modelError = this.getModelError(model);
+
+ if (modelError) {
+ modelError.remove(fieldId);
+ this.trigger("remove-error", model, fieldId);
+
+ if (modelError.count() === 0) {
+ this.removeModelError(model);
+
+ if (!this.hasErrors()) {
+ this.trigger("no-error");
+ }
+ }
+ }
+ },
+ clearModelFieldErrors: function(model, fieldId) {
+ var modelError = this.getModelError(model);
+
+ if (modelError) {
+ modelError.clear();
+ this.removeModelError(model);
+ }
+
+ if (!this.hasErrors()) {
+ this.trigger("no-error");
+ }
+ },
+ containsModelFieldError: function (model, fieldId) {
+ var modelError = this.getModelError(model);
+
+ if (modelError) {
+ return modelError.has(fieldId);
+ }
+
+ return false;
+ },
+ getModelFieldError: function(model, fieldId) {
+ var modelError = this.getModelError(model);
+
+ if (modelError) {
+ return modelError.get(fieldId);
+ }
+
+ return null;
+ },
+ clearAll: function() {
+ this.errors = {};
+ this.trigger("no-error");
+ },
+ hasErrors: function () {
+ return !_.isEmpty(this.errors);
+ },
+ count: function () {
+ var count = 0;
+ for (var k in this.errors) {
+ if (this.errors.hasOwnProperty(k)) {
+ ++count;
+ }
+ }
+ return count;
+ },
+ all: function () {
+ var errors = [];
+ _.each(this.errors, function(modelErrors) {
+ _.each(modelErrors.all(), function(error) {
+ errors.push(error);
+ });
+ });
+
+ return errors;
+ }
+ };
+
+ return ErrorManager;
+});
diff --git a/www/scripts/apps/admin/fields/errors/errorModel.js b/www/scripts/apps/admin/fields/errors/errorModel.js
new file mode 100644
index 0000000000..ebe1399f73
--- /dev/null
+++ b/www/scripts/apps/admin/fields/errors/errorModel.js
@@ -0,0 +1,50 @@
+define([
+ "jquery",
+ "underscore"
+], function($, _) {
+ var ErrorModel = function(id) {
+ this.id = id;
+ this.errors = {};
+ };
+
+ ErrorModel.prototype = {
+ add: function(id, error) {
+ if (! error instanceof Error) {
+ throw "Item must be an error object";
+ }
+
+ this.errors[id] = error;
+ },
+ get: function(id) {
+ if (this.has(id)) {
+ return this.errors[id];
+ }
+ return null;
+ },
+ has: function (id) {
+ return "undefined" !== typeof this.errors[id];
+ },
+ remove: function(id) {
+ if (this.has(id)) {
+ delete this.errors[id];
+ }
+ },
+ count: function() {
+ var count = 0;
+ for (var k in this.errors) {
+ if (this.errors.hasOwnProperty(k)) {
+ ++count;
+ }
+ }
+ return count;
+ },
+ clear: function () {
+ this.errors = {};
+ },
+ all: function () {
+ return this.errors;
+ }
+ };
+
+ return ErrorModel;
+});
diff --git a/www/scripts/apps/admin/fields/main.js b/www/scripts/apps/admin/fields/main.js
index 732974a5c0..2c98838a2f 100644
--- a/www/scripts/apps/admin/fields/main.js
+++ b/www/scripts/apps/admin/fields/main.js
@@ -1,25 +1,27 @@
+// configure AMD loading
require.config({
baseUrl: "/scripts",
paths: {
- jquery: '../include/minify/f=include/jslibs/jquery-1.7.1',
- jqueryui: '../include/jslibs/jquery-ui-1.8.17/js/jquery-ui-1.8.17.custom.min',
- underscore: '../assets/underscore-amd/underscore',
- backbone: '../assets/backbone-amd/backbone',
- twig: '../assets/twig/twig',
- i18n: '../assets/i18n/i18next.amd',
- bootstrap: '../skins/html5/bootstrap/js/bootstrap.min'
+ jquery: "../include/minify/f=include/jslibs/jquery-1.7.1",
+ jqueryui: "../include/jslibs/jquery-ui-1.8.17/js/jquery-ui-1.8.17.custom.min",
+ underscore: "../assets/underscore-amd/underscore",
+ backbone: "../assets/backbone-amd/backbone",
+ twig: "../assets/twig/twig",
+ i18n: "../assets/i18n/i18next.amd",
+ bootstrap: "../skins/html5/bootstrap/js/bootstrap.min"
},
shim: {
twig: {
- exports: 'Twig'
+ exports: "Twig"
},
- bootstrap : ['jquery'],
+ bootstrap : ["jquery"],
jqueryui: {
- deps: [ 'jquery' ]
+ deps: [ "jquery" ]
}
}
});
-require(['apps/admin/fields/app'], function(App) {
+// launch application
+require(["apps/admin/fields/app"], function(App) {
App.initialize();
});
diff --git a/www/scripts/apps/admin/fields/views/alert.js b/www/scripts/apps/admin/fields/views/alert.js
index 5e9208c21c..1f46e44898 100644
--- a/www/scripts/apps/admin/fields/views/alert.js
+++ b/www/scripts/apps/admin/fields/views/alert.js
@@ -1,9 +1,10 @@
define([
- 'underscore',
- 'backbone',
- 'i18n',
- 'bootstrap'
-], function(_, Backbone, i18n, bootstrap) {
+ "jquery",
+ "underscore",
+ "backbone",
+ "i18n",
+ "bootstrap"
+], function($, _, Backbone, i18n, bootstrap) {
var AlertView = Backbone.View.extend({
tagName: "div",
className: "alert",
@@ -15,7 +16,7 @@ define([
this.message = options.message || "";
}
// remove view when alert is closed
- this.$el.bind('closed', function () {
+ this.$el.bind("closed", function () {
self.remove();
});
},
@@ -26,7 +27,7 @@ define([
this.$el.addClass("alert-" + this.alert).html(template).alert();
- $('.block-alert').empty().append(this.$el);
+ $(".block-alert").empty().append(this.$el);
return this;
}
diff --git a/www/scripts/apps/admin/fields/views/dcField.js b/www/scripts/apps/admin/fields/views/dcField.js
index 4b16c49618..972ac71311 100644
--- a/www/scripts/apps/admin/fields/views/dcField.js
+++ b/www/scripts/apps/admin/fields/views/dcField.js
@@ -1,26 +1,31 @@
define([
- 'underscore',
- 'backbone',
- 'i18n'
-], function( _, Backbone, i18n, bootstrap) {
+ "jquery",
+ "underscore",
+ "backbone",
+ "i18n"
+], function($, _, Backbone, i18n, bootstrap) {
var DcFieldsView = Backbone.View.extend({
tagName: "div",
className: "input-append",
- events: {
- "change select": "selectChangedAction"
+ initialize : function (options) {
+ this.field = options.field;
},
render: function() {
var template = _.template($("#dc_fields_template").html(), {
- dces_elements: this.collection.toJSON()
+ dces_elements: this.collection.toJSON(),
+ field: this.field.toJSON()
});
this.$el.html(template);
+ var index = $("#dces-element", AdminFieldApp.$rightBlock)[0].selectedIndex - 1;
+ if (index > 0 ) {
+ $(".dces-help-block", AdminFieldApp.$rightBlock).html(
+ this.collection.at(index).get("definition")
+ );
+ }
+
return this;
- },
- selectChangedAction: function(e) {
- var index = $(e.target)[0].selectedIndex;
- this.$el.closest('table').find('.dces-help-block').empty().append(this.collection.at(index).get('definition'));
}
});
diff --git a/www/scripts/apps/admin/fields/views/edit.js b/www/scripts/apps/admin/fields/views/edit.js
index 1597b1406b..1aaa17723c 100644
--- a/www/scripts/apps/admin/fields/views/edit.js
+++ b/www/scripts/apps/admin/fields/views/edit.js
@@ -1,36 +1,47 @@
define([
- 'underscore',
- 'backbone',
- 'i18n',
- 'apps/admin/fields/views/alert',
- 'apps/admin/fields/views/modal',
- 'apps/admin/fields/views/dcField',
-], function(_, Backbone, i18n, AlertView, ModalView, DcFieldView) {
+ "jquery",
+ "underscore",
+ "backbone",
+ "i18n",
+ "apps/admin/fields/views/alert",
+ "apps/admin/fields/views/modal",
+ "apps/admin/fields/views/dcField",
+ "apps/admin/fields/errors/error"
+], function($, _, Backbone, i18n, AlertView, ModalView, DcFieldView, Error) {
var FieldEditView = Backbone.View.extend({
tagName: "div",
className: "field-edit",
initialize: function() {
- this.model.on('change', this.render, this);
- this.model.on('change:name', this.onModelFieldChange, this);
- this.model.on('change:tag', this.onModelFieldChange, this);
+ this.model.on("change", this._onModelChange, this);
this.dcFieldsSubView = new DcFieldView({
- collection: window.AdminFieldApp.dcFieldsCollection
+ collection: AdminFieldApp.dcFieldsCollection,
+ field: this.model
});
},
+ updateModel: function(model) {
+ // unbind event to previous model
+ this.model.off("change");
+ this.model = model;
+
+ return this;
+ },
render: function() {
+ var self = this;
var template = _.template($("#edit_template").html(), {
field: this.model.toJSON(),
- vocabularyTypes: window.AdminFieldApp.vocabularyCollection.toJSON()
+ vocabularyTypes: AdminFieldApp.vocabularyCollection.toJSON(),
+ modelErrors: AdminFieldApp.errorManager.getModelError(this.model)
});
this.$el.empty().html(template);
- this.assign({
- '.dc-fields-subview' : this.dcFieldsSubView
+ this._assign({
+ ".dc-fields-subview" : this.dcFieldsSubView
});
- $("#tag", this.$el).autocomplete({
+ var completer = $("#tag", this.$el).autocomplete({
+ minLength: 2,
source: function(request, response) {
$.ajax({
url: "/admin/fields/tags/search",
@@ -47,72 +58,170 @@ define([
}));
}
});
+ },
+ close: function(e) {
+ var fieldTag = $(e.target);
+ var fieldTagId = fieldTag.attr("id");
+ var fieldTagValue = fieldTag.val();
+
+ // check for format tag
+ if ("" !== fieldTagValue && false === /[a-z]+:[a-z0-9]+/i.test(fieldTagValue)) {
+ fieldTag
+ .closest(".control-group")
+ .addClass("error")
+ .find(".help-block")
+ .empty()
+ .append(i18n.t("validation_tag_invalid"));
+ // add error
+ AdminFieldApp.errorManager.addModelFieldError(new Error(
+ self.model, fieldTagId, i18n.t("validation_tag_invalid")
+ ));
+ } else if (fieldTag.closest(".control-group").hasClass("error")) {
+ // remove error
+ AdminFieldApp.errorManager.removeModelFieldError(
+ self.model, fieldTagId
+ );
+
+ fieldTag
+ .closest(".control-group")
+ .removeClass("error")
+ .find(".help-block")
+ .empty();
+ }
+
+ var data = {};
+ data[fieldTagId] = fieldTagValue;
+ self.model.set(data);
}
- }).val(this.model.get('tag')).autocomplete("widget").addClass("ui-autocomplete-admin-field");
+ });
+
+ completer
+ .val(this.model.get("tag"))
+ .autocomplete("widget")
+ .addClass("ui-autocomplete-admin-field");
+
+ this.delegateEvents();
return this;
},
events: {
+ "click": "focusAction",
"click .delete-field": "deleteAction",
+ "keyup #name": "changeNameAction",
"focusout input[type=text]": "fieldChangedAction",
"change input[type=checkbox]": "fieldChangedAction",
"change select": "selectionChangedAction"
},
+ focusAction: function() {
+ var index = AdminFieldApp.fieldListView.collection.indexOf(this.model);
+ if (index >= 0) {
+ AdminFieldApp.fieldListView.itemViews[index].animate();
+ }
+
+ return this;
+ },
+ // on input name keyup check for errors
+ changeNameAction: function(event) {
+ var self = this;
+ var fieldName = $(event.target);
+ var fieldNameId = fieldName.attr("id");
+ var fieldNameValue = fieldName.val();
+
+ // check for duplicate field name
+ if ("" === fieldNameValue || "undefined" !== typeof AdminFieldApp.fieldListView.collection.find(function(model) {
+ return model.get("name").toLowerCase() === fieldNameValue.toLowerCase() && self.model.get("id") !== model.get("id");
+ })) {
+ fieldName
+ .closest(".control-group")
+ .addClass("error")
+ .find(".help-block")
+ .empty()
+ .append(i18n.t("validation_name_exists"));
+ // add error
+ AdminFieldApp.errorManager.addModelFieldError(new Error(
+ self.model, fieldNameId, i18n.t("" === fieldNameValue ? "validation_blank" : "validation_name_exists")
+ ));
+ } else if (fieldName.closest(".control-group").hasClass("error")) {
+ fieldName
+ .closest(".control-group")
+ .removeClass("error")
+ .find(".help-block")
+ .empty();
+ // remove error
+ AdminFieldApp.errorManager.removeModelFieldError(
+ self.model, fieldNameId
+ );
+ }
+ },
selectionChangedAction: function(e) {
var field = $(e.currentTarget);
- var value = $("option:selected", field).val();
var data = {};
- data[field.attr('id')] = value;
+ data[field.attr("id")] = $("option:selected", field).val();
this.model.set(data);
+
+ return this;
},
fieldChangedAction: function(e) {
var field = $(e.currentTarget);
+ var fieldId = field.attr("id");
var data = {};
- data[field.attr('id')] = field.is(":checkbox") ? field.is(":checked") : field.val();
+ data[fieldId] = field.is(":checkbox") ? field.is(":checked") : field.val();
this.model.set(data);
+
+ return this;
},
deleteAction: function() {
var self = this;
var modalView = new ModalView({
model: this.model,
- message: i18n.t("are_you_sure_delete", { postProcess: "sprintf", sprintf: [this.model.get('name')] })
+ message: i18n.t("are_you_sure_delete", {
+ postProcess: "sprintf",
+ sprintf: [this.model.get("name")]
+ })
});
- var previousIndex = AdminFieldApp.fieldListView.collection.previousIndex(this.model);
- var nextIndex = AdminFieldApp.fieldListView.collection.nextIndex(this.model);
- var itemView;
- if (previousIndex) {
- itemView = AdminFieldApp.fieldListView.itemViews[previousIndex];
- } else if (nextIndex) {
- itemView = AdminFieldApp.fieldListView.itemViews[nextIndex];
- }
+ // get collection index of previous and next model
+ var previousIndex = AdminFieldApp.fieldListView.collection.previousIndex(this.model);
+ var nextIndex = AdminFieldApp.fieldListView.collection.nextIndex(this.model);
+
+ // get previous index if exists else next index - 1 as item is being deleted
+ var index = previousIndex ? previousIndex : (nextIndex ? nextIndex - 1 : -1);
modalView.render();
- modalView.on('modal:confirm', function() {
+ modalView.on("modal:confirm", function() {
self.model.destroy({
success: function(model, response) {
AdminFieldApp.fieldListView.collection.remove(self.model);
+ self._selectModelView(index);
- if (itemView) {
- itemView.clickAction().animate();
- }
-
- new AlertView({alert: 'info', message: i18n.t("deleted_success", { postProcess: "sprintf", sprintf: [model.get('name')] })}).render();
+ new AlertView({alert: "info", message: i18n.t("deleted_success", {
+ postProcess: "sprintf",
+ sprintf: [model.get("name")]
+ })
+ }).render();
},
error: function(model, xhr) {
- new AlertView({alert: 'error', message: i18n.t("something_wrong")}).render();
+ new AlertView({
+ alert: "error", message: i18n.t("something_wrong")
+ }).render();
}
});
});
return this;
},
- onModelFieldChange: function() {
+ _onModelChange: function() {
AdminFieldApp.fieldListView.collection.remove(this.model, {silent: true});
AdminFieldApp.fieldListView.collection.add(this.model);
+
+ var index = AdminFieldApp.fieldListView.collection.indexOf(this.model);
+
+ this._selectModelView(index);
+
this.render();
},
- assign: function(selector, view) {
+ // bind a subview to a DOM element
+ _assign: function(selector, view) {
var selectors;
if (_.isObject(selector)) {
selectors = selector;
@@ -124,6 +233,13 @@ define([
_.each(selectors, function(view, selector) {
view.setElement(this.$(selector)).render();
}, this);
+ },
+ // select temView by index in itemList
+ _selectModelView: function(index) {
+ // select previous or next itemview
+ if (index >= 0) {
+ AdminFieldApp.fieldListView.itemViews[index].clickAction().animate();
+ }
}
});
diff --git a/www/scripts/apps/admin/fields/views/fieldError.js b/www/scripts/apps/admin/fields/views/fieldError.js
new file mode 100644
index 0000000000..38137523a8
--- /dev/null
+++ b/www/scripts/apps/admin/fields/views/fieldError.js
@@ -0,0 +1,38 @@
+define([
+ "jquery",
+ "underscore",
+ "backbone",
+ "i18n"
+], function($, _, Backbone, i18n) {
+ var FieldErrorView = Backbone.View.extend({
+ initialize: function() {
+ AdminFieldApp.errorManager.on("add-error", this.render, this);
+ AdminFieldApp.errorManager.on("remove-error", this.render, this);
+ },
+ render: function() {
+ var messages = [];
+ var errors = AdminFieldApp.errorManager.all();
+
+ _.each(_.groupBy(errors, function(error) {
+ return error.model.get("name");
+ }), function(groupedErrors) {
+ _.each(groupedErrors, function(error) {
+ messages.push(i18n.t("field_error", {
+ postProcess: "sprintf",
+ sprintf: [error.model.get("name")]
+ }));
+ });
+ });
+
+ var template = _.template($("#field_error_template").html(), {
+ messages: messages
+ });
+
+ $(".block-alert").html(template);
+
+ return this;
+ }
+ });
+
+ return FieldErrorView;
+});
diff --git a/www/scripts/apps/admin/fields/views/list.js b/www/scripts/apps/admin/fields/views/list.js
index 031973913f..8aa8263e9e 100644
--- a/www/scripts/apps/admin/fields/views/list.js
+++ b/www/scripts/apps/admin/fields/views/list.js
@@ -1,12 +1,13 @@
define([
- 'jqueryui',
- 'underscore',
- 'backbone',
- 'i18n',
- 'apps/admin/fields/views/listRow',
- 'apps/admin/fields/views/alert',
- 'models/field'
-], function(jqueryui, _, Backbone, i18n, FieldListRowView, AlertView, FieldModel) {
+ "jquery",
+ "jqueryui",
+ "underscore",
+ "backbone",
+ "i18n",
+ "apps/admin/fields/views/listRow",
+ "apps/admin/fields/views/alert",
+ "models/field"
+], function($, jqueryui, _, Backbone, i18n, FieldListRowView, AlertView, FieldModel) {
var FieldListView = Backbone.View.extend({
events: {
"keyup #live_search": "searchAction",
@@ -16,14 +17,35 @@ define([
"update-sort": "updateSortAction"
},
initialize: function() {
- // Store all single rendered views
+ var self = this;
+ // store all single rendered views
this.itemViews = [];
- _.bindAll(this, "render");
// rerender whenever there is a change on the collection
this.collection.bind("reset", this.render, this);
this.collection.bind("add", this.render, this);
this.collection.bind("remove", this.render, this);
+
+ AdminFieldApp.errorManager.on('add-error', function(error) {
+ var model = error.model;
+ var itemView = _.find(self.itemViews, function(view) {
+ return model.get('id') === view.model.get('id');
+ });
+
+ if ('undefined' !== typeof itemView) {
+ itemView.error(true);
+ }
+ });
+
+ AdminFieldApp.errorManager.on('remove-error', function(model) {
+ var itemView = _.find(self.itemViews, function(view) {
+ return model.get('id') === view.model.get('id');
+ });
+
+ if ('undefined' !== typeof itemView) {
+ itemView.error(false);
+ }
+ });
},
render: function() {
var template = _.template($("#item_list_view_template").html(), {});
@@ -35,6 +57,7 @@ define([
this._renderList(this.collection);
$("#new-source", this.$el).autocomplete({
+ minLength: 2,
source: function(request, response) {
$.ajax({
url: "/admin/fields/tags/search",
@@ -54,8 +77,11 @@ define([
}
}).autocomplete("widget").addClass("ui-autocomplete-admin-field");
+ AdminFieldApp.resizeListBlock();
+
return this;
},
+ // render list by appending single item view, also fill itemViews
_renderList: function(fields) {
var that = this;
@@ -63,45 +89,100 @@ define([
this.itemViews = [];
fields.each(function(field) {
+ var fieldErrors = AdminFieldApp.errorManager.getModelError(field);
+
var singleView = new FieldListRowView({
model: field,
- id: 'field-' + field.get('id')
- });
+ id: "field-" + field.get("id")
+ }).error(fieldErrors && fieldErrors.count() > 0);
+
that.$listEl.append(singleView.render().el);
that.itemViews.push(singleView);
});
this.$listEl.sortable({
handle: ".handle",
+ placeholder: "item-list-placeholder",
+ start: function(event, ui) {
+ ui.item.addClass("border-bottom");
+
+ },
stop: function(event, ui) {
- ui.item.trigger('drop', ui.item.index());
+ ui.firstItemPosition = $("li:first", $(this).sortable('widget')).position().top;
+ ui.item.trigger("drop", ui);
}
});
this.$listEl.disableSelection();
- this.$listEl.find('li:last').addClass('last');
+ this.$listEl.find("li:last").addClass("last");
return this;
},
searchAction: function(event) {
this._renderList(this.collection.search($("#live_search", this.$el).val()));
+
+ return this;
},
createAction: function(event) {
var self = this;
+ var formErrors = 0;
var fieldName = $("#new-name", this.$el);
+ var fieldNameValue = fieldName.val();
+ var fieldTag = $("#new-source", this.$el);
+ var fieldTagValue = fieldTag.val();
- if ('' == fieldName.val()) {
- fieldName.closest('.control-group').addClass('error').find('.help-block').empty().append(i18n.t('validation_blank'));
+ // check for empty field name
+ if ("" === fieldNameValue) {
+ fieldName
+ .closest(".control-group")
+ .addClass("error")
+ .find(".help-block")
+ .empty()
+ .append(i18n.t("validation_blank"));
+
+ formErrors++;
+ }
+
+ // check for duplicate field name
+ if ("undefined" !== typeof this.collection.find(function(model){
+ return model.get("name").toLowerCase() === fieldNameValue.toLowerCase();
+ })) {
+ fieldName
+ .closest(".control-group")
+ .addClass("error")
+ .find(".help-block")
+ .empty()
+ .append(i18n.t("validation_name_exists"));
+
+ formErrors++;
+ }
+
+ // check for format tag
+ if ("" !== fieldTagValue && false === /[a-z]+:[a-z0-9]+/i.test(fieldTagValue)) {
+ fieldTag
+ .closest(".control-group")
+ .addClass("error")
+ .find(".help-block")
+ .empty()
+ .append(i18n.t("validation_tag_invalid"));
+
+ formErrors++;
+ }
+
+ if (formErrors > 0 ) {
return;
}
var field = new FieldModel({
"sbas-id": AdminFieldApp.sbas_id,
- "name": fieldName.val(),
- "tag": $("#new-source", this.$el).val(),
- "multi": $("#new-multivalued", this.$el).is(':checked')
+ "name": fieldNameValue,
+ "tag": fieldTagValue,
+ "multi": $("#new-multivalued", this.$el).is(":checked"),
+ "sorter": this.collection.max(function(model) {
+ return model.get("sorter");
+ }).get("sorter") + 1
});
field.save(null, {
@@ -109,36 +190,53 @@ define([
if (response.success) {
self.collection.add(field);
_.last(self.itemViews).clickAction().animate();
- new AlertView({alert: 'success', message: response.message }).render();
- } else {
- new AlertView({alert: 'warning', message: response.message}).render();
}
+
+ new AlertView({
+ alert: response.success ? "success" : "error", message: response.message
+ }).render();
},
error: function(model, xhr, options) {
- new AlertView({alert: 'error', message: i18n.t("something_wrong")}).render();
+ new AlertView({
+ alert: "error", message: i18n.t("something_wrong")}
+ ).render();
+
self.toggleCreateFormAction();
}
});
+
+ return this;
},
toggleCreateFormAction: function(event) {
- $('.add-field-block', this.$el).toggle();
+ var fieldBlock = $(".add-field-block", this.$el);
+
+ fieldBlock.is(":hidden") ? fieldBlock.show() : fieldBlock.hide();
+ AdminFieldApp.resizeListBlock();
+
+ return this;
},
- updateSortAction: function(event, model, position) {
+ updateSortAction: function(event, model, ui) {
+ var position = ui.item.index();
this.collection.remove(model, {silent: true});
+ // reorder all collection model
this.collection.each(function(model, index) {
var ordinal = index;
if (index >= position) ordinal += 1;
- model.set('sorter', ordinal);
+ model.set("sorter", ordinal);
});
- model.set('sorter', position);
+ model.set("sorter", position);
this.collection.add(model, {at: position});
- // update edit view
+ this.itemViews[0].animate(Math.abs(ui.firstItemPosition));
+
+ // update edit view model
AdminFieldApp.fieldEditView.model = this.collection.find(function(el) {
- return el.get('id') === AdminFieldApp.fieldEditView.model.get('id');
+ return el.get("id") === AdminFieldApp.fieldEditView.model.get("id");
});
+
+ return this;
}
});
diff --git a/www/scripts/apps/admin/fields/views/listRow.js b/www/scripts/apps/admin/fields/views/listRow.js
index 3195a2e343..a94812ff93 100644
--- a/www/scripts/apps/admin/fields/views/listRow.js
+++ b/www/scripts/apps/admin/fields/views/listRow.js
@@ -1,15 +1,15 @@
define([
- 'underscore',
- 'backbone',
- 'apps/admin/fields/views/edit',
- 'apps/admin/fields/views/alert'
-], function(_, Backbone, FieldEditView, AlertView) {
+ "jquery",
+ "underscore",
+ "backbone",
+ "apps/admin/fields/views/edit"
+], function($, _, Backbone, FieldEditView) {
var FieldListRowView = Backbone.View.extend({
tagName: "li",
className: "field-row",
initialize: function() {
// destroy view is model is deleted
- this.model.on('destroy', this.remove, this);
+ this.model.on("destroy", this.remove, this);
},
events : {
"click .trigger-click": "clickAction",
@@ -17,51 +17,71 @@ define([
},
clickAction: function (e) {
this.select();
- // first click create view else update model's view
- if (typeof AdminFieldApp.fieldEditView === 'undefined') {
+ // first click create edit view else update model"s view
+ if (typeof AdminFieldApp.fieldEditView === "undefined") {
AdminFieldApp.fieldEditView = new FieldEditView({
- el: $('.right-block')[0],
+ el: AdminFieldApp.$rightBlock,
model: this.model
});
} else {
- AdminFieldApp.fieldEditView.model = this.model;
+ AdminFieldApp.fieldEditView.updateModel(this.model).initialize();
}
AdminFieldApp.fieldEditView.render();
return this;
},
- dropAction: function(event, index) {
- this.$el.trigger('update-sort', [this.model, index]);
+ dropAction: function(event, ui) {
+ this.$el.trigger("update-sort", [this.model, ui]);
+
+ return this;
},
render: function() {
var template = _.template($("#list_row_template").html(), {
- id: this.model.get('id'),
- position: this.model.get('sorter'),
- name: this.model.get('name'),
- tag: this.model.get('tag')
+ id: this.model.get("id"),
+ position: this.model.get("sorter"),
+ name: this.model.get("name"),
+ tag: this.model.get("tag")
});
this.$el.empty().html(template);
- if (AdminFieldApp.fieldEditView && AdminFieldApp.fieldEditView.model.get('id') === this.model.get('id')) {
+ // highlight view if edit view model match current view model
+ if (AdminFieldApp.fieldEditView
+ && AdminFieldApp.fieldEditView.model.get("id") === this.model.get("id")) {
this.select();
}
+
return this;
},
- // set selected class
+ // set selected class to current view
select: function () {
- $("li", this.$el.closest('ul')).removeClass('selected');
- this.$el.addClass('selected');
+ $("li", this.$el.closest("ul")).removeClass("selected");
+ this.$el.addClass("selected");
return this;
},
- animate: function () {
- var offset = this.$el.offset();
+ // scroll to current view in item list
+ animate: function (top) {
+ top = top || null;
+
+ if (null === top) {
+ top = $(".field-row").index(this.$el) * this.$el.height();
+ }
- this.$el.closest('div').animate({
- scrollTop: offset.top - 20
- });
+ this.$el.closest("div").scrollTop(top);
+
+ return this;
+ },
+ // add error class to item
+ error: function (errored) {
+ if (errored) {
+ this.$el.addClass("error");
+ } else {
+ this.$el.removeClass("error");
+ }
+
+ return this;
}
});
diff --git a/www/scripts/apps/admin/fields/views/modal.js b/www/scripts/apps/admin/fields/views/modal.js
index 5e7c504cb6..d6fc73b99a 100644
--- a/www/scripts/apps/admin/fields/views/modal.js
+++ b/www/scripts/apps/admin/fields/views/modal.js
@@ -1,19 +1,20 @@
define([
- 'underscore',
- 'backbone',
- 'i18n',
- 'bootstrap'
-], function(_, Backbone, i18n, bootstrap) {
+ "jquery",
+ "underscore",
+ "backbone",
+ "i18n",
+ "bootstrap"
+], function($, _, Backbone, i18n, bootstrap) {
var ModalView = Backbone.View.extend({
tagName: "div",
className: "modal",
events: {
- 'click .confirm': 'confirmAction'
+ "click .confirm": "confirmAction"
},
initialize: function (options) {
var self = this;
// remove view when modal is closed
- this.$el.on('hidden', function() {
+ this.$el.on("hidden", function() {
self.remove();
});
@@ -22,8 +23,8 @@ define([
}
},
render: function() {
- var template = _.template($("#modal_delete_confirm_template").html(), {
- msg: this.message || ''
+ var template = _.template($("#modal_template").html(), {
+ msg: this.message || ""
});
this.$el.html(template).modal();
@@ -31,8 +32,8 @@ define([
return this;
},
confirmAction: function () {
- this.trigger('modal:confirm');
- this.$el.modal('hide');
+ this.trigger("modal:confirm");
+ this.$el.modal("hide");
this.remove();
return this;
diff --git a/www/scripts/apps/admin/fields/views/save.js b/www/scripts/apps/admin/fields/views/save.js
new file mode 100644
index 0000000000..bf993075d4
--- /dev/null
+++ b/www/scripts/apps/admin/fields/views/save.js
@@ -0,0 +1,95 @@
+define([
+ "jquery",
+ "underscore",
+ "backbone",
+ "i18n",
+ "bootstrap",
+ "apps/admin/fields/views/alert"
+], function($, _, Backbone, i18n, bootstrap, AlertView) {
+ var SaveView = Backbone.View.extend({
+ initialize: function() {
+ var self = this;
+ this.previousAttributes = [];
+ this.$overlay = null;
+
+ AdminFieldApp.errorManager.on("add-error", function(errors) {
+ self._disableSaveButton(true);
+ });
+
+ AdminFieldApp.errorManager.on("no-error", function() {
+ self._disableSaveButton(false);
+ });
+ },
+ events: {
+ "click button.save-all" : "clickSaveAction"
+ },
+ clickSaveAction: function(event) {
+ var self = this;
+
+ if (this._isModelDesync()) {
+ this._loadingState(true);
+ AdminFieldApp.fieldsCollection.save({
+ success: function(response) {
+ // reset collection with new one
+ if (response.success) {
+ AdminFieldApp.fieldsCollection.reset(response.fields);
+ }
+
+ new AlertView({
+ alert: response.success ? "success" : "error",
+ message: response.messages.join("
")
+ }).render();
+ },
+ error: function(model, xhr, options) {
+ new AlertView({
+ alert: "error", message: i18n.t("something_wrong")
+ }).render();
+ }
+ }).done(function() {
+ self._loadingState(false);
+ });
+ }
+
+ return this;
+ },
+ render: function () {
+ var template = _.template($("#save_template").html());
+ this.$el.html(template);
+
+ return this;
+ },
+ // check whether model has changed or not
+ _isModelDesync: function () {
+ return "undefined" !== typeof AdminFieldApp.fieldsCollection.find(function(model) {
+ return !_.isEmpty(model.previousAttributes());
+ });
+ },
+ // create a transparent overlay on top of the application
+ _overlay: function(showOrHide) {
+ if(showOrHide && !this.$overlay) {
+ this.$overlay = $("
").addClass("overlay");
+ AdminFieldApp.$bottom.append(this.$overlay);
+ } else if (!showOrHide && this.$overlay) {
+ this.$overlay.remove();
+ this.$overlay = null;
+ }
+ },
+ _disableSaveButton: function (active) {
+ $("button.save-all", this.$el).attr("disabled", active);
+ },
+ // put application on loading state (add overlay, add spinner, disable global save button)
+ _loadingState: function(active) {
+ if (active) {
+ $(".save-block", AdminFieldApp.$top).addClass("loading");
+ $(".block-alert", AdminFieldApp.$top).empty();
+ } else {
+ $(".save-block", AdminFieldApp.$top).removeClass("loading");
+ }
+
+ this._disableSaveButton(active);
+ this._overlay(active);
+ }
+ });
+
+ return SaveView;
+});
diff --git a/www/scripts/models/dcField.js b/www/scripts/models/dcField.js
index fa9ab138ba..8c60b959cc 100644
--- a/www/scripts/models/dcField.js
+++ b/www/scripts/models/dcField.js
@@ -1,10 +1,10 @@
define([
- 'underscore',
- 'backbone'
+ "underscore",
+ "backbone"
], function(_, Backbone) {
var DcFieldModel = Backbone.Model.extend({
urlRoot: function () {
- return '/admin/fields/dc-fields';
+ return "/admin/fields/dc-fields";
}
});
diff --git a/www/scripts/models/field.js b/www/scripts/models/field.js
index 2881228e98..1dc22254e5 100644
--- a/www/scripts/models/field.js
+++ b/www/scripts/models/field.js
@@ -1,6 +1,6 @@
define([
- 'underscore',
- 'backbone'
+ "underscore",
+ "backbone"
], function(_, Backbone) {
var FieldModel = Backbone.Model.extend({
initialize : function(attributes, options) {
@@ -9,7 +9,7 @@ define([
}
},
urlRoot: function () {
- return '/admin/fields/'+ this.get('sbas-id') +'/fields';
+ return "/admin/fields/"+ this.get("sbas-id") +"/fields";
},
defaults: {
"business": false,
diff --git a/www/scripts/models/vocabulary.js b/www/scripts/models/vocabulary.js
index 7dcc9439ce..1eb31e0bd1 100644
--- a/www/scripts/models/vocabulary.js
+++ b/www/scripts/models/vocabulary.js
@@ -1,10 +1,10 @@
define([
- 'underscore',
- 'backbone'
+ "underscore",
+ "backbone"
], function(_, Backbone) {
var VocabularyModel = Backbone.Model.extend({
urlRoot: function () {
- return '/admin/fields/vocabularies';
+ return "/admin/fields/vocabularies";
}
});
diff --git a/www/scripts/tests/baseTest.js b/www/scripts/tests/baseTest.js
index ca57e9a75f..660954f6f5 100644
--- a/www/scripts/tests/baseTest.js
+++ b/www/scripts/tests/baseTest.js
@@ -1,4 +1,4 @@
-define(['chai'], function(shai) {
+define(["chai"], function(shai) {
window.expect = shai.expect;
window.assert = shai.assert;
});
diff --git a/www/scripts/tests/main.js b/www/scripts/tests/main.js
index e6fec04564..de6ba69700 100644
--- a/www/scripts/tests/main.js
+++ b/www/scripts/tests/main.js
@@ -1,8 +1,8 @@
require.config({
baseUrl: "../../scripts",
paths: {
- specs: 'tests/specs',
- chai: '../assets/chai/chai'
+ specs: "tests/specs",
+ chai: "../assets/chai/chai"
},
shim : {
shai: {
@@ -12,7 +12,7 @@ require.config({
});
mocha.setup({
- ui: 'bdd',
+ ui: "bdd",
ignoreLeaks: true
});
diff --git a/www/scripts/tests/specs/admin/fields.js b/www/scripts/tests/specs/admin/fields.js
index 643d16d910..5f2a358eef 100644
--- a/www/scripts/tests/specs/admin/fields.js
+++ b/www/scripts/tests/specs/admin/fields.js
@@ -1,5 +1,5 @@
define(function(require) {
- it('should run', function () {
+ it("should run", function () {
expect(true).to.equal(true);
});
});
diff --git a/www/skins/admin/css/fields.css b/www/skins/admin/css/fields.css
index fee0e093cf..6432ded324 100644
--- a/www/skins/admin/css/fields.css
+++ b/www/skins/admin/css/fields.css
@@ -1,69 +1,119 @@
+#admin-field-app .row-top {
+ min-height: 60px;
+ border-bottom: 1px solid #000;
+}
+
+#admin-field-app .row-bottom {
+ position: relative;
+}
+
+#admin-field-app .right-block {
+ border-left: 1px dashed #000;
+ overflow: auto;
+ min-height: 200px;
+}
+
#admin-field-app h4 {
padding: 10px 0;
}
-#admin-field-app li {
+#admin-field-app .left-block li {
background: #FFF;
border-top: 1px solid #ccc;
border-left: 1px solid #ccc;
border-right: 1px solid #ccc;
+ height: 55px;
}
-#admin-field-app li table {
- table-layout:fixed;
- width:100%;
+#admin-field-app .left-block li .trigger-click {
+ cursor: pointer;
}
-#admin-field-app li .field-name {
+#admin-field-app .left-block li.border-bottom{
+ border-bottom: 1px solid #ccc;
+}
+
+#admin-field-app .left-block li table {
+ table-layout: fixed;
+ width: 100%;
+}
+
+#admin-field-app .left-block li .field-name {
font-weight: bold;
font-size: 16px;
color: #666;
}
-#admin-field-app li .handle {
+#admin-field-app .left-block li .handle {
width: 10%;
- vertical-align:middle;
- text-align:center;
+ vertical-align: middle;
+ text-align: center;
cursor: move;
}
-#admin-field-app li .trigger-click {
- padding:10px;
+#admin-field-app .left-block li .trigger-click {
+ padding: 10px;
}
-#admin-field-app li .position {
+#admin-field-app .left-block li .position {
width: 10%;
- vertical-align:bottom;
- text-align: center
+ vertical-align: bottom;
+ text-align: center;
}
-#admin-field-app li .chip {
- width:10%;
+#admin-field-app .left-block li .chip {
+ width: 10%;
}
-#admin-field-app li .handle, #admin-field-app li .position {
+#admin-field-app .left-block li .handle, #admin-field-app li .position {
color: #ccc;
border-right: 1px solid #ccc;
}
-#admin-field-app li.last {
+#admin-field-app .left-block li.last {
border-bottom: 1px solid #ccc;
}
-#admin-field-app li.selected {
+#admin-field-app .left-block li.selected {
border-top-color: #0080FF;
background: #FFF;
color: #000;
}
-#admin-field-app li.selected + li {
+#admin-field-app .left-block li.last.selected {
+ border-bottom-color: #0080FF;
+}
+
+#admin-field-app .left-block li.last.selected.error {
+ border-bottom-color: #9d261d;
+}
+
+#admin-field-app .left-block li.selected + li {
border-top-color: #0080FF;
}
-#admin-field-app li.selected .field-name {
+#admin-field-app .left-block li.selected.error {
+ border-top-color: #9d261d;
+}
+
+#admin-field-app .left-block li.selected.error + li {
+ border-top-color: #9d261d;
+}
+
+#admin-field-app .left-block li.selected .field-name {
color: #0080FF;
}
+#admin-field-app .left-block li.error .field-name {
+ color: #9d261d;
+}
+
+#admin-field-app .item-list-placeholder {
+ border-top: 1px solid #ccc;
+ background-color: red;
+ height: 70px;
+}
+
#admin-field-app .add-field-block .control-label {
width: 80px;
text-align: left;
@@ -85,13 +135,42 @@
margin: 20px 0;
}
-#admin-field-app #collection-fields {
- height:450px;
+#admin-field-app .list-block {
+ height: 450px;
+ min-height: 130px;
overflow: auto;
+ position: relative;
}
#admin-field-app .edit-block {
- padding: 5px 20px
+ padding: 5px 20px;
+}
+
+#admin-field-app .edit-block table {
+ table-layout: fixed;
+ width: 100%;
+}
+
+#admin-field-app .edit-block table label {
+ margin: 0px;
+ width: auto;
+}
+
+#admin-field-app .edit-block table td:first-child {
+ width: 130px;
+}
+
+#admin-field-app .edit-block td {
+ height: 25px;
+}
+
+#admin-field-app .edit-block .dces-help-block {
+ height: auto;
+}
+
+#admin-field-app .info {
+ color: #aaa;
+ padding: 10px;
}
#admin-field-app .edit-block .edit-order {
@@ -103,17 +182,38 @@
}
#admin-field-app .edit-block input#name {
- font-size:28px;
- color:#0080FF;
- height:42px;
- line-height:42px;
- font-weight:bold
+ font-size: 28px;
+ color: #0080FF;
+ height: 42px;
+ line-height: 42px;
+ font-weight: bold
}
+#admin-field-app .overlay {
+ zoom: 1;
+ filter: alpha(opacity=50);
+ opacity: 0.5;
+ background: #fff;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ z-index: 2000;
+}
+
+#admin-field-app .list-field-error li {
+ color: #9d261d;
+}
+
+#admin-field-app .save-block.loading {
+ background: url('/skins/icons/loaderFFF.gif') #fff no-repeat center right;
+}
+
+/* jquery ui autocomplete style */
.ui-autocomplete-admin-field {
list-style-type: none;
- overflow-y: scroll;
- height: 180px;
+ overflow-y: auto;
+ overflow-x: hidden;
+ max-height: 180px;
background: #FFF;
max-width: 300px;
-webkit-box-shadow: 0 10px 6px -6px #777;
@@ -121,10 +221,25 @@
box-shadow: 0 10px 6px -6px #777;
}
-.ui-autocomplete-admin-field li{
- padding: 3px;
+.ui-autocomplete-admin-field li {
+ padding: 5px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.ui-autocomplete-admin-field li:hover {
+ padding: 5px;
+ background: #ccc;
}
.ui-autocomplete-admin-field li a {
text-decoration: none;
}
+
+.ui-autocomplete-admin-field li a:hover {
+ text-decoration: none;
+ background: none;
+ color: #000;
+ border: none;
+}