Browse code

new build from home

Louis Jonget authored on18/03/2023 14:55:56
Showing1 changed files
... ...
@@ -1,314 +1,314 @@
1
-'use strict';
2
-
3
-/**
4
- * A Clay config Item
5
- * @typedef {Object} Clay~ConfigItem
6
- * @property {string} type
7
- * @property {string|boolean|number} defaultValue
8
- * @property {string} [messageKey]
9
- * @property {string} [id]
10
- * @property {string} [label]
11
- * @property {Object} [attributes]
12
- * @property {Array} [options]
13
- * @property {Array} [items]
14
- * @property {Array} [capabilities]
15
- */
16
-
17
-var HTML = require('../vendor/minified').HTML;
18
-var _ = require('../vendor/minified')._;
19
-var ClayItem = require('./clay-item');
20
-var utils = require('../lib/utils');
21
-var ClayEvents = require('./clay-events');
22
-var componentStore = require('./component-registry');
23
-var manipulators = require('./manipulators');
24
-
25
-/**
26
- * @extends ClayEvents
27
- * @param {Object} settings - setting that were set from a previous session
28
- * @param {Array|Object} config
29
- * @param {M} $rootContainer
30
- * @param {Object} meta
31
- * @constructor
32
- */
33
-function ClayConfig(settings, config, $rootContainer, meta) {
34
-  var self = this;
35
-
36
-  var _settings = _.copyObj(settings);
37
-  var _items;
38
-  var _itemsById;
39
-  var _itemsByMessageKey;
40
-  var _isBuilt;
41
-
42
-  /**
43
-   * Initialize the item arrays and objects
44
-   * @private
45
-   * @return {void}
46
-   */
47
-  function _initializeItems() {
48
-    _items = [];
49
-    _itemsById = {};
50
-    _itemsByMessageKey = {};
51
-    _isBuilt = false;
52
-  }
53
-
54
-  /**
55
-   * Add item(s) to the config
56
-   * @param {Clay~ConfigItem|Array} item
57
-   * @param {M} $container
58
-   * @return {void}
59
-   */
60
-  function _addItems(item, $container) {
61
-    if (Array.isArray(item)) {
62
-      item.forEach(function(item) {
63
-        _addItems(item, $container);
64
-      });
65
-    } else if (utils.includesCapability(meta.activeWatchInfo, item.capabilities)) {
66
-      if (item.type === 'section') {
67
-        var $wrapper = HTML('<div class="section">');
68
-        $container.add($wrapper);
69
-        _addItems(item.items, $wrapper);
70
-      } else {
71
-        var _item = _.copyObj(item);
72
-        _item.clayId = _items.length;
73
-
74
-        var clayItem = new ClayItem(_item).initialize(self);
75
-
76
-        if (_item.id) {
77
-          _itemsById[_item.id] = clayItem;
78
-        }
79
-
80
-        if (_item.messageKey) {
81
-          _itemsByMessageKey[_item.messageKey] = clayItem;
82
-        }
83
-
84
-        _items.push(clayItem);
85
-
86
-        // set the value of the item via the manipulator to ensure consistency
87
-        var value = typeof _settings[_item.messageKey] !== 'undefined' ?
88
-          _settings[_item.messageKey] :
89
-          _item.defaultValue;
90
-
91
-        clayItem.set(typeof value !== 'undefined' ? value : '');
92
-
93
-        $container.add(clayItem.$element);
94
-      }
95
-    }
96
-  }
97
-
98
-  /**
99
-   * Throws if the config has not been built yet.
100
-   * @param {string} fnName
101
-   * @returns {boolean}
102
-   * @private
103
-   */
104
-  function _checkBuilt(fnName) {
105
-    if (!_isBuilt) {
106
-      throw new Error(
107
-        'ClayConfig not built. build() must be run before ' +
108
-        'you can run ' + fnName + '()'
109
-      );
110
-    }
111
-    return true;
112
-  }
113
-
114
-  self.meta = meta;
115
-  self.$rootContainer = $rootContainer;
116
-
117
-  self.EVENTS = {
118
-    /**
119
-     * Called before framework has initialized. This is when you would attach your
120
-     * custom components.
121
-     * @const
122
-     */
123
-    BEFORE_BUILD: 'BEFORE_BUILD',
124
-
125
-    /**
126
-     * Called after the config has been parsed and all components have their initial
127
-     * value set
128
-     * @const
129
-     */
130
-    AFTER_BUILD: 'AFTER_BUILD',
131
-
132
-    /**
133
-     * Called if .build() is executed after the page has already been built and
134
-     * before the existing content is destroyed
135
-     * @const
136
-     */
137
-    BEFORE_DESTROY: 'BEFORE_DESTROY',
138
-
139
-    /**
140
-     * Called if .build() is executed after the page has already been built and after
141
-     * the existing content is destroyed
142
-     * @const
143
-     */
144
-    AFTER_DESTROY: 'AFTER_DESTROY'
145
-  };
146
-  utils.updateProperties(self.EVENTS, {writable: false});
147
-
148
-  /**
149
-   * @returns {Array.<ClayItem>}
150
-   */
151
-  self.getAllItems = function() {
152
-    _checkBuilt('getAllItems');
153
-    return _items;
154
-  };
155
-
156
-  /**
157
-   * @param {string} messageKey
158
-   * @returns {ClayItem}
159
-   */
160
-  self.getItemByMessageKey = function(messageKey) {
161
-    _checkBuilt('getItemByMessageKey');
162
-    return _itemsByMessageKey[messageKey];
163
-  };
164
-
165
-  /**
166
-   * @param {string} id
167
-   * @returns {ClayItem}
168
-   */
169
-  self.getItemById = function(id) {
170
-    _checkBuilt('getItemById');
171
-    return _itemsById[id];
172
-  };
173
-
174
-  /**
175
-   * @param {string} type
176
-   * @returns {Array.<ClayItem>}
177
-   */
178
-  self.getItemsByType = function(type) {
179
-    _checkBuilt('getItemsByType');
180
-    return _items.filter(function(item) {
181
-      return item.config.type === type;
182
-    });
183
-  };
184
-
185
-  /**
186
-   * @param {string} group
187
-   * @returns {Array.<ClayItem>}
188
-   */
189
-  self.getItemsByGroup = function(group) {
190
-    _checkBuilt('getItemsByGroup');
191
-    return _items.filter(function(item) {
192
-      return item.config.group === group;
193
-    });
194
-  };
195
-
196
-  /**
197
-   * @returns {Object}
198
-   */
199
-  self.serialize = function() {
200
-    _checkBuilt('serialize');
201
-
202
-    _settings = {};
203
-
204
-    _.eachObj(_itemsByMessageKey, function(messageKey, item) {
205
-      _settings[messageKey] = {
206
-        value: item.get()
207
-      };
208
-
209
-      if (item.precision) {
210
-        _settings[messageKey].precision = item.precision;
211
-      }
212
-    });
213
-    return _settings;
214
-  };
215
-
216
-  // @todo maybe don't do this and force the static method
217
-  self.registerComponent = ClayConfig.registerComponent;
218
-
219
-  /**
220
-   * Empties the root container
221
-   * @returns {ClayConfig}
222
-   */
223
-  self.destroy = function() {
224
-    var el = $rootContainer[0];
225
-    self.trigger(self.EVENTS.BEFORE_DESTROY);
226
-    while (el.firstChild) {
227
-      el.removeChild(el.firstChild);
228
-    }
229
-    _initializeItems();
230
-    self.trigger(self.EVENTS.AFTER_DESTROY);
231
-    return self;
232
-  };
233
-
234
-  /**
235
-   * Build the config page. This must be run before any of the get methods can be run
236
-   * If you call this method after the page has already been built, teh page will be
237
-   * destroyed and built again.
238
-   * @returns {ClayConfig}
239
-   */
240
-  self.build = function() {
241
-    if (_isBuilt) {
242
-      self.destroy();
243
-    }
244
-    self.trigger(self.EVENTS.BEFORE_BUILD);
245
-    _addItems(self.config, $rootContainer);
246
-    _isBuilt = true;
247
-    self.trigger(self.EVENTS.AFTER_BUILD);
248
-    return self;
249
-  };
250
-
251
-  _initializeItems();
252
-
253
-  // attach event methods
254
-  ClayEvents.call(self, $rootContainer);
255
-
256
-  // prevent external modifications of properties
257
-  utils.updateProperties(self, { writable: false, configurable: false });
258
-
259
-  // expose the config to allow developers to update it before the build is run
260
-  self.config = config;
261
-}
262
-
263
-/**
264
- * Register a component to Clay. This must be called prior to .build();
265
- * @param {Object} component - the clay component to register
266
- * @param {string} component.name - the name of the component
267
- * @param {string} component.template - HTML template to use for the component
268
- * @param {string|Object} component.manipulator - methods to attach to the component
269
- * @param {function} component.manipulator.set - set manipulator method
270
- * @param {function} component.manipulator.get - get manipulator method
271
- * @param {Object} [component.defaults] - template defaults
272
- * @param {function} [component.initialize] - method to scaffold the component
273
- * @return {boolean} - Returns true if component was registered correctly
274
- */
275
-ClayConfig.registerComponent = function(component) {
276
-  var _component = _.copyObj(component);
277
-
278
-  if (componentStore[_component.name]) {
279
-    console.warn('Component: ' + _component.name +
280
-                 ' is already registered. If you wish to override the existing' +
281
-                 ' functionality, you must provide a new name');
282
-    return false;
283
-  }
284
-
285
-  if (typeof _component.manipulator === 'string') {
286
-    _component.manipulator = manipulators[component.manipulator];
287
-
288
-    if (!_component.manipulator) {
289
-      throw new Error('The manipulator: ' + component.manipulator +
290
-                      ' does not exist in the built-in manipulators.');
291
-    }
292
-  }
293
-
294
-  if (!_component.manipulator) {
295
-    throw new Error('The manipulator must be defined');
296
-  }
297
-
298
-  if (typeof _component.manipulator.set !== 'function' ||
299
-      typeof _component.manipulator.get !== 'function') {
300
-    throw new Error('The manipulator must have both a `get` and `set` method');
301
-  }
302
-
303
-  if (_component.style) {
304
-    var style = document.createElement('style');
305
-    style.type = 'text/css';
306
-    style.appendChild(document.createTextNode(_component.style));
307
-    document.head.appendChild(style);
308
-  }
309
-
310
-  componentStore[_component.name] = _component;
311
-  return true;
312
-};
313
-
314
-module.exports = ClayConfig;
1
+'use strict';
2
+
3
+/**
4
+ * A Clay config Item
5
+ * @typedef {Object} Clay~ConfigItem
6
+ * @property {string} type
7
+ * @property {string|boolean|number} defaultValue
8
+ * @property {string} [messageKey]
9
+ * @property {string} [id]
10
+ * @property {string} [label]
11
+ * @property {Object} [attributes]
12
+ * @property {Array} [options]
13
+ * @property {Array} [items]
14
+ * @property {Array} [capabilities]
15
+ */
16
+
17
+var HTML = require('../vendor/minified').HTML;
18
+var _ = require('../vendor/minified')._;
19
+var ClayItem = require('./clay-item');
20
+var utils = require('../lib/utils');
21
+var ClayEvents = require('./clay-events');
22
+var componentStore = require('./component-registry');
23
+var manipulators = require('./manipulators');
24
+
25
+/**
26
+ * @extends ClayEvents
27
+ * @param {Object} settings - setting that were set from a previous session
28
+ * @param {Array|Object} config
29
+ * @param {M} $rootContainer
30
+ * @param {Object} meta
31
+ * @constructor
32
+ */
33
+function ClayConfig(settings, config, $rootContainer, meta) {
34
+  var self = this;
35
+
36
+  var _settings = _.copyObj(settings);
37
+  var _items;
38
+  var _itemsById;
39
+  var _itemsByMessageKey;
40
+  var _isBuilt;
41
+
42
+  /**
43
+   * Initialize the item arrays and objects
44
+   * @private
45
+   * @return {void}
46
+   */
47
+  function _initializeItems() {
48
+    _items = [];
49
+    _itemsById = {};
50
+    _itemsByMessageKey = {};
51
+    _isBuilt = false;
52
+  }
53
+
54
+  /**
55
+   * Add item(s) to the config
56
+   * @param {Clay~ConfigItem|Array} item
57
+   * @param {M} $container
58
+   * @return {void}
59
+   */
60
+  function _addItems(item, $container) {
61
+    if (Array.isArray(item)) {
62
+      item.forEach(function(item) {
63
+        _addItems(item, $container);
64
+      });
65
+    } else if (utils.includesCapability(meta.activeWatchInfo, item.capabilities)) {
66
+      if (item.type === 'section') {
67
+        var $wrapper = HTML('<div class="section">');
68
+        $container.add($wrapper);
69
+        _addItems(item.items, $wrapper);
70
+      } else {
71
+        var _item = _.copyObj(item);
72
+        _item.clayId = _items.length;
73
+
74
+        var clayItem = new ClayItem(_item).initialize(self);
75
+
76
+        if (_item.id) {
77
+          _itemsById[_item.id] = clayItem;
78
+        }
79
+
80
+        if (_item.messageKey) {
81
+          _itemsByMessageKey[_item.messageKey] = clayItem;
82
+        }
83
+
84
+        _items.push(clayItem);
85
+
86
+        // set the value of the item via the manipulator to ensure consistency
87
+        var value = typeof _settings[_item.messageKey] !== 'undefined' ?
88
+          _settings[_item.messageKey] :
89
+          _item.defaultValue;
90
+
91
+        clayItem.set(typeof value !== 'undefined' ? value : '');
92
+
93
+        $container.add(clayItem.$element);
94
+      }
95
+    }
96
+  }
97
+
98
+  /**
99
+   * Throws if the config has not been built yet.
100
+   * @param {string} fnName
101
+   * @returns {boolean}
102
+   * @private
103
+   */
104
+  function _checkBuilt(fnName) {
105
+    if (!_isBuilt) {
106
+      throw new Error(
107
+        'ClayConfig not built. build() must be run before ' +
108
+        'you can run ' + fnName + '()'
109
+      );
110
+    }
111
+    return true;
112
+  }
113
+
114
+  self.meta = meta;
115
+  self.$rootContainer = $rootContainer;
116
+
117
+  self.EVENTS = {
118
+    /**
119
+     * Called before framework has initialized. This is when you would attach your
120
+     * custom components.
121
+     * @const
122
+     */
123
+    BEFORE_BUILD: 'BEFORE_BUILD',
124
+
125
+    /**
126
+     * Called after the config has been parsed and all components have their initial
127
+     * value set
128
+     * @const
129
+     */
130
+    AFTER_BUILD: 'AFTER_BUILD',
131
+
132
+    /**
133
+     * Called if .build() is executed after the page has already been built and
134
+     * before the existing content is destroyed
135
+     * @const
136
+     */
137
+    BEFORE_DESTROY: 'BEFORE_DESTROY',
138
+
139
+    /**
140
+     * Called if .build() is executed after the page has already been built and after
141
+     * the existing content is destroyed
142
+     * @const
143
+     */
144
+    AFTER_DESTROY: 'AFTER_DESTROY'
145
+  };
146
+  utils.updateProperties(self.EVENTS, {writable: false});
147
+
148
+  /**
149
+   * @returns {Array.<ClayItem>}
150
+   */
151
+  self.getAllItems = function() {
152
+    _checkBuilt('getAllItems');
153
+    return _items;
154
+  };
155
+
156
+  /**
157
+   * @param {string} messageKey
158
+   * @returns {ClayItem}
159
+   */
160
+  self.getItemByMessageKey = function(messageKey) {
161
+    _checkBuilt('getItemByMessageKey');
162
+    return _itemsByMessageKey[messageKey];
163
+  };
164
+
165
+  /**
166
+   * @param {string} id
167
+   * @returns {ClayItem}
168
+   */
169
+  self.getItemById = function(id) {
170
+    _checkBuilt('getItemById');
171
+    return _itemsById[id];
172
+  };
173
+
174
+  /**
175
+   * @param {string} type
176
+   * @returns {Array.<ClayItem>}
177
+   */
178
+  self.getItemsByType = function(type) {
179
+    _checkBuilt('getItemsByType');
180
+    return _items.filter(function(item) {
181
+      return item.config.type === type;
182
+    });
183
+  };
184
+
185
+  /**
186
+   * @param {string} group
187
+   * @returns {Array.<ClayItem>}
188
+   */
189
+  self.getItemsByGroup = function(group) {
190
+    _checkBuilt('getItemsByGroup');
191
+    return _items.filter(function(item) {
192
+      return item.config.group === group;
193
+    });
194
+  };
195
+
196
+  /**
197
+   * @returns {Object}
198
+   */
199
+  self.serialize = function() {
200
+    _checkBuilt('serialize');
201
+
202
+    _settings = {};
203
+
204
+    _.eachObj(_itemsByMessageKey, function(messageKey, item) {
205
+      _settings[messageKey] = {
206
+        value: item.get()
207
+      };
208
+
209
+      if (item.precision) {
210
+        _settings[messageKey].precision = item.precision;
211
+      }
212
+    });
213
+    return _settings;
214
+  };
215
+
216
+  // @todo maybe don't do this and force the static method
217
+  self.registerComponent = ClayConfig.registerComponent;
218
+
219
+  /**
220
+   * Empties the root container
221
+   * @returns {ClayConfig}
222
+   */
223
+  self.destroy = function() {
224
+    var el = $rootContainer[0];
225
+    self.trigger(self.EVENTS.BEFORE_DESTROY);
226
+    while (el.firstChild) {
227
+      el.removeChild(el.firstChild);
228
+    }
229
+    _initializeItems();
230
+    self.trigger(self.EVENTS.AFTER_DESTROY);
231
+    return self;
232
+  };
233
+
234
+  /**
235
+   * Build the config page. This must be run before any of the get methods can be run
236
+   * If you call this method after the page has already been built, teh page will be
237
+   * destroyed and built again.
238
+   * @returns {ClayConfig}
239
+   */
240
+  self.build = function() {
241
+    if (_isBuilt) {
242
+      self.destroy();
243
+    }
244
+    self.trigger(self.EVENTS.BEFORE_BUILD);
245
+    _addItems(self.config, $rootContainer);
246
+    _isBuilt = true;
247
+    self.trigger(self.EVENTS.AFTER_BUILD);
248
+    return self;
249
+  };
250
+
251
+  _initializeItems();
252
+
253
+  // attach event methods
254
+  ClayEvents.call(self, $rootContainer);
255
+
256
+  // prevent external modifications of properties
257
+  utils.updateProperties(self, { writable: false, configurable: false });
258
+
259
+  // expose the config to allow developers to update it before the build is run
260
+  self.config = config;
261
+}
262
+
263
+/**
264
+ * Register a component to Clay. This must be called prior to .build();
265
+ * @param {Object} component - the clay component to register
266
+ * @param {string} component.name - the name of the component
267
+ * @param {string} component.template - HTML template to use for the component
268
+ * @param {string|Object} component.manipulator - methods to attach to the component
269
+ * @param {function} component.manipulator.set - set manipulator method
270
+ * @param {function} component.manipulator.get - get manipulator method
271
+ * @param {Object} [component.defaults] - template defaults
272
+ * @param {function} [component.initialize] - method to scaffold the component
273
+ * @return {boolean} - Returns true if component was registered correctly
274
+ */
275
+ClayConfig.registerComponent = function(component) {
276
+  var _component = _.copyObj(component);
277
+
278
+  if (componentStore[_component.name]) {
279
+    console.warn('Component: ' + _component.name +
280
+                 ' is already registered. If you wish to override the existing' +
281
+                 ' functionality, you must provide a new name');
282
+    return false;
283
+  }
284
+
285
+  if (typeof _component.manipulator === 'string') {
286
+    _component.manipulator = manipulators[component.manipulator];
287
+
288
+    if (!_component.manipulator) {
289
+      throw new Error('The manipulator: ' + component.manipulator +
290
+                      ' does not exist in the built-in manipulators.');
291
+    }
292
+  }
293
+
294
+  if (!_component.manipulator) {
295
+    throw new Error('The manipulator must be defined');
296
+  }
297
+
298
+  if (typeof _component.manipulator.set !== 'function' ||
299
+      typeof _component.manipulator.get !== 'function') {
300
+    throw new Error('The manipulator must have both a `get` and `set` method');
301
+  }
302
+
303
+  if (_component.style) {
304
+    var style = document.createElement('style');
305
+    style.type = 'text/css';
306
+    style.appendChild(document.createTextNode(_component.style));
307
+    document.head.appendChild(style);
308
+  }
309
+
310
+  componentStore[_component.name] = _component;
311
+  return true;
312
+};
313
+
314
+module.exports = ClayConfig;
Browse code

init commit

louis.jonget authored on30/09/2022 18:58:18
Showing1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,314 @@
1
+'use strict';
2
+
3
+/**
4
+ * A Clay config Item
5
+ * @typedef {Object} Clay~ConfigItem
6
+ * @property {string} type
7
+ * @property {string|boolean|number} defaultValue
8
+ * @property {string} [messageKey]
9
+ * @property {string} [id]
10
+ * @property {string} [label]
11
+ * @property {Object} [attributes]
12
+ * @property {Array} [options]
13
+ * @property {Array} [items]
14
+ * @property {Array} [capabilities]
15
+ */
16
+
17
+var HTML = require('../vendor/minified').HTML;
18
+var _ = require('../vendor/minified')._;
19
+var ClayItem = require('./clay-item');
20
+var utils = require('../lib/utils');
21
+var ClayEvents = require('./clay-events');
22
+var componentStore = require('./component-registry');
23
+var manipulators = require('./manipulators');
24
+
25
+/**
26
+ * @extends ClayEvents
27
+ * @param {Object} settings - setting that were set from a previous session
28
+ * @param {Array|Object} config
29
+ * @param {M} $rootContainer
30
+ * @param {Object} meta
31
+ * @constructor
32
+ */
33
+function ClayConfig(settings, config, $rootContainer, meta) {
34
+  var self = this;
35
+
36
+  var _settings = _.copyObj(settings);
37
+  var _items;
38
+  var _itemsById;
39
+  var _itemsByMessageKey;
40
+  var _isBuilt;
41
+
42
+  /**
43
+   * Initialize the item arrays and objects
44
+   * @private
45
+   * @return {void}
46
+   */
47
+  function _initializeItems() {
48
+    _items = [];
49
+    _itemsById = {};
50
+    _itemsByMessageKey = {};
51
+    _isBuilt = false;
52
+  }
53
+
54
+  /**
55
+   * Add item(s) to the config
56
+   * @param {Clay~ConfigItem|Array} item
57
+   * @param {M} $container
58
+   * @return {void}
59
+   */
60
+  function _addItems(item, $container) {
61
+    if (Array.isArray(item)) {
62
+      item.forEach(function(item) {
63
+        _addItems(item, $container);
64
+      });
65
+    } else if (utils.includesCapability(meta.activeWatchInfo, item.capabilities)) {
66
+      if (item.type === 'section') {
67
+        var $wrapper = HTML('<div class="section">');
68
+        $container.add($wrapper);
69
+        _addItems(item.items, $wrapper);
70
+      } else {
71
+        var _item = _.copyObj(item);
72
+        _item.clayId = _items.length;
73
+
74
+        var clayItem = new ClayItem(_item).initialize(self);
75
+
76
+        if (_item.id) {
77
+          _itemsById[_item.id] = clayItem;
78
+        }
79
+
80
+        if (_item.messageKey) {
81
+          _itemsByMessageKey[_item.messageKey] = clayItem;
82
+        }
83
+
84
+        _items.push(clayItem);
85
+
86
+        // set the value of the item via the manipulator to ensure consistency
87
+        var value = typeof _settings[_item.messageKey] !== 'undefined' ?
88
+          _settings[_item.messageKey] :
89
+          _item.defaultValue;
90
+
91
+        clayItem.set(typeof value !== 'undefined' ? value : '');
92
+
93
+        $container.add(clayItem.$element);
94
+      }
95
+    }
96
+  }
97
+
98
+  /**
99
+   * Throws if the config has not been built yet.
100
+   * @param {string} fnName
101
+   * @returns {boolean}
102
+   * @private
103
+   */
104
+  function _checkBuilt(fnName) {
105
+    if (!_isBuilt) {
106
+      throw new Error(
107
+        'ClayConfig not built. build() must be run before ' +
108
+        'you can run ' + fnName + '()'
109
+      );
110
+    }
111
+    return true;
112
+  }
113
+
114
+  self.meta = meta;
115
+  self.$rootContainer = $rootContainer;
116
+
117
+  self.EVENTS = {
118
+    /**
119
+     * Called before framework has initialized. This is when you would attach your
120
+     * custom components.
121
+     * @const
122
+     */
123
+    BEFORE_BUILD: 'BEFORE_BUILD',
124
+
125
+    /**
126
+     * Called after the config has been parsed and all components have their initial
127
+     * value set
128
+     * @const
129
+     */
130
+    AFTER_BUILD: 'AFTER_BUILD',
131
+
132
+    /**
133
+     * Called if .build() is executed after the page has already been built and
134
+     * before the existing content is destroyed
135
+     * @const
136
+     */
137
+    BEFORE_DESTROY: 'BEFORE_DESTROY',
138
+
139
+    /**
140
+     * Called if .build() is executed after the page has already been built and after
141
+     * the existing content is destroyed
142
+     * @const
143
+     */
144
+    AFTER_DESTROY: 'AFTER_DESTROY'
145
+  };
146
+  utils.updateProperties(self.EVENTS, {writable: false});
147
+
148
+  /**
149
+   * @returns {Array.<ClayItem>}
150
+   */
151
+  self.getAllItems = function() {
152
+    _checkBuilt('getAllItems');
153
+    return _items;
154
+  };
155
+
156
+  /**
157
+   * @param {string} messageKey
158
+   * @returns {ClayItem}
159
+   */
160
+  self.getItemByMessageKey = function(messageKey) {
161
+    _checkBuilt('getItemByMessageKey');
162
+    return _itemsByMessageKey[messageKey];
163
+  };
164
+
165
+  /**
166
+   * @param {string} id
167
+   * @returns {ClayItem}
168
+   */
169
+  self.getItemById = function(id) {
170
+    _checkBuilt('getItemById');
171
+    return _itemsById[id];
172
+  };
173
+
174
+  /**
175
+   * @param {string} type
176
+   * @returns {Array.<ClayItem>}
177
+   */
178
+  self.getItemsByType = function(type) {
179
+    _checkBuilt('getItemsByType');
180
+    return _items.filter(function(item) {
181
+      return item.config.type === type;
182
+    });
183
+  };
184
+
185
+  /**
186
+   * @param {string} group
187
+   * @returns {Array.<ClayItem>}
188
+   */
189
+  self.getItemsByGroup = function(group) {
190
+    _checkBuilt('getItemsByGroup');
191
+    return _items.filter(function(item) {
192
+      return item.config.group === group;
193
+    });
194
+  };
195
+
196
+  /**
197
+   * @returns {Object}
198
+   */
199
+  self.serialize = function() {
200
+    _checkBuilt('serialize');
201
+
202
+    _settings = {};
203
+
204
+    _.eachObj(_itemsByMessageKey, function(messageKey, item) {
205
+      _settings[messageKey] = {
206
+        value: item.get()
207
+      };
208
+
209
+      if (item.precision) {
210
+        _settings[messageKey].precision = item.precision;
211
+      }
212
+    });
213
+    return _settings;
214
+  };
215
+
216
+  // @todo maybe don't do this and force the static method
217
+  self.registerComponent = ClayConfig.registerComponent;
218
+
219
+  /**
220
+   * Empties the root container
221
+   * @returns {ClayConfig}
222
+   */
223
+  self.destroy = function() {
224
+    var el = $rootContainer[0];
225
+    self.trigger(self.EVENTS.BEFORE_DESTROY);
226
+    while (el.firstChild) {
227
+      el.removeChild(el.firstChild);
228
+    }
229
+    _initializeItems();
230
+    self.trigger(self.EVENTS.AFTER_DESTROY);
231
+    return self;
232
+  };
233
+
234
+  /**
235
+   * Build the config page. This must be run before any of the get methods can be run
236
+   * If you call this method after the page has already been built, teh page will be
237
+   * destroyed and built again.
238
+   * @returns {ClayConfig}
239
+   */
240
+  self.build = function() {
241
+    if (_isBuilt) {
242
+      self.destroy();
243
+    }
244
+    self.trigger(self.EVENTS.BEFORE_BUILD);
245
+    _addItems(self.config, $rootContainer);
246
+    _isBuilt = true;
247
+    self.trigger(self.EVENTS.AFTER_BUILD);
248
+    return self;
249
+  };
250
+
251
+  _initializeItems();
252
+
253
+  // attach event methods
254
+  ClayEvents.call(self, $rootContainer);
255
+
256
+  // prevent external modifications of properties
257
+  utils.updateProperties(self, { writable: false, configurable: false });
258
+
259
+  // expose the config to allow developers to update it before the build is run
260
+  self.config = config;
261
+}
262
+
263
+/**
264
+ * Register a component to Clay. This must be called prior to .build();
265
+ * @param {Object} component - the clay component to register
266
+ * @param {string} component.name - the name of the component
267
+ * @param {string} component.template - HTML template to use for the component
268
+ * @param {string|Object} component.manipulator - methods to attach to the component
269
+ * @param {function} component.manipulator.set - set manipulator method
270
+ * @param {function} component.manipulator.get - get manipulator method
271
+ * @param {Object} [component.defaults] - template defaults
272
+ * @param {function} [component.initialize] - method to scaffold the component
273
+ * @return {boolean} - Returns true if component was registered correctly
274
+ */
275
+ClayConfig.registerComponent = function(component) {
276
+  var _component = _.copyObj(component);
277
+
278
+  if (componentStore[_component.name]) {
279
+    console.warn('Component: ' + _component.name +
280
+                 ' is already registered. If you wish to override the existing' +
281
+                 ' functionality, you must provide a new name');
282
+    return false;
283
+  }
284
+
285
+  if (typeof _component.manipulator === 'string') {
286
+    _component.manipulator = manipulators[component.manipulator];
287
+
288
+    if (!_component.manipulator) {
289
+      throw new Error('The manipulator: ' + component.manipulator +
290
+                      ' does not exist in the built-in manipulators.');
291
+    }
292
+  }
293
+
294
+  if (!_component.manipulator) {
295
+    throw new Error('The manipulator must be defined');
296
+  }
297
+
298
+  if (typeof _component.manipulator.set !== 'function' ||
299
+      typeof _component.manipulator.get !== 'function') {
300
+    throw new Error('The manipulator must have both a `get` and `set` method');
301
+  }
302
+
303
+  if (_component.style) {
304
+    var style = document.createElement('style');
305
+    style.type = 'text/css';
306
+    style.appendChild(document.createTextNode(_component.style));
307
+    document.head.appendChild(style);
308
+  }
309
+
310
+  componentStore[_component.name] = _component;
311
+  return true;
312
+};
313
+
314
+module.exports = ClayConfig;