1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2007, 2009, 2010, 2013, 2014, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2007, 2009, 2010, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 // 25 // Base class 26 // 27 28 AjxSerializer = function() { 29 if (arguments.length == 0) return; 30 } 31 32 // Public methods 33 34 AjxSerializer.prototype.serialize = function(node) { throw "NOT IMPLEMENTED"; }; 35 36 // 37 // Classes 38 // 39 40 /** 41 * This class attempts to serialize a DOM document in the same format 42 * as the Zimbra Server. 43 * @class 44 * @constructor 45 * @private 46 */ 47 AjxJsonSerializer = function(minimize) { 48 AjxSerializer.call(this, null); 49 this._minimize = Boolean(minimize); 50 } 51 AjxJsonSerializer.prototype = new AjxSerializer; 52 AjxJsonSerializer.prototype.constructor = AjxJsonSerializer; 53 54 // Constants 55 56 AjxJsonSerializer.E_ATTRS = "_attrs"; 57 AjxJsonSerializer.A_CONTENT = "_content"; 58 AjxJsonSerializer.A_NAMESPACE = "_jsns"; 59 60 // AjxSerializer methods 61 62 // TODO: handle namespaces 63 AjxJsonSerializer.prototype.serialize = function(node) { 64 // gather elements and content 65 var elems = {}; 66 var elemCount = 0; 67 var content = null; 68 for (var child = node.firstChild; child; child = child.nextSibling) { 69 var type = child.nodeType; 70 // elements 71 if (type == 1) { 72 var name = child.nodeName; 73 if (!elems[name]) { 74 elems[name] = []; 75 elemCount++; 76 } 77 elems[name].push(child); 78 continue; 79 } 80 // text and CDATA nodes 81 if (type == 3 || type == 4) { 82 if (!content) { 83 content = []; 84 } 85 content.push(child.nodeValue); 86 continue; 87 } 88 } 89 90 // gather attributes 91 var attrs = {}; 92 var attrCount = 0; 93 if (node.attributes) { 94 for (var i = 0; i < node.attributes.length; i++) { 95 var attr = node.attributes[i]; 96 var name = attr.nodeName; 97 var value = this.quote(attr.nodeValue); 98 if (elems[name]) { 99 attrs[name] = value; 100 attrCount++; 101 } 102 else { 103 elems[name] = value; 104 elemCount++; 105 } 106 } 107 } 108 109 // escape content 110 content = content && content.join(""); 111 if (content && content.match(/^\s*$/)) { 112 content = null; 113 } 114 if (content) { 115 content = this.quote(content); 116 117 // do we only have content? 118 if (attrCount == 0 && elemCount == 0) { 119 return content; 120 } 121 } 122 123 // is there anything to do? 124 if (content == null && elemCount == 0 && attrCount == 0) { 125 // return "null"; 126 return "{}"; 127 } 128 129 // serialize content 130 var a = [ "{" ]; 131 if (content) { 132 a.push(this.quote(AjxJsonSerializer.A_CONTENT), ":", content); 133 if (attrCount > 0 || elemCount > 0) { 134 a.push(","); 135 } 136 } 137 138 // serialize attributes 139 if (attrCount > 0) { 140 a.push(this.quote(AjxJsonSerializer.E_ATTRS), ": { "); 141 var i = 0; 142 for (var name in attrs) { 143 if (i > 0) { 144 a.push(","); 145 } 146 a.push(this.quote(name), ":", attrs[name]); 147 i++; 148 } 149 a.push("}"); 150 if (elemCount > 0) { 151 a.push(", "); 152 } 153 } 154 155 // serialize elements 156 var j = 0; 157 for (var name in elems) { 158 if (j > 0) { 159 a.push(","); 160 } 161 var elem = elems[name]; 162 if (typeof elem == "string") { 163 a.push(this.quote(name), ":", elem); 164 j++; 165 continue; 166 } 167 a.push(this.quote(name), ":"); 168 if (!this._minimize || elem.length > 1) { 169 a.push("["); 170 } 171 for (var i = 0; i < elem.length; i++) { 172 if (i > 0) { 173 a.push(","); 174 } 175 a.push(this.serialize(elem[i])); 176 } 177 if (!this._minimize || elem.length > 1) { 178 a.push("]"); 179 } 180 j++; 181 } 182 a.push("}"); 183 184 return a.join(""); 185 }; 186 187 // convenience methods 188 189 AjxJsonSerializer.prototype.quote = function(s) { 190 return [ '"', this.escape(s), '"' ].join(""); 191 }; 192 193 AjxJsonSerializer.prototype.escape = function(s) { 194 return s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n"); 195 };