001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.visualweb.designer.jsf;
043:
044: import org.netbeans.modules.visualweb.designer.html.HtmlTag;
045: import com.sun.rave.designtime.DesignProperty;
046: import com.sun.rave.designtime.markup.MarkupDesignBean;
047: import java.lang.reflect.InvocationTargetException;
048: import java.lang.reflect.Method;
049: import org.apache.xerces.dom.events.MutationEventImpl;
050: import org.netbeans.modules.visualweb.api.designer.DomProvider;
051: import org.netbeans.modules.visualweb.api.designer.DomProvider.DomPosition;
052: import org.netbeans.modules.visualweb.api.designer.DomProvider.DomPosition.Bias;
053: import org.netbeans.modules.visualweb.api.designer.cssengine.CssProvider;
054: import org.netbeans.modules.visualweb.api.designer.markup.MarkupService;
055: import org.netbeans.modules.visualweb.designer.html.HtmlAttribute;
056: import org.netbeans.modules.visualweb.insync.Util;
057: import org.netbeans.modules.visualweb.insync.faces.Entities;
058: import org.netbeans.modules.visualweb.insync.markup.MarkupUnit;
059: import org.openide.ErrorManager;
060: import org.w3c.dom.DocumentFragment;
061: import org.w3c.dom.Element;
062: import org.w3c.dom.Node;
063: import org.w3c.dom.NodeList;
064: import org.w3c.dom.events.Event;
065: import org.w3c.dom.events.EventListener;
066: import org.w3c.dom.events.EventTarget;
067:
068: /**
069: * Impl of <code>DomProvider.InlineEditorSupport</code>
070: *
071: * @author Peter Zavadsky
072: * @author Tor Norby (old original code)
073: */
074: class InlineEditorSupportImpl implements
075: DomProvider.InlineEditorSupport, EventListener {
076:
077: private final JsfForm jsfForm;
078: // XXX TODO Remove ref to domProviderImpl.
079: private final DomProviderImpl domProviderImpl;
080: private final MarkupDesignBean markupDesignBean;
081: private final DesignProperty designProperty;
082:
083: private DomPosition begin;
084: private DomPosition end;
085:
086: // XXX AttributeInlineEditor only
087: private DocumentFragment fragment;
088: private String xpath;
089: private Node text;
090: private boolean hasBeenEdited = false;
091:
092: /** Creates a new instance of InlineEditorSupportImpl */
093: public InlineEditorSupportImpl(JsfForm jsfForm,
094: DomProviderImpl domProviderImpl,
095: MarkupDesignBean markupDesignBean,
096: DesignProperty designProperty, String xpath) {
097: this .jsfForm = jsfForm;
098: this .domProviderImpl = domProviderImpl;
099: this .markupDesignBean = markupDesignBean;
100: this .designProperty = designProperty;
101: this .xpath = xpath;
102: }
103:
104: // public static DomProvider.InlineEditorSupport createDummyInlineEditorSupport() {
105: // return new DummyInlineEditorSupport();
106: // }
107: //
108: // private static class DummyInlineEditorSupport implements DomProvider.InlineEditorSupport {
109: // } // End of DummyInlineEditorSupport.
110:
111: public boolean isEditingAllowed() {
112: return DomProviderServiceImpl.isEditingAllowed(designProperty);
113: }
114:
115: public String getValueSource() {
116: return designProperty.getValueSource();
117: }
118:
119: public void unset() {
120: designProperty.unset();
121: }
122:
123: public void setValue(String value) {
124: designProperty.setValue(value);
125: }
126:
127: public String getName() {
128: return designProperty.getPropertyDescriptor().getName();
129: }
130:
131: // XXX AttributeInlineEditor only.
132: public String getSpecialInitValue() {
133: return DomProviderServiceImpl
134: .getSpecialInitValue(designProperty);
135: }
136:
137: private/*public*/String getValue() {
138: // String assumption should be checked in beandescriptor search for TEXT_NODE_PROPERTY,
139: // especially if we publish this property. Or we could at least specify that the
140: // property MUST be a String.
141: return (String) designProperty.getValue();
142: }
143:
144: private/*public*/String getDisplayName() {
145: return designProperty.getPropertyDescriptor().getDisplayName();
146: }
147:
148: // public Method getWriteMethod() {
149: // return designProperty.getPropertyDescriptor().getWriteMethod();
150: // }
151: private/*public*/void setViaWriteMethod(String value) {
152: Method m = designProperty.getPropertyDescriptor()
153: .getWriteMethod();
154: try {
155: m.invoke(markupDesignBean.getInstance(),
156: new Object[] { value });
157: } catch (IllegalArgumentException ex) {
158: ErrorManager.getDefault().notify(
159: ErrorManager.INFORMATIONAL, ex);
160: } catch (IllegalAccessException ex) {
161: ErrorManager.getDefault().notify(
162: ErrorManager.INFORMATIONAL, ex);
163: } catch (InvocationTargetException ex) {
164: ErrorManager.getDefault().notify(
165: ErrorManager.INFORMATIONAL, ex);
166: }
167: }
168:
169: public Element getRenderedElement() {
170: Element sourceElement = markupDesignBean.getElement();
171: return MarkupService
172: .getRenderedElementForElement(sourceElement);
173: }
174:
175: public DocumentFragment createSourceFragment() {
176: return domProviderImpl.createSourceFragment(markupDesignBean);
177: }
178:
179: public String expandHtmlEntities(String value, boolean warn) {
180: return Entities.expandHtmlEntities(value, warn,
181: markupDesignBean.getElement());
182: }
183:
184: public boolean isEscaped() {
185: return DomProviderServiceImpl
186: .isEscapedDesignBean(markupDesignBean);
187: }
188:
189: public void handleEvent(Event e) {
190: hasBeenEdited = true;
191:
192: if (e instanceof org.w3c.dom.events.MutationEvent) {
193: org.w3c.dom.events.MutationEvent me = (org.w3c.dom.events.MutationEvent) e;
194: String old = me.getPrevValue();
195: String nw = me.getNewValue();
196:
197: if (((old != null) && (nw != null) && (old.equals(nw)))) {
198: return;
199: }
200: }
201:
202: Node n = (Node) e.getTarget();
203:
204: // DomPosition end = inlineEditorSupport.getEndPosition();
205: if (n == end.getNode()) {
206: // XXX hack Don't do this, I should have Positions be
207: // immutable.
208: // end.setOffset(n.getNodeValue().length());
209: // end = new Position(end.getNode(), n.getNodeValue().length(), end.getBias());
210: // end = Position.create(end.getNode(), n.getNodeValue().length(), end.getBias());
211: // end = webform.createDomPosition(end.getNode(), n.getNodeValue().length(), end.getBias());
212: // inlineEditorSupport.setEndPosition(webform.createDomPosition(end.getNode(), n.getNodeValue().length(), end.getBias()));
213: end = jsfForm.getDomDocumentImpl().createDomPosition(
214: end.getNode(), n.getNodeValue().length(),
215: end.getBias());
216: }
217:
218: // /*
219: // Node node = (org.w3c.dom.Node)e.getTarget();
220: // String type = e.getType();
221: // Node parent = node.getParentNode(); // XXX or use getRelatedNode?
222: //
223: // */
224: // dispatchEvent(bean);
225: Node node = (org.w3c.dom.Node) e.getTarget();
226: Node parent = node.getParentNode(); // XXX or use getRelatedNode?
227:
228: // Text node or entity node changes should get translated
229: // into a change event on their surrounding element...
230: // XXX I could possibly handle to rebreak only
231: // the LineBreakGroup.... That would save work -ESPECIALLY-
232: // for text right within the <body> tag... but optimize that
233: // later
234: if (!(node instanceof Element)
235: || ((Element) node).getTagName()
236: .equals(HtmlTag.BR.name)) { // text, cdata, entity, ...
237: node = parent;
238: parent = parent.getParentNode();
239:
240: if (node instanceof Element) {
241: // MarkupDesignBean b = ((RaveElement)node).getDesignBean();
242: // MarkupDesignBean b = InSyncService.getProvider().getMarkupDesignBeanForElement((Element)node);
243: // MarkupDesignBean b = WebForm.getDomProviderService().getMarkupDesignBeanForElement((Element)node);
244: MarkupDesignBean b = MarkupUnit
245: .getMarkupDesignBeanForElement((Element) node);
246:
247: if (b == null) {
248: // b = bean;
249: b = markupDesignBean;
250: }
251:
252: // webform.getDomSynchronizer().requestTextUpdate(b);
253: // webform.requestTextUpdate(b);
254: domProviderImpl.requestTextUpdate(b);
255: }
256: } else {
257: // webform.getDomSynchronizer().requestChange(bean);
258: // webform.requestChange(bean);
259: domProviderImpl.requestChange(markupDesignBean);
260: }
261: }
262:
263: private/*public*/void beanChanged() {
264: domProviderImpl.beanChanged(markupDesignBean);
265: }
266:
267: private/*public*/void requestChange() {
268: domProviderImpl.requestChange(markupDesignBean);
269: }
270:
271: public void clearPrerendered() {
272: domProviderImpl.setPrerenderedBean(null, null);
273: }
274:
275: private/*public*/boolean setPrerendered(DocumentFragment fragment) {
276: return domProviderImpl.setPrerenderedBean(markupDesignBean,
277: fragment);
278: }
279:
280: private/*public*/void setStyleParent(DocumentFragment fragment) {
281: NodeList children = fragment.getChildNodes();
282:
283: for (int i = 0; i < children.getLength(); i++) {
284: Node child = children.item(i);
285:
286: if (child.getNodeType() == Node.ELEMENT_NODE) {
287: // RaveElement e = (RaveElement)child;
288: Element e = (Element) child;
289: // CssLookup.getCssEngine(e).clearComputedStyles(e, null);
290: // CssProvider.getEngineService().clearComputedStylesForElement(e);
291: Element beanElement = markupDesignBean.getElement();
292: // XXX #6489063 Inherit the style from the original element.
293: // Maybe there should be just the size of the font inherited.
294: CssProvider.getEngineService()
295: .setStyleParentForElement(e, beanElement);
296:
297: // e = e.getRendered();
298: e = MarkupService.getRenderedElementForElement(e);
299: Element beanRenderedElement = MarkupService
300: .getRenderedElementForElement(beanElement);
301: if (e != null && beanRenderedElement != null) {
302: // CssLookup.getCssEngine(e).clearComputedStyles(e, null);
303: // CssProvider.getEngineService().clearComputedStylesForElement(e);
304: // XXX #6489063 Inherit the style from the original element.
305: // Maybe there should be just the size of the font inherited.
306: CssProvider.getEngineService()
307: .setStyleParentForElement(e,
308: beanRenderedElement);
309: }
310: }
311: }
312: }
313:
314: private/*public*/DocumentFragment renderDomFragment() {
315: DocumentFragment fragment = domProviderImpl
316: .renderHtmlForMarkupDesignBean(markupDesignBean);
317: // XXX To get it into source document so it can work (Positions work only against source doc!).
318: // TODO Change the positions to work over the rendered document, and also attach this fragment to the rendered doc.
319: fragment = (DocumentFragment) domProviderImpl.getJsfForm()
320: .getJspDom().importNode(fragment, true);
321:
322: // XXX Moved from designer/../AttributeInlineEditor
323: jsfForm.updateErrorsInComponent();
324:
325: return fragment;
326: }
327:
328: public boolean prepareAttributeInlineEditor(boolean selectText) {
329: // facesPageUnit = FacesSupport.getFacesUnit(webform.getModel().getLiveUnit());
330: // facesPageUnit = getFacesUnit(webform.getModel().getLiveUnit());
331: //
332: // if (facesPageUnit == null) {
333: // return;
334: // }
335:
336: // fragment = webform.getDomSynchronizer().createSourceFragment(bean);
337: // fragment = webform.createSourceFragment(bean);
338: // fragment = inlineEditorSupport.createSourceFragment();
339: fragment = createSourceFragment();
340:
341: if (fragment == null) {
342: // return;
343: return selectText;
344: }
345:
346: // facesPageUnit.setPreRendered(bean, fragment);
347: // if (!webform.setPrerenderedBean(bean, fragment)) {
348: // if (!inlineEditorSupport.setPrerendered(fragment)) {
349: // return;
350: if (!setPrerendered(fragment)) {
351: return selectText;
352: }
353:
354: // // Select the text in the component and set the
355: // // caret to the end
356: // DesignerPane pane = webform.getPane();
357: //
358: //// if (pane.getCaret() == null) {
359: //// DesignerCaret dc = pane.getPaneUI().createCaret();
360: //// webform.getPane().setCaret(dc);
361: //// }
362: // if (!pane.hasCaret()) {
363: // pane.createCaret();
364: // }
365:
366: Node n = null;
367:
368: if (xpath != null) {
369: // n = findPropertyNode(fragment, xpath);
370: n = JsfSupportUtilities.findPropertyNode(fragment, xpath);
371:
372: // if ((n == null) && (property.getValueSource() == null)) {
373: // if ((n == null) && (inlineEditorSupport.getValueSource() == null)) {
374: if ((n == null) && (getValueSource() == null)) {
375: // It's possible that we're editing a property that hasn't
376: // resulted in any markup in the component yet if it's not
377: // set. For example, an attempt to edit the "label" property
378: // of a text field. This should edit xpath "//span", but there's
379: // no <span> rendered until label is set to something. So,
380: // if this is the case, set the property temporarily, render
381: // the fragment, and unset it.
382: // String oldPropertyValue = (String)property.getValue();
383: // String oldPropertyValue = inlineEditorSupport.getValue();
384: String oldPropertyValue = getValue();
385:
386: // final String MARKER = property.getPropertyDescriptor().getDisplayName();
387: // final String MARKER = inlineEditorSupport.getDisplayName();
388: final String MARKER = getDisplayName();
389:
390: // try {
391: // Method m = property.getPropertyDescriptor().getWriteMethod();
392: // m.invoke(bean.getInstance(), new Object[] { MARKER });
393: // inlineEditorSupport.setViaWriteMethod(MARKER);
394: setViaWriteMethod(MARKER);
395: // } catch (Exception ex) {
396: // ErrorManager.getDefault().notify(ex);
397: // }
398:
399: try {
400: // facesPageUnit.setPreRendered(null, null);
401: // webform.setPrerenderedBean(null, null);
402: // inlineEditorSupport.clearPrerendered();
403: clearPrerendered();
404:
405: // XXX TODO There is not needed webform here.
406: // FileObject markupFile = webform.getModel().getMarkupFile();
407: // fragment = FacesSupport.renderHtml(markupFile, bean, false);
408: // fragment = InSyncService.getProvider().renderHtml(markupFile, bean);
409: // fragment = webform.renderHtmlForMarkupDesignBean(bean);
410: // fragment = inlineEditorSupport.renderDomFragment();
411: fragment = renderDomFragment();
412: // XXX Moved into the impl (of the above method).
413: // // XXX To get it into source document so it can work (Positions work only against source doc!).
414: // fragment = (DocumentFragment)webform.getJspDom().importNode(fragment, true);
415:
416: // // XXX FIXME Is this correct here?
417: // XXX Moving into designer/jsf/../InlineEditorSupport
418: // webform.updateErrorsInComponent();
419:
420: // facesPageUnit.setPreRendered(bean, fragment);
421: // webform.setPrerenderedBean(bean, fragment);
422: // inlineEditorSupport.setPrerendered(fragment);
423: setPrerendered(fragment);
424:
425: // n = findPropertyNode(fragment, xpath);
426: // n = WebForm.getDomProviderService().findPropertyNode(fragment, xpath);
427: n = JsfSupportUtilities.findPropertyNode(fragment,
428: xpath);
429: selectText = true;
430: // webform.getDomSynchronizer().requestChange(bean);
431: // webform.requestChange(bean);
432: // inlineEditorSupport.requestChange();
433: requestChange();
434: } finally {
435: // try {
436: // Method m = property.getPropertyDescriptor().getWriteMethod();
437: // m.invoke(bean.getInstance(), new Object[] { oldPropertyValue });
438: // inlineEditorSupport.setViaWriteMethod(oldPropertyValue);
439: setViaWriteMethod(oldPropertyValue);
440: // } catch (Exception ex) {
441: // ErrorManager.getDefault().notify(ex);
442: // }
443: }
444: }
445: }
446:
447: if (n != null) {
448: // See if I can find a text node inside it
449: text = findTextNode(n);
450:
451: if (text == null) {
452: text = n;
453: }
454: } else {
455: text = findTextNode(fragment);
456: }
457:
458: if (text != null) {
459: // Put a line break at the end of the text to ensure that we have a possible caret position
460: // if the user erases all text
461: //// XXX unfortunately this leads to comparisons failing in LineBoxGroup.paintBackground
462: // so the selection winds up not getting painted. I've gotta investigate this.
463: //if (isEscaped()) {
464: // br = text.getParentNode().appendChild(text.getOwnerDocument().createElement("br"));
465: //}
466: if (text.getNodeType() == Node.TEXT_NODE) {
467: // begin = new Position(text, 0, Bias.FORWARD);
468: // begin = Position.create(text, 0, Bias.FORWARD);
469: // begin = webform.createDomPosition(text, 0, Bias.FORWARD);
470: begin = jsfForm.getDomDocumentImpl().createDomPosition(
471: text, 0, Bias.FORWARD);
472:
473: // if (br != null) {
474: // end = Position.create(br, false);
475: // } else {
476: // end = new Position(text, text.getNodeValue().length(), Bias.BACKWARD);
477: // end = Position.create(text, text.getNodeValue().length(), Bias.BACKWARD);
478: // end = webform.createDomPosition(text, text.getNodeValue().length(), Bias.BACKWARD);
479: end = jsfForm.getDomDocumentImpl().createDomPosition(
480: text, text.getNodeValue().length(),
481: Bias.BACKWARD);
482: // }
483: } else {
484: NodeList children = text.getChildNodes();
485:
486: if (children.getLength() > 0) {
487: // begin = new Position(children.item(0), 0, Bias.FORWARD);
488: // begin = Position.create(children.item(0), 0, Bias.FORWARD);
489: // begin = webform.createDomPosition(children.item(0), 0, Bias.FORWARD);
490: begin = jsfForm.getDomDocumentImpl()
491: .createDomPosition(children.item(0), 0,
492: Bias.FORWARD);
493:
494: // if (br != null) {
495: // end = Position.create(br, false);
496: // } else {
497: Node last = children.item(children.getLength() - 1);
498:
499: if (last.getNodeType() == Node.TEXT_NODE) {
500: // end = new Position(last, last.getNodeValue().length(), Bias.BACKWARD);
501: // end = Position.create(last, last.getNodeValue().length(), Bias.BACKWARD);
502: // end = webform.createDomPosition(last, last.getNodeValue().length(), Bias.BACKWARD);
503: end = jsfForm.getDomDocumentImpl()
504: .createDomPosition(last,
505: last.getNodeValue().length(),
506: Bias.BACKWARD);
507: } else {
508: // end = new Position(last, last.getChildNodes().getLength(), Bias.BACKWARD);
509: // end = Position.create(last, last.getChildNodes().getLength(), Bias.BACKWARD);
510: // end = webform.createDomPosition(last, last.getChildNodes().getLength(), Bias.BACKWARD);
511: end = jsfForm.getDomDocumentImpl()
512: .createDomPosition(
513: last,
514: last.getChildNodes()
515: .getLength(),
516: Bias.BACKWARD);
517: }
518: // }
519: } else {
520: // begin = new Position(text, 0, Bias.FORWARD);
521: // begin = Position.create(text, 0, Bias.FORWARD);
522: // begin = webform.createDomPosition(text, 0, Bias.FORWARD);
523: begin = jsfForm.getDomDocumentImpl()
524: .createDomPosition(text, 0, Bias.FORWARD);
525:
526: // if (br != null) {
527: // end = Position.create(br, false);
528: // } else {
529: // end = new Position(text, text.getChildNodes().getLength(), Bias.BACKWARD);
530: // end = Position.create(text, text.getChildNodes().getLength(), Bias.BACKWARD);
531: // end = webform.createDomPosition(text, text.getChildNodes().getLength(), Bias.BACKWARD);
532: end = jsfForm.getDomDocumentImpl()
533: .createDomPosition(text,
534: text.getChildNodes().getLength(),
535: Bias.BACKWARD);
536: // }
537: }
538: }
539: } else {
540: // begin = new Position(fragment, 0, Bias.FORWARD);
541: // end = new Position(fragment, fragment.getChildNodes().getLength(), Bias.FORWARD);
542: // begin = Position.create(fragment, 0, Bias.FORWARD);
543: // end = Position.create(fragment, fragment.getChildNodes().getLength(), Bias.FORWARD);
544: // begin = webform.createDomPosition(fragment, 0, Bias.FORWARD);
545: // end = webform.createDomPosition(fragment, fragment.getChildNodes().getLength(), Bias.FORWARD);
546: begin = jsfForm.getDomDocumentImpl().createDomPosition(
547: fragment, 0, Bias.FORWARD);
548: end = jsfForm.getDomDocumentImpl().createDomPosition(
549: fragment, fragment.getChildNodes().getLength(),
550: Bias.FORWARD);
551: }
552: // XXX It seems to be redundant here.
553: // setStyleParent(fragment);
554:
555: registerDomListeners();
556:
557: return selectText;
558: }
559:
560: // XXX Moved from designer/../AttributeInlineEditor.
561: /** Return the text node containing the value attribute.
562: * This is simplified. Later, what if you have an output text
563: * with this value: "<b>Hello World</b>". I won't find that; I need
564: * to search for the <b> node etc.
565: */
566: private Node findTextNode(Node root) {
567: assert root != null;
568:
569: // // String assumption should be checked in beandescriptor search for TEXT_NODE_PROPERTY,
570: // // especially if we publish this property. Or we could at least specify that the
571: // // property MUST be a String.
572: // String value = (String)property.getValue();
573: // String value = inlineEditorSupport.getValue();
574: String value = getValue();
575:
576: // Search for the special node which contains the text.
577: // Special case empty... look for span with a CSS class of
578: // "rave-uninitialized-text".
579: if ((value == null) || (value.length() == 0)) {
580: // User has not set text; renderer may have done something
581: // like insert "Text" with a special CSS style so look for
582: // the element with that style and take its children to
583: // be the content to be edited.
584: // First see if we can find an eligible text node
585: Node text = findFirstTextNode(root);
586:
587: if (text == null) {
588: text = findDesignStyleClass(root);
589: }
590:
591: return text;
592: } else {
593: return findText(root, value);
594: }
595: }
596:
597: private Node findFirstTextNode(Node node) {
598: if (node.getNodeType() == Node.TEXT_NODE) {
599: String s = node.getNodeValue();
600:
601: // if (!DesignerUtils.onlyWhitespace(s)) {
602: if (!JsfSupportUtilities.onlyWhitespace(s)) {
603: return node;
604: }
605: }
606:
607: NodeList nl = node.getChildNodes();
608:
609: for (int i = 0, n = nl.getLength(); i < n; i++) {
610: Node child = nl.item(i);
611:
612: // Don't pick text children in JavaScript or CSS subtrees
613: if ((child.getNodeType() == Node.ELEMENT_NODE)
614: && (node.getNodeName().equals(HtmlTag.SCRIPT.name) || node
615: .getNodeName().equals(HtmlTag.STYLE.name))) {
616: continue;
617: }
618:
619: Node match = findFirstTextNode(child);
620:
621: if (match != null) {
622: return match;
623: }
624: }
625:
626: return null;
627: }
628:
629: private Node findDesignStyleClass(Node node) {
630: if (node.getNodeType() == Node.ELEMENT_NODE) {
631: Element element = (Element) node;
632:
633: if (element.getAttribute(HtmlAttribute.CLASS).indexOf(
634: "rave-uninitialized-text") != -1) { // NOI18N
635: return node;
636: }
637: }
638:
639: NodeList nl = node.getChildNodes();
640:
641: for (int i = 0, n = nl.getLength(); i < n; i++) {
642: Node match = findDesignStyleClass(nl.item(i));
643:
644: if (match != null) {
645: return match;
646: }
647: }
648:
649: return null;
650: }
651:
652: private Node findText(Node node, String value) {
653: //XXX rewrite this!!!
654: if (node.getNodeType() == Node.TEXT_NODE) {
655: String nodeText = node.getNodeValue();
656: int index = nodeText.indexOf(value);
657:
658: if ((index != -1) && (value.length() > 0)) {
659: // Possibly split the text node node, in case it contains
660: // "additional junk". For example, the table header will
661: // render a row count into this text node too, which is not
662: // part of the property value, so we don't want it included
663: // in our text editing efforts.
664: org.w3c.dom.Text text = (org.w3c.dom.Text) node;
665:
666: if (text.getLength() != value.length()) {
667: // Strip text before the value text
668: if (index != 0) {
669: node = text.splitText(index);
670: text = (org.w3c.dom.Text) node;
671: }
672:
673: // Strip off text after the value text
674: if (text.getLength() > value.length()) {
675: text.splitText(value.length());
676: }
677: }
678:
679: return node;
680: }
681: }
682:
683: NodeList nl = node.getChildNodes();
684:
685: for (int i = 0, n = nl.getLength(); i < n; i++) {
686: Node match = findText(nl.item(i), value);
687:
688: if (match != null) {
689: return match;
690: }
691: }
692:
693: return null;
694: }
695:
696: public DomPosition getBeginPosition() {
697: return begin;
698: }
699:
700: public DomPosition getEndPosition() {
701: return end;
702: }
703:
704: public Node findXPathNodeForComponentRootElement(
705: Element componentRootElement) {
706: if (xpath != null) {
707: // RaveElement sourceElement = (RaveElement)bean.getElement();
708: // RaveElement root = sourceElement.getRendered();
709: // Element sourceElement = bean.getElement();
710: // Element root = MarkupService.getRenderedElementForElement(sourceElement);
711:
712: Element root = componentRootElement;
713: if (root != null) {
714: // Node node = findPropertyNode(root, xpath);
715: // return WebForm.getDomProviderService().findPropertyNode(root, xpath);
716: return JsfSupportUtilities
717: .findPropertyNode(root, xpath);
718: }
719: }
720: return null;
721: }
722:
723: public DocumentFragment getFragment() {
724: return fragment;
725: }
726:
727: public Node getText() {
728: return text;
729: }
730:
731: // public void setEndPosition(DomPosition endPosition) {
732: // end = endPosition;
733: // }
734:
735: // XXX Moved from designer/../AttributeInlineEditor
736: private void registerDomListeners() {
737: // DocumentFragment fragment = inlineEditorSupport.getFragment();
738: if (fragment == null) {
739: return;
740: }
741:
742: EventTarget target = (EventTarget) fragment;
743: EventListener adapter = this ;
744: target.addEventListener(MutationEventImpl.DOM_ATTR_MODIFIED,
745: adapter, false);
746:
747: /* This event seems to be redundant.
748: target.addEventListener(MutationEventImpl.DOM_SUBTREE_MODIFIED, adapter, false);
749: */
750: target.addEventListener(MutationEventImpl.DOM_NODE_INSERTED,
751: adapter, false);
752: target.addEventListener(
753: MutationEventImpl.DOM_NODE_INSERTED_INTO_DOCUMENT,
754: adapter, false);
755: target.addEventListener(MutationEventImpl.DOM_NODE_REMOVED,
756: adapter, false);
757: target.addEventListener(
758: MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT,
759: adapter, false);
760: target.addEventListener(
761: MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED, adapter,
762: false);
763:
764: // target.addEventListener(InSyncService.DOM_DOCUMENT_REPLACED, adapter, false);
765: // target.addEventListener(WebForm.getDomProviderService().getDomDocumentReplacedEventConstant(), adapter, false);
766: target.addEventListener(MarkupUnit.DOM_DOCUMENT_REPLACED,
767: adapter, false);
768: }
769:
770: // Moved from designer/../AttributeInlineEditor
771: private void unregisterDomListeners() {
772: // DocumentFragment fragment = inlineEditorSupport.getFragment();
773: if (fragment == null) {
774: return;
775: }
776:
777: EventTarget target = (EventTarget) fragment;
778: EventListener adapter = this ;
779: target.removeEventListener(MutationEventImpl.DOM_ATTR_MODIFIED,
780: adapter, false);
781:
782: /* This event seems to be redundant.
783: target.removeEventListener(MutationEventImpl.DOM_SUBTREE_MODIFIED, adapter, false);
784: */
785: target.removeEventListener(MutationEventImpl.DOM_NODE_INSERTED,
786: adapter, false);
787: target.removeEventListener(
788: MutationEventImpl.DOM_NODE_INSERTED_INTO_DOCUMENT,
789: adapter, false);
790: target.removeEventListener(MutationEventImpl.DOM_NODE_REMOVED,
791: adapter, false);
792: target.removeEventListener(
793: MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT,
794: adapter, false);
795: target.removeEventListener(
796: MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED, adapter,
797: false);
798:
799: // target.removeEventListener(InSyncService.DOM_DOCUMENT_REPLACED, adapter, false);
800: // target.removeEventListener(WebForm.getDomProviderService().getDomDocumentReplacedEventConstant(), adapter, false);
801: target.removeEventListener(MarkupUnit.DOM_DOCUMENT_REPLACED,
802: adapter, false);
803: }
804:
805: // XXX Moved from designer/../AttributeInlineEditor#finish
806: public void cleanAttributeInlineEditor(boolean cancel) {
807: // if (facesPageUnit != null) {
808: // facesPageUnit.setPreRendered(null, null);
809: // }
810: // webform.setPrerenderedBean(null, null);
811: // inlineEditorSupport.clearPrerendered();
812: clearPrerendered();
813:
814: // DocumentFragment fragment = inlineEditorSupport.getFragment();
815: if (fragment != null) {
816: unregisterDomListeners();
817: }
818:
819: // // XXX #6431953.
820: // webform.getPane().removeFocusListener(focusListener);
821: //
822: // // XXX This logic should be moved somewhere else.
823: // // XXX in flow mode, gotta set it to some other valid position
824: // webform.getPane().setCaret(null);
825:
826: if (cancel) {
827: // Update component rendering the rendered html may contain the
828: // rendered value
829: // webform.getDomSynchronizer().beanChanged(bean);
830: // webform.beanChanged(bean);
831: // inlineEditorSupport.beanChanged();
832: beanChanged();
833:
834: return; // don't apply value
835: }
836:
837: // Don't apply changes if we haven't made any edits; that way,
838: // we won't change a null value (rendered into "Text") into "Text"
839: // which really has a "null" value
840: if (!hasBeenEdited) {
841: // Ensure that we re-render the component anyway
842: // such that the rendered-references are correct again
843: // webform.getDomSynchronizer().requestChange(bean);
844: // webform.requestChange(bean);
845: // inlineEditorSupport.requestChange();
846: requestChange();
847:
848: return;
849: }
850:
851: // Determine what part of the fragment should be part of the
852: // value text!
853: // XXX for now use the whole enchilada!!
854: String value = null;
855: // Node text = inlineEditorSupport.getText();
856: Node text = getText();
857: Node origin = text;
858:
859: if (origin == null) {
860: origin = fragment;
861:
862: if (origin == null) {
863: return;
864: }
865: } else {
866: if (origin.getNodeType() == Node.TEXT_NODE) {
867: //origin = origin.getParentNode();
868: value = text.getNodeValue();
869: }
870: }
871:
872: if (value == null) {
873: // Get rid of our temporary newline
874: // if (br != null) {
875: // br.getParentNode().removeChild(br);
876: // }
877:
878: // Commit value
879: StringBuffer sb = new StringBuffer(300);
880: NodeList children = origin.getChildNodes();
881:
882: for (int i = 0, n = children.getLength(); i < n; i++) {
883: Node child = children.item(i);
884: // sb.append(InSyncService.getProvider().getHtmlStream(child));
885: // sb.append(WebForm.getDomProviderService().getHtmlStream(child));
886: sb.append(Util.getHtmlStream(child));
887: }
888:
889: value = sb.toString();
890: }
891:
892: if (isEscaped()) {
893: // value =
894: // // <markup_separation>
895: //// MarkupServiceProvider.getDefault().expandHtmlEntities(value, false,
896: //// bean.getElement());
897: // // ====
898: //// InSyncService.getProvider().expandHtmlEntities(value, false, bean.getElement());
899: // WebForm.getDomProviderService().expandHtmlEntities(value, false, bean.getElement());
900: // // </markup_separation>\
901: // value = inlineEditorSupport.expandHtmlEntities(value, false);
902: value = expandHtmlEntities(value, false);
903: }
904:
905: if ((value != null) && (value.length() == 0)) {
906: // property.unset();
907: // inlineEditorSupport.unset();
908: unset();
909: } else {
910: // property.setValue(value);
911: // inlineEditorSupport.setValue(value);
912: setValue(value);
913: }
914: }
915:
916: }
|