Merge lp:~rharding/launchpad/garden_client into lp:launchpad

Proposed by Richard Harding
Status: Merged
Approved by: Richard Harding
Approved revision: no longer in the source branch.
Merged at revision: 15680
Proposed branch: lp:~rharding/launchpad/garden_client
Merge into: lp:launchpad
Prerequisite: lp:~rharding/launchpad/tab_client
Diff against target: 807 lines (+239/-272)
1 file modified
lib/lp/app/javascript/client.js (+239/-272)
To merge this branch: bzr merge lp:~rharding/launchpad/garden_client
Reviewer Review Type Date Requested Status
Benji York (community) code Approve
Review via email: mp+116330@code.launchpad.net

Commit message

Garden client.js code.

Description of the change

= Summary =

In the process of getting some LoC points towards the bug fix in another
branch this performs some cleanup of the code in the client.js.

== Implementation Notes ==

Move all objects/methods not attached to the module (thus private) to the
top of the file.

Fix 2-space lines with 4-space indentation.

Change the ErrorHandler, FormErrorHandler, PatchPlugin to be created via
Y.Base.create().

Assign objects straight to module vs creating them as vars and then later
perform a:
module.SomeObject = SomeObject

Fixed a comment block to match.

Removing some double spaces lines to try to keep things consistant as there
are various 2-line/1-line spacing between objects and methods.

Make sure variables are declared (querystring) so that they don't leak to the
global window scope.

And some line length cleanup due to the increased tab depth from the prev
branch.

== Tests ==
                                                                                                                                                        xvfb-run ./bin/test -x -cvv --layer=YUITestLayer

== Lint ==

Linting changed files:
  lib/lp/app/javascript/client.js

== LoC Qualification ==

Negative LoC

To post a comment you must log in.
Revision history for this message
Benji York (benji) wrote :

Looks good.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/app/javascript/client.js'
2--- lib/lp/app/javascript/client.js 2012-07-23 18:09:20 +0000
3+++ lib/lp/app/javascript/client.js 2012-07-23 18:09:20 +0000
4@@ -7,6 +7,75 @@
5 * @module Y.lp.client
6 */
7 YUI.add('lp.client', function(Y) {
8+ // Private code used only in the module.
9+ var NOTIFICATION_INFO = {
10+ 'level10': {
11+ 'selector': '.debug.message',
12+ 'css_class': 'debug message'
13+ },
14+ 'level20': {
15+ 'selector': '.informational.message',
16+ 'css_class': 'informational message'
17+ },
18+ 'level30': {
19+ 'selector': '.warning.message',
20+ 'css_class': 'warning message'
21+ },
22+ 'level40': {
23+ 'selector': '.error.message',
24+ 'css_class': 'error message'
25+ }
26+ };
27+
28+ var update_cached_object = function (cache_name, cache, entry) {
29+ var fields_changed = [];
30+ var name;
31+ var html_name;
32+ for (name in cache) {
33+ if (cache.hasOwnProperty(name)) {
34+ var old_value = cache[name];
35+ var new_value = entry.get(name);
36+ if (name !== 'lp_html') {
37+ if (old_value !== new_value) {
38+ fields_changed.push(name);
39+ cache[name] = new_value;
40+ var field_updated_event_name =
41+ 'lp:' + cache_name + ':' + name + ':changed';
42+ var new_value_html = entry.getHTML(name);
43+ var event = {
44+ 'name': name,
45+ 'old_value': old_value,
46+ 'new_value': new_value,
47+ 'new_value_html': new_value_html,
48+ 'entry': entry
49+ };
50+ Y.fire(field_updated_event_name, event);
51+ }
52+ }
53+ else {
54+ // Since we don't care here about the content, we aren't
55+ // using the values here to determine if the field has
56+ // changed, so we can just update the cache.
57+ for (html_name in old_value) {
58+ if (old_value.hasOwnProperty(html_name)) {
59+ old_value[html_name] = new_value[html_name];
60+ }
61+ }
62+ }
63+ }
64+ }
65+
66+ if (fields_changed.length > 0) {
67+ var event_name = 'lp:' + cache_name + ':changed';
68+ var event_ = {
69+ 'fields_changed': fields_changed,
70+ 'entry': entry
71+ };
72+ Y.fire(event_name, event_);
73+ }
74+ };
75+
76+
77
78 var module = Y.namespace('lp.client');
79
80@@ -120,11 +189,11 @@
81 };
82
83 module.normalize_uri = function(uri) {
84- /* Converts an absolute URI into a relative URI.
85-
86- Appends the root to a relative URI that lacks the root.
87-
88- Does nothing to a relative URI that includes the root.*/
89+ /**
90+ * Converts an absolute URI into a relative URI.
91+ * Appends the root to a relative URI that lacks the root.
92+ * Does nothing to a relative URI that includes the root.
93+ */
94 var host_start = uri.indexOf('//');
95 if (host_start !== -1) {
96 var host_end = uri.indexOf('/', host_start+2);
97@@ -183,7 +252,6 @@
98 }
99 };
100
101-
102 /**
103 * Get the URL of the view for an Entry
104 * @method get_view_url
105@@ -195,15 +263,15 @@
106 */
107 module.get_view_url = function(entry, view_name, namespace, query){
108 entry_url = Y.lp.get_url_path(entry.get('web_link'));
109- querystring = Y.QueryString.stringify(query);
110+ var querystring = Y.QueryString.stringify(query);
111 if (querystring !== '') {
112 querystring = '?' + querystring;
113 }
114 return (
115- entry_url + '/' + view_name + '/++' + namespace + '++' + querystring);
116+ entry_url + '/' + view_name + '/++' +
117+ namespace + '++' + querystring);
118 };
119
120-
121 /**
122 * Get the URL of the form for a view for an Entry
123 * @method get_form_url
124@@ -215,7 +283,6 @@
125 return module.get_view_url(entry, view_name, 'form');
126 };
127
128-
129 /**
130 * Load the model for a view.
131 *
132@@ -237,7 +304,6 @@
133 io_provider.io(url, y_config);
134 };
135
136-
137 module.add_accept = function(config, headers) {
138 if (headers === undefined) {
139 headers = {};
140@@ -258,77 +324,27 @@
141 return data;
142 };
143
144- var update_cached_object = function (cache_name, cache, entry)
145- {
146- var fields_changed = [];
147- var name;
148- var html_name;
149- for (name in cache) {
150- if (cache.hasOwnProperty(name)) {
151- var old_value = cache[name];
152- var new_value = entry.get(name);
153- if (name !== 'lp_html') {
154- if (old_value !== new_value) {
155- fields_changed.push(name);
156- cache[name] = new_value;
157- var field_updated_event_name =
158- 'lp:' + cache_name + ':' + name + ':changed';
159- var new_value_html = entry.getHTML(name);
160- var event = {
161- 'name': name,
162- 'old_value': old_value,
163- 'new_value': new_value,
164- 'new_value_html': new_value_html,
165- 'entry': entry
166- };
167- Y.fire(field_updated_event_name, event);
168- }
169- }
170- else {
171- // Since we don't care here about the content, we aren't using the
172- // values here to determine if the field has changed, so we can just
173- // update the cache.
174- for (html_name in old_value) {
175- if (old_value.hasOwnProperty(html_name)) {
176- old_value[html_name] = new_value[html_name];
177- }
178- }
179- }
180- }
181- }
182-
183- if (fields_changed.length > 0) {
184- var event_name = 'lp:' + cache_name + ':changed';
185- var event_ = {
186- 'fields_changed': fields_changed,
187- 'entry': entry
188- };
189- Y.fire(event_name, event_);
190- }
191- };
192-
193-
194 module.update_cache = function(entry) {
195- if (!entry) {
196- return;
197- }
198- var original_uri = entry.lp_original_uri;
199- var full_uri = module.get_absolute_uri(original_uri);
200- var name;
201- var cached_object;
202- for (name in LP.cache) {
203- if (LP.cache.hasOwnProperty(name)) {
204- cached_object = LP.cache[name];
205- /*jslint continue:true*/
206- if (!Y.Lang.isValue(cached_object)) {
207- continue;
208- }
209- if (cached_object.self_link === full_uri) {
210- Y.log(name + ' cached object has been updated.');
211- update_cached_object(name, cached_object, entry);
212- }
213- }
214- }
215+ if (!entry) {
216+ return;
217+ }
218+ var original_uri = entry.lp_original_uri;
219+ var full_uri = module.get_absolute_uri(original_uri);
220+ var name;
221+ var cached_object;
222+ for (name in LP.cache) {
223+ if (LP.cache.hasOwnProperty(name)) {
224+ cached_object = LP.cache[name];
225+ /*jslint continue:true*/
226+ if (!Y.Lang.isValue(cached_object)) {
227+ continue;
228+ }
229+ if (cached_object.self_link === full_uri) {
230+ Y.log(name + ' cached object has been updated.');
231+ update_cached_object(name, cached_object, entry);
232+ }
233+ }
234+ }
235 };
236
237 module.wrap_resource_on_success = function(ignore, response, args) {
238@@ -364,25 +380,6 @@
239 }
240 };
241
242- var NOTIFICATION_INFO = {
243- 'level10': {
244- 'selector': '.debug.message',
245- 'css_class': 'debug message'
246- },
247- 'level20': {
248- 'selector': '.informational.message',
249- 'css_class': 'informational message'
250- },
251- 'level30': {
252- 'selector': '.warning.message',
253- 'css_class': 'warning message'
254- },
255- 'level40': {
256- 'selector': '.error.message',
257- 'css_class': 'error message'
258- }
259- };
260-
261 /**
262 * Display a list of notifications - error, warning, informational or debug.
263 * @param notifications An json encoded array of (level, message) tuples.
264@@ -455,8 +452,7 @@
265 // The resources that come together to make Launchpad.
266
267 // A hosted file resource.
268-
269- var HostedFile = function(client, uri, content_type, contents) {
270+ module.HostedFile = function(client, uri, content_type, contents) {
271 /* A binary file manipulable through the web service. */
272 this.lp_client = client;
273 this.uri = uri;
274@@ -465,8 +461,7 @@
275 this.io_provider = client.io_provider;
276 };
277
278- HostedFile.prototype = {
279-
280+ module.HostedFile.prototype = {
281 'lp_save' : function(config) {
282 /* Write a new version of this file back to the web service. */
283 var on = config.on;
284@@ -482,7 +477,8 @@
285 'data': hosted_file.contents,
286 'sync': this.lp_client.sync
287 };
288- this.io_provider.io(module.normalize_uri(hosted_file.uri), y_config);
289+ this.io_provider.io(module.normalize_uri(hosted_file.uri),
290+ y_config);
291 },
292
293 'lp_delete' : function(config) {
294@@ -498,12 +494,10 @@
295 }
296 };
297
298- module.HostedFile = HostedFile;
299-
300- var Resource = function() {
301+ module.Resource = function() {
302 /* The base class for objects retrieved from Launchpad's web service. */
303 };
304- Resource.prototype = {
305+ module.Resource.prototype = {
306 'init': function(client, representation, uri) {
307 /* Initialize a resource with its representation and URI. */
308 this.lp_client = client;
309@@ -533,9 +527,9 @@
310 }
311
312 // If the response is 404, it means we have a hosted file that
313- // doesn't exist yet. If the response is 303 and goes off to another
314- // site, that means we have a hosted file that does exist. Either way
315- // we should turn the failure into a success.
316+ // doesn't exist yet. If the response is 303 and goes off to
317+ // another site, that means we have a hosted file that does exist.
318+ // Either way we should turn the failure into a success.
319 var on_success = on.success;
320 var old_on_failure = on.failure;
321 on.failure = function(ignore, response, args) {
322@@ -543,7 +537,7 @@
323 var original_url = args[1];
324 if (response.status === module.HTTP_NOT_FOUND ||
325 response.status === module.HTTP_SEE_ALSO) {
326- var file = new HostedFile(client, original_url);
327+ var file = new module.HostedFile(client, original_url);
328 return on_success(file);
329 } else if (old_on_failure !== undefined) {
330 return old_on_failure(ignore, response, args);
331@@ -554,43 +548,41 @@
332
333 'named_get': function(operation_name, config) {
334 /* Get the result of a named GET operation on this resource. */
335- return this.lp_client.named_get(this.lp_original_uri, operation_name,
336+ return this.lp_client.named_get(this.lp_original_uri,
337+ operation_name,
338 config);
339 },
340
341 'named_post': function(operation_name, config) {
342 /* Trigger a named POST operation on this resource. */
343- return this.lp_client.named_post(this.lp_original_uri, operation_name,
344+ return this.lp_client.named_post(this.lp_original_uri,
345+ operation_name,
346 config);
347 }
348 };
349
350- module.Resource = Resource;
351-
352-
353 // The service root resource.
354- Root = function(client, representation, uri) {
355+ module.Root = function(client, representation, uri) {
356 /* The root of the Launchpad web service. */
357 this.init(client, representation, uri);
358 };
359- Root.prototype = new Resource();
360-
361- module.Root = Root;
362-
363-
364- var Collection = function(client, representation, uri) {
365+ module.Root.prototype = new module.Resource();
366+
367+ module.Collection = function(client, representation, uri) {
368 /* A grouped collection of objets from the Launchpad web service. */
369 var index, entry;
370 this.init(client, representation, uri);
371 for (index = 0 ; index < this.entries.length ; index++) {
372 entry = this.entries[index];
373- this.entries[index] = new Entry(client, entry, entry.self_link);
374+ this.entries[index] = new module.Entry(client,
375+ entry,
376+ entry.self_link);
377 }
378 };
379
380- Collection.prototype = new Resource();
381+ module.Collection.prototype = new module.Resource();
382
383- Collection.prototype.lp_slice = function(on, start, size) {
384+ module.Collection.prototype.lp_slice = function(on, start, size) {
385 /* Retrieve a subset of the collection.
386
387 :param start: Where in the collection to start serving entries.
388@@ -600,9 +592,7 @@
389 {on: on, start: start, size: size});
390 };
391
392- module.Collection = Collection;
393-
394- var Entry = function(client, representation, uri) {
395+ module.Entry = function(client, representation, uri) {
396 /* A single object from the Launchpad web service. */
397 this.lp_client = client;
398 this.lp_original_uri = uri;
399@@ -620,19 +610,19 @@
400 }
401 };
402
403- Entry.prototype = new Resource();
404+ module.Entry.prototype = new module.Resource();
405
406 // Augment with Attribute so that we can listen for attribute change events.
407- Y.augment(Entry, Y.Attribute);
408+ Y.augment(module.Entry, Y.Attribute);
409
410- Entry.prototype.mark_as_dirty = function(event) {
411+ module.Entry.prototype.mark_as_dirty = function(event) {
412 /* Respond to an event triggered by modification to an Entry's field. */
413 if (event.newVal !== event.prevVal) {
414 this.dirty_attributes.push(event.attrName);
415 }
416 };
417
418- Entry.prototype.lp_save = function(config) {
419+ module.Entry.prototype.lp_save = function(config) {
420 /* Write modifications to this entry back to the web service. */
421 var representation = {};
422 var entry = this;
423@@ -648,12 +638,12 @@
424 this.dirty_attributes = [];
425 };
426
427- Entry.prototype.lookup_value = function(key) {
428+ module.Entry.prototype.lookup_value = function(key) {
429 /* A common getter interface between Entrys and non-Entrys. */
430 return this.get(key);
431 };
432
433- Entry.prototype.getHTML = function(key) {
434+ module.Entry.prototype.getHTML = function(key) {
435 var lp_html = this.get('lp_html');
436 if (lp_html) {
437 // First look for the key.
438@@ -671,17 +661,14 @@
439 return null;
440 };
441
442- module.Entry = Entry;
443-
444 // The Launchpad client itself.
445-
446- var Launchpad = function(config) {
447+ module.Launchpad = function(config) {
448 /* A client that makes HTTP requests to Launchpad's web service. */
449 this.io_provider = module.get_configured_io_provider(config);
450 this.sync = (config ? config.sync : false);
451 };
452
453- Launchpad.prototype = {
454+ module.Launchpad.prototype = {
455 'get': function (uri, config) {
456 /* Get the current state of a resource. */
457 var on = Y.merge(config.on);
458@@ -750,7 +737,9 @@
459 { on: { success: old_on_success,
460 failure: on.failure } });
461 }
462- return module.wrap_resource_on_success(undefined, response, args);
463+ return module.wrap_resource_on_success(undefined,
464+ response,
465+ args);
466 };
467 var client = this;
468 var update_cache = false;
469@@ -814,7 +803,7 @@
470 || representation.total_size_link !== undefined) {
471 // It's a list. Treat it as a collection;
472 // it should be slicable.
473- return new Collection(this, representation, uri);
474+ return new module.Collection(this, representation, uri);
475 } else if (Y.Lang.isObject(representation)) {
476 // It's an Array or mapping. Recurse into it.
477 if (Y.Lang.isArray(representation)) {
478@@ -840,18 +829,15 @@
479 }
480 } else if (representation.resource_type_link.search(
481 /\/#service-root$/) !== -1) {
482- return new Root(this, representation, uri);
483+ return new module.Root(this, representation, uri);
484 } else if (representation.total_size === undefined) {
485- return new Entry(this, representation, uri);
486+ return new module.Entry(this, representation, uri);
487 } else {
488- return new Collection(this, representation, uri);
489+ return new module.Collection(this, representation, uri);
490 }
491 }
492 };
493
494- module.Launchpad = Launchpad;
495-
496-
497 /**
498 * Helper object for handling XHR failures.
499 * clearProgressUI() and showError() need to be defined by the callsite
500@@ -859,12 +845,7 @@
501 *
502 * @class ErrorHandler
503 */
504- var ErrorHandler;
505- ErrorHandler = function(config) {
506- ErrorHandler.superclass.constructor.apply(this, arguments);
507- };
508-
509- Y.extend(ErrorHandler, Y.Base, {
510+ module.ErrorHandler = Y.Base.create('client-error-handler', Y.Base, [], {
511 /**
512 * Clear the progress indicator.
513 *
514@@ -898,8 +879,8 @@
515 * @method handleError
516 * @param ioId The request id.
517 * @param response The XHR call response object.
518- * @return {Boolean} Return true if the error has been fully processed and
519- * any further generic error handling is not required.
520+ * @return {Boolean} Return true if the error has been fully processed
521+ * and any further generic error handling is not required.
522 */
523 handleError: function(ioId, response) {
524 return false;
525@@ -917,8 +898,8 @@
526 var self = this;
527 return function(ioId, o) {
528 self.clearProgressUI();
529- // Perform any user specified error handling. If true is returned,
530- // we do not do any further processing.
531+ // Perform any user specified error handling. If true is
532+ // returned, we do not do any further processing.
533 if( self.handleError(ioId, o) ) {
534 return;
535 }
536@@ -951,21 +932,8 @@
537 }
538 });
539
540- module.ErrorHandler = ErrorHandler;
541-
542- var FormErrorHandler;
543- FormErrorHandler = function(config) {
544- FormErrorHandler.superclass.constructor.apply(this, arguments);
545- };
546-
547- FormErrorHandler.ATTRS = {
548- form: {
549- value: null
550- }
551- };
552-
553- Y.extend(FormErrorHandler, ErrorHandler, {
554-
555+ module.FormErrorHandler = Y.Base.create('client-form-error-handler',
556+ module.ErrorHandler, [], {
557 // Clear any errors on the form.
558 clearFormErrors: function() {
559 Y.all('.error.message').remove(true);
560@@ -973,8 +941,8 @@
561 Y.all('div.error').removeClass('error');
562 },
563
564- // If the XHR call returns a form validation error, we display the errors
565- // on the form.
566+ // If the XHR call returns a form validation error, we display the
567+ // errors on the form.
568 handleError: function(ioId, response) {
569 if (response.status === 400
570 && response.statusText === 'Validation') {
571@@ -1051,15 +1019,19 @@
572 return response.status + ' ' + response.statusText;
573 }
574 }
575- });
576-
577- module.FormErrorHandler = FormErrorHandler;
578-
579-
580-}, "0.1",
581- {"requires":["attribute", "io", "querystring", "json-parse",
582- "json-stringify", "lp"]});
583-
584+ }, {
585+ ATTRS: {
586+ form: {
587+ value: null
588+ }
589+ }
590+ });
591+
592+
593+}, "0.1", {
594+ requires: ["attribute", "base", "io", "querystring", "json-parse",
595+ "json-stringify", "lp"]
596+});
597
598 YUI.add('lp.client.plugins', function (Y) {
599
600@@ -1078,81 +1050,8 @@
601 * @class PATCHPlugin
602 * @extends Widget
603 */
604- var PATCHPlugin = function PATCHPlugin () {
605- PATCHPlugin.superclass.constructor.apply(this, arguments);
606- };
607-
608- Y.mix(PATCHPlugin, {
609- /**
610- * The identity of the plugin.
611- *
612- * @property PATCHPlugin.NAME
613- * @type String
614- * @static
615- */
616- NAME: 'PATCHPlugin',
617-
618- /**
619- * The namespace of the plugin.
620- *
621- * @property PATCHPlugin.NS
622- * @type String
623- * @static
624- */
625- NS: 'patcher',
626-
627- /**
628- * Static property used to define the default attribute configuration of
629- * this plugin.
630- *
631- * @property PATCHPlugin.ATTRS
632- * @type Object
633- * @static
634- */
635- ATTRS : {
636- /**
637- * Name of the attribute to patch.
638- *
639- * @attribute patch
640- * @type String
641- */
642- patch: {},
643-
644- /**
645- * URL of the resource to PATCH.
646- *
647- * @attribute resource
648- * @type String
649- */
650- resource: {},
651-
652- /**
653- * Should the resulting field get the value from the lp_html
654- * attribute?
655- *
656- * @attribute use_html
657- * @type Boolean
658- */
659- use_html: false,
660-
661- /**
662- * The function to use to format the returned result into a form that
663- * can be inserted into the page DOM.
664- *
665- * The default value is a function that simply returns the result
666- * unmodified.
667- *
668- * @attribute formatter
669- * @type Function
670- * @default null
671- */
672- formatter: {
673- valueFn: function() { return this._defaultFormatter; }
674- }
675- }});
676-
677- Y.extend(PATCHPlugin, Y.Plugin.Base, {
678-
679+ module.PATCHPlugin = Y.Base.create('client-plugin-patch',
680+ Y.Plugin.Base, [], {
681 /**
682 * Configuration parameters that will be passed through to the lp.client
683 * call.
684@@ -1171,15 +1070,17 @@
685 */
686 initializer: function(config) {
687 if (!Y.Lang.isString(config.patch)) {
688- Y.error("missing config: 'patch' containing the attribute name");
689+ Y.error(
690+ "missing config: 'patch' containing the attribute name");
691 }
692
693 if (!Y.Lang.isString(config.resource)) {
694- Y.error("missing config: 'resource' containing the URL to patch");
695+ Y.error(
696+ "missing config: 'resource' containing the URL to patch");
697 }
698
699- // Save the config object that the user passed in so that we can pass
700- // any extra parameters through to the lp.client constructor.
701+ // Save the config object that the user passed in so that we can
702+ // pass any extra parameters through to the lp.client constructor.
703 this.extra_config = config || {};
704 this.extra_config.accept = 'application/json;include=lp_html';
705
706@@ -1250,8 +1151,8 @@
707 },
708
709 /**
710- * Return the webservice Entry object attribute that is to be shown in the
711- * page DOM.
712+ * Return the webservice Entry object attribute that is to be shown in
713+ * the page DOM.
714 *
715 * This function may be overridden in various ways.
716 *
717@@ -1259,8 +1160,8 @@
718 * @protected
719 * @param result {Entry|String} A Launchpad webservice Entry object, or
720 * the unmodified result string if the default Content-Type wasn't used.
721- * @param attribute {String} The resource attribute that the PATCH request
722- * was sent to.
723+ * @param attribute {String} The resource attribute that the PATCH
724+ * request was sent to.
725 * @return {String|Node} A string or Node instance to be inserted into
726 * the DOM.
727 */
728@@ -1275,10 +1176,76 @@
729 }
730 }
731 }
732+ }, {
733+ /**
734+ * The identity of the plugin.
735+ *
736+ * @property PATCHPlugin.NAME
737+ * @type String
738+ * @static
739+ */
740+ NAME: 'PATCHPlugin',
741+
742+ /**
743+ * The namespace of the plugin.
744+ *
745+ * @property PATCHPlugin.NS
746+ * @type String
747+ * @static
748+ */
749+ NS: 'patcher',
750+
751+ /**
752+ * Static property used to define the default attribute configuration of
753+ * this plugin.
754+ *
755+ * @property PATCHPlugin.ATTRS
756+ * @type Object
757+ * @static
758+ */
759+ ATTRS : {
760+ /**
761+ * Name of the attribute to patch.
762+ *
763+ * @attribute patch
764+ * @type String
765+ */
766+ patch: {},
767+
768+ /**
769+ * URL of the resource to PATCH.
770+ *
771+ * @attribute resource
772+ * @type String
773+ */
774+ resource: {},
775+
776+ /**
777+ * Should the resulting field get the value from the lp_html
778+ * attribute?
779+ *
780+ * @attribute use_html
781+ * @type Boolean
782+ */
783+ use_html: false,
784+
785+ /**
786+ * The function to use to format the returned result into a form
787+ * that can be inserted into the page DOM.
788+ *
789+ * The default value is a function that simply returns the result
790+ * unmodified.
791+ *
792+ * @attribute formatter
793+ * @type Function
794+ * @default null
795+ */
796+ formatter: {
797+ valueFn: function() { return this._defaultFormatter; }
798+ }
799+ }
800 });
801
802- module.PATCHPlugin = PATCHPlugin;
803-
804 }, "0.1", {
805- requires: ["plugin", "dump", "lazr.editor", "lp.client"]
806+ requires: ["base", "plugin", "dump", "lazr.editor", "lp.client"]
807 });