OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Cloud Input Tools Authors. All Rights Reserved. |
| 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with the License. |
| 5 // You may obtain a copy of the License at |
| 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS-IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 // See the License for the specific language governing permissions and |
| 13 // limitations under the License. |
| 14 |
| 15 /** |
| 16 * @fileoverview Provides common operation of dom for input tools. |
| 17 */ |
| 18 |
| 19 |
| 20 goog.provide('i18n.input.common.dom'); |
| 21 |
| 22 goog.require('goog.array'); |
| 23 goog.require('goog.dom'); |
| 24 goog.require('goog.dom.TagName'); |
| 25 goog.require('goog.dom.classlist'); |
| 26 goog.require('goog.style'); |
| 27 goog.require('goog.uri.utils'); |
| 28 goog.require('i18n.input.common.GlobalSettings'); |
| 29 |
| 30 |
| 31 /** |
| 32 * When detects whether the same domain iframe, browser will throw |
| 33 * exceptions on accessing the cross domain iframe. Stores result to avoid to |
| 34 * throws exception twice. |
| 35 * Key is document uid, value is object. ifrmae uid : true/false |
| 36 * |
| 37 * @type {!Object.<number, !Object.<number, boolean>>} |
| 38 * @private |
| 39 */ |
| 40 i18n.input.common.dom.sameDomainIframes_ = {}; |
| 41 |
| 42 |
| 43 /** |
| 44 * Checks the given element whether is editable. |
| 45 * |
| 46 * @param {!Element} element The element. |
| 47 * @return {boolean} Whether the give element is editable. |
| 48 */ |
| 49 i18n.input.common.dom.isEditable = function(element) { |
| 50 if (!element.tagName) { |
| 51 return false; |
| 52 } |
| 53 |
| 54 if (element.readOnly) { |
| 55 return false; |
| 56 } |
| 57 |
| 58 switch (element.tagName.toUpperCase()) { |
| 59 case 'TEXTAREA': |
| 60 return true; |
| 61 case 'INPUT': |
| 62 return (element.type.toUpperCase() == 'TEXT' || |
| 63 element.type.toUpperCase() == 'SEARCH'); |
| 64 case 'DIV': |
| 65 return element.isContentEditable; |
| 66 case 'IFRAME': |
| 67 // Accessing iframe's contents or properties throws exception when the |
| 68 // iframe is not hosted on the same domain. |
| 69 // When it happens, ignore it and consider this iframe isn't editable. |
| 70 /** @preserveTry */ |
| 71 try { |
| 72 var ifdoc = i18n.input.common.dom.getSameDomainFrameDoc(element); |
| 73 return !!ifdoc && (ifdoc.designMode && |
| 74 ifdoc.designMode.toUpperCase() == 'ON' || |
| 75 ifdoc.body && ifdoc.body.isContentEditable); |
| 76 } catch (e) { |
| 77 return false; |
| 78 } |
| 79 } |
| 80 |
| 81 return false; |
| 82 }; |
| 83 |
| 84 |
| 85 /** |
| 86 * Sets class names to an element. |
| 87 * |
| 88 * @param {Element} elem Element to set class names. |
| 89 * @param {Array.<string>} classes Class names. |
| 90 */ |
| 91 i18n.input.common.dom.setClasses = function(elem, classes) { |
| 92 if (elem) { |
| 93 for (var i = 0; i < classes.length; i++) { |
| 94 if (i == 0) { |
| 95 goog.dom.classlist.set(elem, classes[0]); |
| 96 } else { |
| 97 goog.dom.classlist.add(elem, classes[i]); |
| 98 } |
| 99 } |
| 100 } |
| 101 }; |
| 102 |
| 103 |
| 104 /** |
| 105 * Check the iframe whether is the same domain as the current domain. |
| 106 * Returns the iframe content document when it's the same domain, |
| 107 * otherwise return null. |
| 108 * |
| 109 * @param {!Element} element The iframe element. |
| 110 * @return {Document} The iframe content document. |
| 111 */ |
| 112 i18n.input.common.dom.getSameDomainFrameDoc = function(element) { |
| 113 var uid = goog.getUid(document); |
| 114 var frameUid = goog.getUid(element); |
| 115 var states = i18n.input.common.dom.sameDomainIframes_[uid]; |
| 116 if (!states) { |
| 117 states = i18n.input.common.dom.sameDomainIframes_[uid] = {}; |
| 118 } |
| 119 /** @preserveTry */ |
| 120 try { |
| 121 var url = window.location.href || ''; |
| 122 //Note: cross-domain IFRAME's src can be: |
| 123 // http://www... |
| 124 // https://www.... |
| 125 // //www. |
| 126 // Non-cross-domain IFRAME's src can be: |
| 127 // javascript:... |
| 128 // javascript://... |
| 129 // abc:... |
| 130 // abc://... |
| 131 // abc//... |
| 132 // path/index.html |
| 133 if (!(frameUid in states)) { |
| 134 if (element.src) { |
| 135 var pos = element.src.indexOf('//'); |
| 136 var protocol = pos < 0 ? 'N/A' : element.src.slice(0, pos); |
| 137 states[frameUid] = (protocol != '' && |
| 138 protocol != 'http:' && |
| 139 protocol != 'https:' || |
| 140 goog.uri.utils.haveSameDomain(element.src, url)); |
| 141 } else { |
| 142 states[frameUid] = true; |
| 143 } |
| 144 } |
| 145 return states[frameUid] ? goog.dom.getFrameContentDocument(element) : null; |
| 146 } catch (e) { |
| 147 states[frameUid] = false; |
| 148 return null; |
| 149 } |
| 150 }; |
| 151 |
| 152 |
| 153 /** |
| 154 * Gets the same domain iframe or frame document in given document, default |
| 155 * given document is current document. |
| 156 * |
| 157 * @param {Document=} opt_doc The given document. |
| 158 * @return {Array.<!Document>} The same domain iframe document. |
| 159 */ |
| 160 i18n.input.common.dom.getSameDomainDocuments = function(opt_doc) { |
| 161 var doc = opt_doc || document; |
| 162 var iframes = []; |
| 163 var rets = []; |
| 164 goog.array.extend(iframes, |
| 165 doc.getElementsByTagName(goog.dom.TagName.IFRAME), |
| 166 doc.getElementsByTagName(goog.dom.TagName.FRAME)); |
| 167 goog.array.forEach(iframes, function(frame) { |
| 168 var frameDoc = i18n.input.common.dom.getSameDomainFrameDoc(frame); |
| 169 frameDoc && rets.push(frameDoc); |
| 170 }); |
| 171 return rets; |
| 172 }; |
| 173 |
| 174 |
| 175 /** |
| 176 * Create the iframe in given document or default document. Then the input tool |
| 177 * UI element will be create inside the iframe document to avoid CSS conflict. |
| 178 * |
| 179 * @param {Document=} opt_doc The given document. |
| 180 * @return {!Element} The iframe element. |
| 181 */ |
| 182 i18n.input.common.dom.createIframeWrapper = function(opt_doc) { |
| 183 var doc = opt_doc || document; |
| 184 var dom = goog.dom.getDomHelper(); |
| 185 var frame = dom.createDom(goog.dom.TagName.IFRAME, { |
| 186 'frameborder': '0', |
| 187 'scrolling': 'no', |
| 188 'style': 'background-color:transparent;border:0;display:none;' |
| 189 }); |
| 190 dom.append(/** @type {!Element} */ (doc.body), frame); |
| 191 var frameDoc = dom.getFrameContentDocument(frame); |
| 192 |
| 193 var css = i18n.input.common.GlobalSettings.alternativeImageUrl ? |
| 194 i18n.input.common.GlobalSettings.css.replace( |
| 195 /\/\/ssl.gstatic.com\/inputtools\/images/g, |
| 196 i18n.input.common.GlobalSettings.alternativeImageUrl) : |
| 197 i18n.input.common.GlobalSettings.css; |
| 198 goog.style.installStyles( |
| 199 'html body{border:0;margin:0;padding:0} html,body{overflow:hidden}' + |
| 200 css, /** @type {!Element} */(frameDoc.body)); |
| 201 return frame; |
| 202 }; |
| 203 |
| 204 |
| 205 /** |
| 206 * The property need to be copied from original element to its iframe wrapper. |
| 207 * |
| 208 * @type {!Array.<string>} |
| 209 * @private |
| 210 */ |
| 211 i18n.input.common.dom.iframeWrapperProperty_ = ['box-shadow', 'z-index', |
| 212 'margin', 'position', 'display']; |
| 213 |
| 214 |
| 215 /** |
| 216 * Copies the necessary properties value from original element to its iframe |
| 217 * wrapper element. |
| 218 * |
| 219 * @param {Element} element . |
| 220 * @param {Element} iframe The iframe wrapper element. |
| 221 */ |
| 222 i18n.input.common.dom.copyNecessaryStyle = function(element, iframe) { |
| 223 goog.style.setContentBoxSize(iframe, goog.style.getSize(element)); |
| 224 goog.array.forEach(i18n.input.common.dom.iframeWrapperProperty_, |
| 225 function(property) { |
| 226 goog.style.setStyle(iframe, property, |
| 227 goog.style.getComputedStyle(element, property)); |
| 228 }); |
| 229 }; |
OLD | NEW |