source: Dev/trunk/src/client/dojox/atom/widget/FeedEntryEditor.js

Last change on this file was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 40.3 KB
Line 
1define([
2        "dojo/_base/kernel",
3        "dojo/_base/lang",
4        "dojo/_base/connect",
5        "dojo/_base/declare",
6        "dojo/_base/fx",
7        "dojo/_base/sniff",
8        "dojo/dom",
9        "dojo/dom-style",
10        "dojo/dom-construct",
11        "dijit/_Widget",
12        "dijit/_Templated",
13        "dijit/_Container",
14        "dijit/Editor",
15        "dijit/form/TextBox",
16        "dijit/form/SimpleTextarea",
17        "./FeedEntryViewer",
18        "../io/model",
19        "dojo/text!./templates/FeedEntryEditor.html",
20        "dojo/text!./templates/PeopleEditor.html",
21        "dojo/i18n!./nls/FeedEntryViewer",
22        "dojo/i18n!./nls/FeedEntryEditor",
23        "dojo/i18n!./nls/PeopleEditor"
24], function (dojo, lang, connect, declare, fx, has, domUtil, domStyle, domConstruct,
25                         _Widget, _Templated, _Container, Editor, TextBox, SimpleTextarea,
26                         FeedEntryViewer, model, template, peopleEditorTemplate, i18nViewer, i18nEditor, i18nPeople) {
27dojo.experimental("dojox.atom.widget.FeedEntryEditor");
28
29
30var FeedEntryEditor = declare("dojox.atom.widget.FeedEntryEditor", FeedEntryViewer,{
31        // summary:
32        //              An ATOM feed entry editor that allows viewing of the individual attributes of an entry.
33
34        _contentEditor: null,
35        _oldContent: null,
36        _setObject: null,
37        enableEdit: false,
38        _contentEditorCreator: null,
39        _editors: {},
40        entryNewButton: null,
41        _editable: false,               //Flag denoting if the current entry is editable or not.
42
43        //Templates for the HTML rendering.  Need to figure these out better, admittedly.
44        templateString: template,
45
46        postCreate: function(){
47                if(this.entrySelectionTopic !== ""){
48                        this._subscriptions = [dojo.subscribe(this.entrySelectionTopic, this, "_handleEvent")];
49                }
50                var _nlsResources = i18nViewer;
51                this.displayOptions.innerHTML = _nlsResources.displayOptions;
52                this.feedEntryCheckBoxLabelTitle.innerHTML = _nlsResources.title;
53                this.feedEntryCheckBoxLabelAuthors.innerHTML = _nlsResources.authors;
54                this.feedEntryCheckBoxLabelContributors.innerHTML = _nlsResources.contributors;
55                this.feedEntryCheckBoxLabelId.innerHTML = _nlsResources.id;
56                this.close.innerHTML = _nlsResources.close;
57                this.feedEntryCheckBoxLabelUpdated.innerHTML = _nlsResources.updated;
58                this.feedEntryCheckBoxLabelSummary.innerHTML = _nlsResources.summary;
59                this.feedEntryCheckBoxLabelContent.innerHTML = _nlsResources.content;
60
61                _nlsResources = i18nEditor;
62                this.doNew.innerHTML = _nlsResources.doNew;
63                this.edit.innerHTML = _nlsResources.edit;
64                this.save.innerHTML = _nlsResources.save;
65                this.cancel.innerHTML = _nlsResources.cancel;
66        },
67       
68        setEntry: function(/*object*/entry, /*object*/feed, /*boolean*/leaveMenuState){
69                // summary:
70                //              Function to set the current entry that is being edited.
71                // entry:
72                //              Instance of dojox.atom.io.model.Entry to display for reading/editing.
73                if(this._entry !== entry){
74                        //If we swap entries, we don't want to keep the menu states and modes.
75                        this._editMode=false;
76                        leaveMenuState=false;
77                }else{
78                        leaveMenuState = true;
79                }
80                FeedEntryEditor.superclass.setEntry.call(this, entry, feed);
81                this._editable = this._isEditable(entry);
82                if(!leaveMenuState && !this._editable){
83                        domStyle.set(this.entryEditButton, 'display', 'none');
84                        domStyle.set(this.entrySaveCancelButtons, 'display', 'none');
85                }
86                if(this._editable && this.enableEdit){
87                        if(!leaveMenuState){
88                                domStyle.set(this.entryEditButton, 'display', '');
89                                //TODO double check this &&...
90                                if(this.enableMenuFade && this.entrySaveCancelButton){
91                                        fx.fadeOut({node: this.entrySaveCancelButton,duration: 250}).play();
92                                }
93                        }
94                }
95        },
96
97        _toggleEdit: function(){
98                // summary:
99                //              Internal function for toggling/enabling the display of edit mode
100                // returns:
101                //              Nothing.
102                if(this._editable && this.enableEdit){
103                        domStyle.set(this.entryEditButton, 'display', 'none');
104                        domStyle.set(this.entrySaveCancelButtons, 'display', '');
105                        this._editMode = true;
106
107                        //Rebuild the view using the same entry and feed.
108                        this.setEntry(this._entry, this._feed, true);
109                }
110        },
111
112        _handleEvent: function(/*object*/entrySelectionEvent){
113                // summary:
114                //              Internal function for listening to a topic that will handle entry notification.
115                // entrySelectionEvent:
116                //              The topic message containing the entry that was selected for view.
117                // returns:
118                //              Nothing.
119                if(entrySelectionEvent.source != this && entrySelectionEvent.action == "delete" &&
120                        entrySelectionEvent.entry && entrySelectionEvent.entry == this._entry){
121                                domStyle.set(this.entryEditButton, 'display', 'none');
122                }
123                FeedEntryEditor.superclass._handleEvent.call(this, entrySelectionEvent);
124        },
125
126        _isEditable: function(/*object*/entry){
127                // summary:
128                //              Internal function for determining of a particular entry is editable.
129                // description:
130                //              Internal function for determining of a particular entry is editable.
131                //              This is used for determining if the delete action should be displayed or not.
132                // entry:
133                //              The dojox.atom.io.model.Entry object to examine
134                // returns:
135                //              Boolean denoting if the entry seems editable or not..
136                var retVal = false;
137                if(entry && entry !== null && entry.links && entry.links !== null){
138                        for(var x in entry.links){
139                                if(entry.links[x].rel && entry.links[x].rel == "edit"){
140                                        retVal = true;
141                                        break;
142                                }
143                        }
144                }
145                return retVal;
146        },
147       
148        // The following set<Attribute> functions override the corresponding functions in FeedEntryViewer.  These handle
149        // the editMode flag by inserting appropriate editor widgets inside of just splashing the content in the page.
150        setTitle: function(/*DOM node*/titleAnchorNode, /*boolean*/editMode, /*object*/entry){
151                // summary:
152                //              Function to set the contents of the title node in the template to some value from the entry.
153                // description:
154                //              Function to set the contents of the title node in the template to some value from the entry.
155                //              This exists specifically so users can over-ride how the title data is filled out from an entry.
156                // titleAnchorNode:
157                //              The DOM node to attach the title data to.
158                // editMode:
159                //              Boolean to indicate if the display should be in edit mode or not.
160                // entry:
161                //              The Feed Entry to work with.
162
163                if(!editMode){
164                        FeedEntryEditor.superclass.setTitle.call(this, titleAnchorNode, editMode, entry);
165                        if(entry.title && entry.title.value && entry.title.value !== null){
166                                this.setFieldValidity("title", true);
167                        }
168                }else{
169                        if(entry.title && entry.title.value && entry.title.value !== null){
170                                if(!this._toLoad){
171                                        this._toLoad = [];
172                                }
173                                this.entryTitleSelect.value = entry.title.type;
174                               
175                                var editor = this._createEditor(titleAnchorNode, entry.title, true, entry.title.type === "html" || entry.title.type === "xhtml");
176                                editor.name = "title";
177                                this._toLoad.push(editor);
178                                this.setFieldValidity("titleedit",true);
179                                this.setFieldValidity("title",true);
180                        }
181                }
182        },
183
184        setAuthors: function(/*DOM node*/authorsAnchorNode, /*boolean*/editMode, /*object*/entry){
185                // summary:
186                //              Function to set the contents of the author node in the template to some value from the entry.
187                // description:
188                //              Function to set the contents of the author node in the template to some value from the entry.
189                //              This exists specifically so users can over-ride how the title data is filled out from an entry.
190                // authorsAnchorNode:
191                //              The DOM node to attach the author data to.
192                // editMode:
193                //              Boolean to indicate if the display should be in edit mode or not.
194                // entry:
195                //              The Feed Entry to work with.
196                if(!editMode){
197                        FeedEntryEditor.superclass.setAuthors.call(this, authorsAnchorNode, editMode, entry);
198                        if(entry.authors && entry.authors.length > 0){
199                                this.setFieldValidity("authors", true);
200                        }
201                }else{
202                        if(entry.authors && entry.authors.length > 0){
203                                this._editors.authors = this._createPeopleEditor(this.entryAuthorNode, {data: entry.authors, name: "Author"});
204                                this.setFieldValidity("authors", true);
205                        }
206                }
207        },
208
209
210        setContributors: function(/*DOM node*/contributorsAnchorNode, /*boolean*/editMode, /*object*/entry){
211                // summary:
212                //              Function to set the contents of the contributor node in the template to some value from the entry.
213                // description:
214                //              Function to set the contents of the contributor node in the template to some value from the entry.
215                //              This exists specifically so users can over-ride how the title data is filled out from an entry.
216                // contributorsAnchorNode:
217                //              The DOM node to attach the contributor data to.
218                // editMode:
219                //              Boolean to indicate if the display should be in edit mode or not.
220                // entry:
221                //              The Feed Entry to work with.
222                if(!editMode){
223                        FeedEntryEditor.superclass.setContributors.call(this, contributorsAnchorNode, editMode, entry);
224                        if(entry.contributors && entry.contributors.length > 0){
225                                this.setFieldValidity("contributors", true);
226                        }
227                }else{
228                        if(entry.contributors && entry.contributors.length > 0){
229                                this._editors.contributors = this._createPeopleEditor(this.entryContributorNode, {data: entry.contributors, name: "Contributor"});
230                                this.setFieldValidity("contributors", true);
231                        }
232                }
233        },
234
235
236        setId: function(/*DOM node*/idAnchorNode, /*boolean*/editMode, /*object*/entry){
237                // summary:
238                //              Function to set the contents of the ID  node in the template to some value from the entry.
239                // description:
240                //              Function to set the contents of the ID node in the template to some value from the entry.
241                //              This exists specifically so users can over-ride how the title data is filled out from an entry.
242                // idAnchorNode:
243                //              The DOM node to attach the ID data to.
244                // editMode:
245                //              Boolean to indicate if the display should be in edit mode or not.
246                // entry:
247                //              The Feed Entry to work with.
248                if(!editMode){
249                        FeedEntryEditor.superclass.setId.call(this, idAnchorNode, editMode, entry);
250                        if(entry.id && entry.id !== null){
251                                this.setFieldValidity("id", true);
252                        }
253                }else{
254                        if(entry.id && entry.id !== null){
255                                this._editors.id = this._createEditor(idAnchorNode, entry.id);
256                                this.setFieldValidity("id",true);
257                        }
258                }
259        },
260
261        setUpdated: function(/*DOM node*/updatedAnchorNode, /*boolean*/editMode, /*object*/entry){
262                // summary:
263                //              Function to set the contents of the updated  node in the template to some value from the entry.
264                // description:
265                //              Function to set the contents of the updated node in the template to some value from the entry.
266                //              This exists specifically so users can over-ride how the title data is filled out from an entry.
267                // updatedAnchorNode:
268                //              The DOM node to attach the updated data to.
269                // editMode:
270                //              Boolean to indicate if the display should be in edit mode or not.
271                // entry:
272                //              The Feed Entry to work with.
273                if(!editMode){
274                        FeedEntryEditor.superclass.setUpdated.call(this, updatedAnchorNode, editMode, entry);
275                        if(entry.updated && entry.updated !== null){
276                                this.setFieldValidity("updated", true);
277                        }
278                }else{
279                        if(entry.updated && entry.updated !== null){
280                                this._editors.updated = this._createEditor(updatedAnchorNode, entry.updated);
281                                this.setFieldValidity("updated",true);
282                        }
283                }
284        },
285
286
287        setSummary: function(/*DOM node*/summaryAnchorNode, /*boolean*/editMode, /*object*/entry){
288                // summary:
289                //              Function to set the contents of the summary  node in the template to some value from the entry.
290                // description:
291                //              Function to set the contents of the summary node in the template to some value from the entry.
292                //              This exists specifically so users can over-ride how the title data is filled out from an entry.
293                // summaryAnchorNode:
294                //              The DOM node to attach the summary data to.
295                // editMode:
296                //              Boolean to indicate if the display should be in edit mode or not.
297                // entry:
298                //              The Feed Entry to work with.
299                if(!editMode){
300                        FeedEntryEditor.superclass.setSummary.call(this, summaryAnchorNode, editMode, entry);
301                        if(entry.summary && entry.summary.value && entry.summary.value !== null){
302                                this.setFieldValidity("summary", true);
303                        }
304                }else{
305                        if(entry.summary && entry.summary.value && entry.summary.value !== null){
306                                if(!this._toLoad){
307                                        this._toLoad = [];
308                                }
309                                this.entrySummarySelect.value = entry.summary.type;
310                               
311                                var editor = this._createEditor(summaryAnchorNode, entry.summary, true, entry.summary.type === "html" || entry.summary.type === "xhtml");
312                                editor.name = "summary";
313                                this._toLoad.push(editor);
314                                this.setFieldValidity("summaryedit",true);
315                                this.setFieldValidity("summary",true);
316                        }
317                }
318        },
319
320        setContent: function(/*DOM node*/contentAnchorNode, /*boolean*/editMode, /*object*/entry){
321                // summary:
322                //              Function to set the contents of the content node in the template to some value from the entry.
323                // description:
324                //              Function to set the contents of the content node in the template to some value from the entry.
325                //              This exists specifically so users can over-ride how the title data is filled out from an entry.
326                // summaryAnchorNode:
327                //              The DOM node to attach the content data to.
328                // editMode:
329                //              Boolean to indicate if the display should be in edit mode or not.
330                // entry:
331                //              The Feed Entry to work with.
332                if(!editMode){
333                        FeedEntryEditor.superclass.setContent.call(this, contentAnchorNode, editMode, entry);
334                        if(entry.content && entry.content.value && entry.content.value !== null){
335                                this.setFieldValidity("content",true);
336                        }
337                }else{
338                        if(entry.content && entry.content.value && entry.content.value !== null){
339                                if(!this._toLoad){
340                                        this._toLoad = [];
341                                }
342                                this.entryContentSelect.value = entry.content.type;
343                                var editor = this._createEditor(contentAnchorNode, entry.content, true, entry.content.type === "html" || entry.content.type === "xhtml");
344                                editor.name = "content";
345                                this._toLoad.push(editor);
346                                this.setFieldValidity("contentedit",true);
347                                this.setFieldValidity("content",true);
348                        }
349                }
350        },
351       
352        _createEditor: function(/*DOM node*/anchorNode, /*DOM node*/node, /*boolean*/multiline, /*object*/rte){
353                // summary:
354                //              Function to create an appropriate text editor widget based on the given parameters.
355                // anchorNode:
356                //              The DOM node to attach the editor widget to.
357                // node:
358                //              An object containing the value to be put into the editor.  This ranges from an anonymous object
359                //              with a value parameter to a dojox.atom.io.model.Content object.
360                // multiline:
361                //              A boolean indicating whether the content should be multiline (such as a textarea) instead of a
362                //              single line (such as a textbox).
363                // rte:
364                //              A boolean indicating whether the content should be a rich text editor widget.
365                // returns:
366                //              Either a widget (for textarea or textbox widgets) or an anonymous object to be used to create a
367                //              rich text area widget.
368                var viewNode;
369                var box;
370                if(!node){
371                        if(rte){
372                                // Returns an anonymous object which would then be loaded later, after the containing element
373                                // exists on the page.
374                                return {anchorNode: anchorNode,
375                                                entryValue: "",
376                                                editor: null,
377                                                generateEditor: function(){
378                                                        // The only way I found I could get the editor to behave consistently was to
379                                                        // create the content on a span, and allow the content editor to replace it.
380                                                        // This gets around the dynamic/delayed way in which content editors get created.
381                                                        var node = document.createElement("div");
382                                                        node.innerHTML = this.entryValue;
383                                                        this.anchorNode.appendChild(node);
384                                                        var _editor = new Editor({}, node);
385                                                        this.editor = _editor;
386                                                        return _editor;
387                                                }
388                                };
389                        }
390                        if(multiline){
391                                // If multiline, create a textarea
392                                viewNode = document.createElement("textarea");
393                                anchorNode.appendChild(viewNode);
394                                domStyle.set(viewNode, 'width', '90%');
395                                box = new SimpleTextarea({},viewNode);
396                        }else{
397                                // If single line, create a textbox.
398                                viewNode = document.createElement("input");
399                                anchorNode.appendChild(viewNode);
400                                domStyle.set(viewNode, 'width', '95%');
401                                box = new TextBox({},viewNode);
402                        }
403                        box.attr('value', '');
404                        return box;
405                }
406
407                // Check through the node parameter to get the value to be used.
408                var value;
409                if(node.value !== undefined){
410                        value = node.value;
411                }else if(node.attr){
412                        value = node.attr('value');
413                }else{
414                        value = node;
415                }
416                if(rte){
417                        // Returns an anonymous object which would then be loaded later, after the containing element
418                        // exists on the page.
419                        if(value.indexOf("<") != -1){
420                                value = value.replace(/</g, "&lt;");
421                        }
422                        return {anchorNode: anchorNode,
423                                        entryValue: value,
424                                        editor: null,
425                                        generateEditor: function(){
426                                                // The only way I found I could get the editor to behave consistently was to
427                                                // create the content on a span, and allow the content editor to replace it.
428                                                // This gets around the dynamic/delayed way in which content editors get created.
429                                                var node = document.createElement("div");
430                                                node.innerHTML = this.entryValue;
431                                                this.anchorNode.appendChild(node);
432                                                var _editor = new Editor({}, node);
433                                                this.editor = _editor;
434                                                return _editor;
435                                        }
436                        };
437                }
438                if(multiline){
439                        // If multiline, create a textarea
440                        viewNode = document.createElement("textarea");
441                        anchorNode.appendChild(viewNode);
442                        domStyle.set(viewNode, 'width', '90%');
443                        box = new SimpleTextarea({},viewNode);
444                }else{
445                        // If single line, create a textbox.
446                        viewNode = document.createElement("input");
447                        anchorNode.appendChild(viewNode);
448                        domStyle.set(viewNode, 'width', '95%');
449                        box = new TextBox({},viewNode);
450                }
451                box.attr('value', value);
452                return box;
453        },
454       
455        _switchEditor: function(/*object*/event){
456                // summary:
457                //              Function to switch between editor types.
458                // description:
459                //              Function to switch between a rich text editor and a textarea widget.  Used for title, summary,
460                //              And content when switching between text and html/xhtml content.
461                // event:
462                //              The event generated by the change in the select box on the page.
463                var type = null;
464                var target = null;
465                var parent = null;
466               
467                // Determine the source/target of this event (to determine which editor we're switching)
468                if(has("ie")){
469                        target = event.srcElement;
470                }else{
471                        target = event.target;
472                }
473                       
474                // Determine which editor (title, summary, or content)
475                if(target === this.entryTitleSelect){
476                        parent = this.entryTitleNode;
477                        type = "title";
478                } else if(target === this.entrySummarySelect){
479                        parent = this.entrySummaryNode;
480                        type = "summary";
481                }else{
482                        parent = this.entryContentNode;
483                        type = "content";
484                }
485
486                // Grab the existing editor.
487                var editor = this._editors[type];
488                var newEditor;
489                var value;
490               
491                if(target.value === "text"){
492                        if(editor.isInstanceOf(Editor)){
493                                // If we're changing the type to text and our existing editor is a rich text editor, we need to destroy
494                                // it and switch to a multiline editor.
495                                value = editor.attr('value', false);
496                                editor.close(false,true);
497                                editor.destroy();
498                                while(parent.firstChild){
499                                        domConstruct.destroy(parent.firstChild);
500                                }
501                                newEditor = this._createEditor(parent, {value: value}, true, false);
502                                this._editors[type] = newEditor;
503                        }
504                }else{
505                        if(!editor.isInstanceOf(Editor)){
506                                // Otherwise, we're switching to a html or xhtml type, but we currently have a textarea widget.  We need
507                                // to destroy the existing RTE and create a multiline textarea widget.
508                                value = editor.attr('value');
509                                editor.destroy();
510                                while(parent.firstChild){
511                                        domConstruct.destroy(parent.firstChild);
512                                }
513                                newEditor = this._createEditor(parent, {value: value}, true, true);
514                                newEditor = lang.hitch(newEditor, newEditor.generateEditor)();
515                                this._editors[type] = newEditor;
516                        }
517                }
518        },
519       
520        _createPeopleEditor: function(/*DOM node*/anchorNode, /*DOM node*/node){
521                // summary:
522                //              Creates a People Editor widget, sets its value, and returns it.
523                // anchorNode:
524                //              The node to attach the editor to.
525                // node:
526                //              An object containing the value to be put into the editor. Typically, this is an
527                //              dojox.atom.io.model.Person object.
528                // returns:
529                //              A new People Editor object.
530                var viewNode = document.createElement("div");
531                anchorNode.appendChild(viewNode);
532                return new PeopleEditor(node,viewNode);
533        },
534
535        saveEdits: function(){
536                // summary:
537                //              Saves edits submitted when the 'save' button is pressed.
538                // description:
539                //              Saves edits submitted when the 'save' button is pressed.  Distinguishes between new and existing
540                //              entries and saves appropriately.  Fetches the values of the editors, and, if existing, compares them to
541                //              the existing values and submits the updates, otherwise creates a new entry and posts it as a new entry.
542                // returns:
543                //              Nothing.
544                domStyle.set(this.entrySaveCancelButtons, 'display', 'none');
545                domStyle.set(this.entryEditButton, 'display', '');
546                domStyle.set(this.entryNewButton, 'display', '');
547                var modifiedEntry = false;
548                var value;
549                var i;
550                var changed;
551                var entry;
552                var authors;
553                var contributors;
554                if(!this._new){
555                        entry = this.getEntry();
556                        if(this._editors.title && (this._editors.title.attr('value') != entry.title.value || this.entryTitleSelect.value != entry.title.type)){
557                                value = this._editors.title.attr('value');
558                                if(this.entryTitleSelect.value === "xhtml"){
559                                        value = this._enforceXhtml(value);
560                                        if(value.indexOf('<div xmlns="http://www.w3.org/1999/xhtml">') !== 0){
561                                                value = '<div xmlns="http://www.w3.org/1999/xhtml">' + value + '</div>';
562                                        }
563                                }
564                                entry.title = new model.Content("title", value, null, this.entryTitleSelect.value);
565                                modifiedEntry = true;
566                        }
567                       
568                        if(this._editors.id.attr('value') != entry.id){
569                                entry.id = this._editors.id.attr('value');
570                                modifiedEntry = true;
571                        }
572                       
573                        if(this._editors.summary && (this._editors.summary.attr('value') != entry.summary.value || this.entrySummarySelect.value != entry.summary.type)){
574                                value = this._editors.summary.attr('value');
575                                if(this.entrySummarySelect.value === "xhtml"){
576                                        value = this._enforceXhtml(value);
577                                        if(value.indexOf('<div xmlns="http://www.w3.org/1999/xhtml">') !== 0){
578                                                value = '<div xmlns="http://www.w3.org/1999/xhtml">' + value + '</div>';
579                                        }
580                                }
581                                entry.summary = new model.Content("summary", value, null, this.entrySummarySelect.value);
582                                modifiedEntry = true;
583                        }
584                       
585                        if(this._editors.content && (this._editors.content.attr('value') != entry.content.value || this.entryContentSelect.value != entry.content.type)){
586                                value = this._editors.content.attr('value');
587                                if(this.entryContentSelect.value === "xhtml"){
588                                        value = this._enforceXhtml(value);
589                                        if(value.indexOf('<div xmlns="http://www.w3.org/1999/xhtml">') !== 0){
590                                                value = '<div xmlns="http://www.w3.org/1999/xhtml">' + value + '</div>';
591                                        }
592                                }
593                                entry.content = new model.Content("content", value, null, this.entryContentSelect.value);
594                                modifiedEntry = true;
595                        }
596                       
597                        if(this._editors.authors){
598                                if(modifiedEntry){
599                                        entry.authors = [];
600                                        authors = this._editors.authors.getValues();
601                                        for(i in authors){
602                                                if(authors[i].name || authors[i].email || authors[i].uri){
603                                                        entry.addAuthor(authors[i].name, authors[i].email, authors[i].uri);
604                                                }
605                                        }
606                                }else{
607                                        var currentAuthors = entry.authors;
608                                        var searchAuthors = function(name, email, uri){
609                                                for(i in currentAuthors){
610                                                        if(currentAuthors[i].name === name && currentAuthors[i].email === email && currentAuthors[i].uri === uri){
611                                                                return true;
612                                                        }
613                                                }
614                                                return false;
615                                        };
616                                        authors = this._editors.authors.getValues();
617                                        changed = false;
618                                        for(i in authors){
619                                                if(!searchAuthors(authors[i].name, authors[i].email, authors[i].uri)){
620                                                        changed = true;
621                                                        break;
622                                                }
623                                        }
624                                        if(changed){
625                                                entry.authors = [];
626                                                for(i in authors){
627                                                        if(authors[i].name || authors[i].email || authors[i].uri){
628                                                                entry.addAuthor(authors[i].name, authors[i].email, authors[i].uri);
629                                                        }
630                                                }
631                                                modifiedEntry = true;
632                                        }
633                                }
634                        }
635                       
636                        if(this._editors.contributors){
637                        if(modifiedEntry){
638                                        entry.contributors = [];
639                                        contributors = this._editors.contributors.getValues();
640                                        for(i in contributors){
641                                                if(contributors[i].name || contributors[i].email || contributors[i].uri){
642                                                        entry.addAuthor(contributors[i].name, contributors[i].email, contributors[i].uri);
643                                                }
644                                        }
645                                }else{
646                                        var currentContributors = entry.contributors;
647                                        var searchContributors = function(name, email, uri){
648                                                for(i in currentContributors){
649                                                        if(currentContributors[i].name === name && currentContributors[i].email === email && currentContributors[i].uri === uri){
650                                                                return true;
651                                                        }
652                                                }
653                                                return false;
654                                        };
655                                        contributors = this._editors.contributors.getValues();
656                                        changed = false;
657                                        for(i in contributors){
658                                                if(searchContributors(contributors[i].name, contributors[i].email, contributors[i].uri)){
659                                                        changed = true;
660                                                        break;
661                                                }
662                                        }
663                                        if(changed){
664                                                entry.contributors = [];
665                                                for(i in contributors){
666                                                        if(contributors[i].name || contributors[i].email || contributors[i].uri){
667                                                                entry.addContributor(contributors[i].name, contributors[i].email, contributors[i].uri);
668                                                        }
669                                                }
670                                                modifiedEntry = true;
671                                        }
672                                }
673                        }
674
675                        if(modifiedEntry){
676                                dojo.publish(this.entrySelectionTopic, [{action: "update", source: this, entry: entry, callback: this._handleSave }]);
677                                //TODO: REMOVE BELOW
678                                //var atomIO = new dojox.atom.io.Connection();
679                                //atomIO.updateEntry(entry, dojo.hitch(this,this._handleSave));
680                                //WARNING: Use above when testing with SimpleProxy (or any other servlet which
681                                //                      doesn't actually create a new entry and return it properly)
682                                //atomIO.updateEntry(entry, dojo.hitch(this,this._handleSave), true);
683                        }
684                }else{
685                        this._new = false;
686                        entry = new model.Entry();
687                       
688                        value = this._editors.title.attr('value');
689                        if(this.entryTitleSelect.value === "xhtml"){
690                                value = this._enforceXhtml(value);
691                                value = '<div xmlns="http://www.w3.org/1999/xhtml">' + value + '</div>';
692                        }
693                        entry.setTitle(value, this.entryTitleSelect.value);
694                        entry.id = this._editors.id.attr('value');
695                       
696                        authors = this._editors.authors.getValues();
697                        for(i in authors){
698                                if(authors[i].name || authors[i].email || authors[i].uri){
699                                        entry.addAuthor(authors[i].name, authors[i].email, authors[i].uri);
700                                }
701                        }
702                                       
703                        contributors = this._editors.contributors.getValues();
704                        for(i in contributors){
705                                if(contributors[i].name || contributors[i].email || contributors[i].uri){
706                                        entry.addContributor(contributors[i].name, contributors[i].email, contributors[i].uri);
707                                }
708                        }
709
710                       
711                        value = this._editors.summary.attr('value');
712                        if(this.entrySummarySelect.value === "xhtml"){
713                                value = this._enforceXhtml(value);
714                                value = '<div xmlns="http://www.w3.org/1999/xhtml">' + value + '</div>';
715                        }
716                        entry.summary = new model.Content("summary", value, null, this.entrySummarySelect.value);
717
718                        value = this._editors.content.attr('value');
719                        if(this.entryContentSelect.value === "xhtml"){
720                                value = this._enforceXhtml(value);
721                                value = '<div xmlns="http://www.w3.org/1999/xhtml">' + value + '</div>';
722                        }
723                        entry.content = new model.Content("content", value, null, this.entryContentSelect.value);
724
725                        domStyle.set(this.entryNewButton, 'display', '');
726                        dojo.publish(this.entrySelectionTopic, [{action: "post", source: this, entry: entry }]);
727                }
728                this._editMode = false;
729               
730                //Rebuild the view using the same entry and feed.
731                this.setEntry(entry, this._feed, true);
732        },
733       
734        _handleSave: function(/*object*/entry, /*string*/ location){
735                // summary:
736                //              Function for handling the save of an entry, cleaning up the display after the edit is completed.
737                // entry: dojox.atom.io.model.Entry object
738                //              The entry that was saved.
739                // Location: String
740                //              A URL to be used, not used here, but part of the call back from the AtomIO
741                // returns: Nothing
742
743                //Close the editor and revert out.
744                this._editMode = false;
745               
746                //Rebuild the view using the same entry and feed.
747                this.clear();
748                this.setEntry(entry, this.getFeed(), true);
749        },
750
751        cancelEdits: function(){
752                // summary:
753                //              Cancels edits and reverts the editor to its previous state (display mode)
754                // returns:
755                //              Nothing.
756                this._new = false;
757                domStyle.set(this.entrySaveCancelButtons, 'display', 'none');
758                if(this._editable){
759                        domStyle.set(this.entryEditButton, 'display', '');
760                }
761                domStyle.set(this.entryNewButton, 'display', '');
762                this._editMode = false;
763               
764                //Rebuild the view using the same entry and feed.
765                this.clearEditors();
766                this.setEntry(this.getEntry(), this.getFeed(), true);
767        },
768
769        clear: function(){
770                // summary:
771                //              Clears the editor, destroys all editors, leaving the editor completely clear
772                this._editable=false;
773                this.clearEditors();
774                FeedEntryEditor.superclass.clear.apply(this);
775                if(this._contentEditor){
776                        // Note that the superclass clear destroys the widget since it's in the child widget list,
777                        // so this is just ref clearing.
778                        this._contentEditor = this._setObject = this._oldContent = this._contentEditorCreator = null;
779                        this._editors = {};
780                }
781        },
782       
783        clearEditors: function(){
784                for(var key in this._editors){
785                        if(this._editors[key].isInstanceOf(Editor)){
786                                this._editors[key].close(false, true);
787                        }
788                        this._editors[key].destroy();
789                }
790                this._editors = {};
791        },
792
793        _enforceXhtml: function(/*string*/html){
794                // summary:
795                //              Function for cleaning up/enforcing the XHTML standard in HTML returned from the editor2 widget.
796                // html:
797                //              HTML string to be enforced as xhtml.
798                // returns:
799                //              string of cleaned up HTML.
800                var xhtml = null;
801                if(html){
802                        //Handle <BR>
803                        var brRegExp = /<br>/g;
804                        xhtml = html.replace(brRegExp, "<br/>");
805
806                        //Handle <HR>
807                        xhtml = this._closeTag(xhtml, "hr");
808
809                        //Handle <img>
810                        xhtml = this._closeTag(xhtml, "img");
811                }
812                return xhtml;
813        },
814
815        _closeTag: function(/*string*/xhtml, /*string*/tag){
816                // summary:
817                //              Function for closing tags in a text of HTML/XHTML
818                // xhtml: String
819                //              XHTML string which needs the closing tag.
820                // tag:
821                //              The tag to close.
822                // returns:
823                //              string of cleaned up HTML.
824
825                // NOTE:  Probably should redo this function in a more efficient way.  This could get expensive.
826                var tagStart = "<" + tag;
827                var tagIndex = xhtml.indexOf(tagStart);
828                if(tagIndex !== -1){
829                        while (tagIndex !== -1){
830                                var tempString = "";
831                                var foundTagEnd = false;
832                                for (var i = 0; i < xhtml.length; i++){
833                                        var c = xhtml.charAt(i);
834                                        if(i <= tagIndex ||foundTagEnd){
835                                                tempString += c;
836                                        }
837                                        else
838                                        {
839                                                if(c === '>'){
840                                                        tempString += "/";
841                                                        foundTagEnd = true;
842                                                }
843                                                tempString +=c;
844                                        }
845                                }
846                                xhtml = tempString;
847                                tagIndex = xhtml.indexOf(tagStart, tagIndex + 1);
848                        }
849                }
850                return xhtml;
851        },
852       
853        _toggleNew: function(){
854                // summary:
855                //              Function to put the editor into a state to create a new entry.
856
857                // Hide the edit/new buttons and show the save/cancel buttons.
858                domStyle.set(this.entryNewButton, 'display', 'none');
859                domStyle.set(this.entryEditButton, 'display', 'none');
860                domStyle.set(this.entrySaveCancelButtons, 'display', '');
861               
862                // Reset the type select boxes to text.
863                this.entrySummarySelect.value = "text";
864                this.entryContentSelect.value = "text";
865                this.entryTitleSelect.value = "text";
866               
867                // Clear all nodes.
868                this.clearNodes();
869                this._new = true;
870               
871                var _nlsResources = i18nViewer;
872                // Create all headers and editors.
873                var titleHeader = new FeedEntryViewer.EntryHeader({title: _nlsResources.title});
874                this.entryTitleHeader.appendChild(titleHeader.domNode);
875               
876                this._editors.title = this._createEditor(this.entryTitleNode, null);
877                this.setFieldValidity("title",true);
878               
879                var authorHeader = new FeedEntryViewer.EntryHeader({title: _nlsResources.authors});
880                this.entryAuthorHeader.appendChild(authorHeader.domNode);
881
882                this._editors.authors = this._createPeopleEditor(this.entryAuthorNode, {name: "Author"});
883                this.setFieldValidity("authors", true);
884               
885                var contributorHeader = new FeedEntryViewer.EntryHeader({title: _nlsResources.contributors});
886                this.entryContributorHeader.appendChild(contributorHeader.domNode);
887
888                this._editors.contributors = this._createPeopleEditor(this.entryContributorNode, {name: "Contributor"});
889                this.setFieldValidity("contributors", true);
890               
891                var idHeader = new FeedEntryViewer.EntryHeader({title: _nlsResources.id});
892                this.entryIdHeader.appendChild(idHeader.domNode);
893               
894                this._editors.id = this._createEditor(this.entryIdNode, null);
895                this.setFieldValidity("id",true);
896
897                var updatedHeader = new FeedEntryViewer.EntryHeader({title: _nlsResources.updated});
898                this.entryUpdatedHeader.appendChild(updatedHeader.domNode);
899               
900                this._editors.updated = this._createEditor(this.entryUpdatedNode, null);
901                this.setFieldValidity("updated",true);
902
903                var summaryHeader = new FeedEntryViewer.EntryHeader({title: _nlsResources.summary});
904                this.entrySummaryHeader.appendChild(summaryHeader.domNode);
905               
906                this._editors.summary = this._createEditor(this.entrySummaryNode, null, true);
907                this.setFieldValidity("summaryedit",true);
908                this.setFieldValidity("summary",true);
909
910                var contentHeader = new FeedEntryViewer.EntryHeader({title: _nlsResources.content});
911                this.entryContentHeader.appendChild(contentHeader.domNode);
912               
913                this._editors.content = this._createEditor(this.entryContentNode, null, true);
914                this.setFieldValidity("contentedit",true);
915                this.setFieldValidity("content",true);
916
917                // Show the sections.
918                this._displaySections();
919        },
920       
921        _displaySections: function(){
922                // summary:
923                //              Function to display the appropriate sections based on validity.
924
925                // Hide select boxes.
926                domStyle.set(this.entrySummarySelect, 'display', 'none');
927                domStyle.set(this.entryContentSelect, 'display', 'none');
928                domStyle.set(this.entryTitleSelect, 'display', 'none');
929
930                // Show select boxes if the flags are set.
931                if(this.isFieldValid("contentedit")){
932                        domStyle.set(this.entryContentSelect, 'display', '');
933                }
934                if(this.isFieldValid("summaryedit")){
935                        domStyle.set(this.entrySummarySelect, 'display', '');
936                }
937                if(this.isFieldValid("titleedit")){
938                        domStyle.set(this.entryTitleSelect, 'display', '');
939                }
940                // Call super's _displaySections.
941                FeedEntryEditor.superclass._displaySections.apply(this);
942               
943                // If we have editors to load after the nodes are created on the page, execute those now.
944                if(this._toLoad){
945                        for(var i in this._toLoad){
946                                var editor;
947                                if(this._toLoad[i].generateEditor){
948                                        editor = lang.hitch(this._toLoad[i], this._toLoad[i].generateEditor)();
949                                }else{
950                                        editor = this._toLoad[i];
951                                }
952                                this._editors[this._toLoad[i].name] = editor;
953                                this._toLoad[i] = null;
954                        }
955                        this._toLoad = null;
956                }
957        }
958});
959
960var PeopleEditor = declare("dojox.atom.widget.PeopleEditor", [_Widget, _Templated, _Container],{
961                // summary:
962                //              An editor for dojox.atom.io.model.Person objects.
963                // description:
964                //              An editor for dojox.atom.io.model.Person objects.  Displays multiple rows for the respective arrays
965                //              of people.  Can add/remove rows on the fly.
966                templateString: peopleEditorTemplate,
967
968                _rows: [],
969                _editors: [],
970                _index: 0,
971                _numRows: 0,
972               
973                postCreate: function(){
974                        // Initializer function for the PeopleEditor widget.
975                        var _nlsResources = i18nPeople;
976                        if(this.name){
977                                if(this.name == "Author"){
978                                        this.peopleEditorButton.appendChild(document.createTextNode("["+_nlsResources.addAuthor+"]"));
979                                }else if(this.name == "Contributor"){
980                                        this.peopleEditorButton.appendChild(document.createTextNode("["+_nlsResources.addContributor+"]"));
981                                }
982                        }else{
983                                this.peopleEditorButton.appendChild(document.createTextNode("["+_nlsResources.add+"]"));
984                        }
985                        this._editors = [];
986
987                        if(!this.data || this.data.length===0){
988                                this._createEditors(null, null, null, 0, this.name);
989                                this._index = 1;
990                        }else{
991                                for(var i in this.data){
992                                        this._createEditors(this.data[i].name, this.data[i].email, this.data[i].uri, i);
993                                        this._index++;
994                                        this._numRows++;
995                                }
996                        }
997                },
998               
999                destroy: function(){
1000                        for(var key in this._editors){
1001                                for(var key2 in this._editors[key]){
1002                                        this._editors[key][key2].destroy();
1003                                }
1004                        }
1005                        this._editors = [];
1006                },
1007               
1008                _createEditors: function(/*string*/name, /*string*/email, /*string*/uri, /*int*/index, /*string*/widgetName){
1009                        // summary:
1010                        //              creates editor boxes (textbox widgets) for the individual values of a Person.
1011                        // name:
1012                        //              The name of this Person.
1013                        // email:
1014                        //              The email of this Person.
1015                        // uri:
1016                        //              The Person's URI.
1017                        // index:
1018                        //              The row index to use for this Person.
1019                        var row = document.createElement("tr");
1020                        this.peopleEditorEditors.appendChild(row);
1021                        row.id = "removeRow"+index;
1022                       
1023                        var node = document.createElement("td");
1024                        node.setAttribute('align', 'right');
1025                        row.appendChild(node);
1026                        node.colSpan = 2;
1027                       
1028                        if(this._numRows>0){
1029                                var hr = document.createElement("hr");
1030                                node.appendChild(hr);
1031                                hr.id = "hr"+index;
1032                        }
1033                       
1034                        row = document.createElement("span");
1035                        node.appendChild(row);
1036                        row.className = "peopleEditorButton";
1037                        domStyle.set(row, 'font-size', 'x-small');
1038                        connect.connect(row, "onclick", this, "_removeEditor");
1039                        row.id = "remove"+index;
1040                       
1041                        node = document.createTextNode("[X]");
1042                        row.appendChild(node);
1043                       
1044                        row = document.createElement("tr");
1045                        this.peopleEditorEditors.appendChild(row);
1046                        row.id = "editorsRow"+index;
1047                       
1048                        var labelNode = document.createElement("td");
1049                        row.appendChild(labelNode);
1050                        domStyle.set(labelNode, 'width', '20%');
1051                       
1052                        node = document.createElement("td");
1053                        row.appendChild(node);
1054                       
1055                        row = document.createElement("table");
1056                        labelNode.appendChild(row);
1057                        domStyle.set(row, 'width', '100%');
1058                       
1059                        labelNode = document.createElement("tbody");
1060                        row.appendChild(labelNode);
1061                       
1062                        row = document.createElement("table");
1063                        node.appendChild(row);
1064                        domStyle.set(row, 'width', '100%');
1065                       
1066                        node = document.createElement("tbody");
1067                        row.appendChild(node);
1068
1069                        this._editors[index] = [];
1070                        this._editors[index].push(this._createEditor(name, widgetName+'name'+index, 'Name:', labelNode, node));
1071                        this._editors[index].push(this._createEditor(email, widgetName+'email'+index, 'Email:', labelNode, node));
1072                        this._editors[index].push(this._createEditor(uri, widgetName+'uri'+index, 'URI:', labelNode, node));
1073                },
1074               
1075                _createEditor: function(/*string*/value, /*string*/id, /*string*/name, /*DOM node*/labelNode, /*DOM node*/node){
1076                        // summary:
1077                        //              Creates an individual editor widget (textbox) for a value.
1078                        // value:
1079                        //              The initial value of the textbox
1080                        // id:
1081                        //              The id the textbox should have.
1082                        // name:
1083                        //              The text to put in the label element for this textbox.
1084                        // labelNode:
1085                        //              The node to attach the label to.
1086                        // node:
1087                        //              The node to attach the editor rows to.
1088                        // returns:
1089                        //              Editor widget.
1090                        var row = document.createElement("tr");
1091                        labelNode.appendChild(row);
1092                       
1093                        var label = document.createElement("label");
1094                        label.setAttribute('for', id);
1095                        label.appendChild(document.createTextNode(name));
1096                        labelNode = document.createElement("td");
1097                        labelNode.appendChild(label);
1098                        row.appendChild(labelNode);
1099                       
1100                        row = document.createElement("tr");
1101                        node.appendChild(row);
1102                               
1103                        node = document.createElement("td");
1104                        row.appendChild(node);
1105                       
1106                        var viewNode = document.createElement("input");
1107                        viewNode.setAttribute('id', id);
1108                        node.appendChild(viewNode);
1109                        domStyle.set(viewNode, 'width', '95%');
1110                       
1111                        var box = new TextBox({},viewNode);
1112                        box.attr('value', value);
1113                        return box;
1114                },
1115               
1116                _removeEditor: function(/*object*/event){
1117                        // summary:
1118                        //              Removes a Person from our list of editors.
1119                        // description:
1120                        //              Removes a Person from our list of editors by removing the block of editors that
1121                        //              make up that Person.
1122                        // event:
1123                        //              The event generated when the remove button is pressed on the page.
1124                        var target = null;
1125               
1126                        if(has("ie")){
1127                                target = event.srcElement;
1128                        }else{
1129                                target = event.target;
1130                        }
1131                               
1132                        var id = target.id;
1133                        id = id.substring(6);
1134                        for(var key in this._editors[id]){
1135                                this._editors[id][key].destroy();
1136                        }
1137                       
1138                        var node = domUtil.byId("editorsRow"+id);
1139                        var parent = node.parentNode;
1140                        parent.removeChild(node);
1141                       
1142                        node = domUtil.byId("removeRow"+id);
1143                        parent = node.parentNode;
1144                        parent.removeChild(node);
1145
1146                        this._numRows--;
1147                        if(this._numRows === 1 && parent.firstChild.firstChild.firstChild.tagName.toLowerCase() === "hr"){
1148                                node = parent.firstChild.firstChild;
1149                                node.removeChild(node.firstChild);
1150                        }
1151                        this._editors[id] = null;
1152                },
1153               
1154                _add: function(){
1155                        // summary:
1156                        //              Adds a new block of blank editors to represent a Person.
1157                        this._createEditors(null, null, null, this._index);
1158                        this._index++;
1159                        this._numRows++;
1160                },
1161               
1162                getValues: function(){
1163                        // summary:
1164                        //              Gets the values of this editor in an array.
1165                        // description:
1166                        //              Gets the values of this editor in an array, with each Person as an object within the array.
1167                        // returns:
1168                        //              An array of anonymous objects representing dojox.atom.io.model.Persons.
1169                        var values = [];
1170                        for(var i in this._editors){
1171                                if(this._editors[i]){
1172                                        values.push({name: this._editors[i][0].attr('value'), email: this._editors[i][1].attr('value'), uri: this._editors[i][2].attr('value')});
1173                                }
1174                        }
1175                        return values;
1176                }
1177});
1178
1179return FeedEntryEditor;
1180});
Note: See TracBrowser for help on using the repository browser.