1e484b35bSopenharmony_ci/* 2e484b35bSopenharmony_ci * Licensed to the Apache Software Foundation (ASF) under one 3e484b35bSopenharmony_ci * or more contributor license agreements. See the NOTICE file 4e484b35bSopenharmony_ci * distributed with this work for additional information 5e484b35bSopenharmony_ci * regarding copyright ownership. The ASF licenses this file 6e484b35bSopenharmony_ci * to you under the Apache License, Version 2.0 (the 7e484b35bSopenharmony_ci * "License"); you may not use this file except in compliance 8e484b35bSopenharmony_ci * with the License. You may obtain a copy of the License at 9e484b35bSopenharmony_ci * 10e484b35bSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 11e484b35bSopenharmony_ci * 12e484b35bSopenharmony_ci * Unless required by applicable law or agreed to in writing, 13e484b35bSopenharmony_ci * software distributed under the License is distributed on an 14e484b35bSopenharmony_ci * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15e484b35bSopenharmony_ci * KIND, either express or implied. See the License for the 16e484b35bSopenharmony_ci * specific language governing permissions and limitations 17e484b35bSopenharmony_ci * under the License. 18e484b35bSopenharmony_ci */ 19e484b35bSopenharmony_ci 20e484b35bSopenharmony_ci/** 21e484b35bSopenharmony_ci * @fileOverview Document & Element Helpers. 22e484b35bSopenharmony_ci * 23e484b35bSopenharmony_ci * required: 24e484b35bSopenharmony_ci * Document#: createElement, createComment, getRef 25e484b35bSopenharmony_ci * Element#: appendChild, insertBefore, removeChild, nextSibling 26e484b35bSopenharmony_ci */ 27e484b35bSopenharmony_ciimport Vm from './index'; 28e484b35bSopenharmony_ciimport Element from '../../vdom/Element'; 29e484b35bSopenharmony_ciimport Comment from '../../vdom/Comment'; 30e484b35bSopenharmony_ciimport Node from '../../vdom/Node'; 31e484b35bSopenharmony_ciimport { FragBlockInterface, isBlock } from './compiler'; 32e484b35bSopenharmony_ciimport { emitSubVmLife } from './pageLife'; 33e484b35bSopenharmony_ci 34e484b35bSopenharmony_ci/** 35e484b35bSopenharmony_ci * Create a body by type. 36e484b35bSopenharmony_ci * @param {Vm} vm - Vm object. 37e484b35bSopenharmony_ci * @param {string} type - Element type. 38e484b35bSopenharmony_ci * @return {Node} Body of Node by type. 39e484b35bSopenharmony_ci */ 40e484b35bSopenharmony_ciexport function createBody(vm: Vm, type: string): Node { 41e484b35bSopenharmony_ci const doc = vm._app.doc; 42e484b35bSopenharmony_ci return doc.createBody(type); 43e484b35bSopenharmony_ci} 44e484b35bSopenharmony_ci 45e484b35bSopenharmony_ci/** 46e484b35bSopenharmony_ci * Create an element by type 47e484b35bSopenharmony_ci * @param {Vm} vm - Vm object. 48e484b35bSopenharmony_ci * @param {string} type - Element type. 49e484b35bSopenharmony_ci * @return {Element} Element of Node by type. 50e484b35bSopenharmony_ci */ 51e484b35bSopenharmony_ciexport function createElement(vm: Vm, type: string): Element { 52e484b35bSopenharmony_ci const doc = vm._app.doc; 53e484b35bSopenharmony_ci return doc.createElement(type); 54e484b35bSopenharmony_ci} 55e484b35bSopenharmony_ci 56e484b35bSopenharmony_ci/** 57e484b35bSopenharmony_ci * Create and return a frag block for an element. 58e484b35bSopenharmony_ci * @param {Vm} vm - Vm object. 59e484b35bSopenharmony_ci * @param {Element} element - Element object. 60e484b35bSopenharmony_ci * @return {FragBlockInterface} New block. 61e484b35bSopenharmony_ci */ 62e484b35bSopenharmony_ciexport function createBlock(vm: Vm, element: Element | FragBlockInterface): FragBlockInterface { 63e484b35bSopenharmony_ci const start = createBlockStart(vm); 64e484b35bSopenharmony_ci const end = createBlockEnd(vm); 65e484b35bSopenharmony_ci const blockId = lastestBlockId++; 66e484b35bSopenharmony_ci const newBlock: FragBlockInterface = {start, end, blockId}; 67e484b35bSopenharmony_ci if (isBlock(element)) { 68e484b35bSopenharmony_ci let updateMark = element.updateMark; 69e484b35bSopenharmony_ci if (updateMark) { 70e484b35bSopenharmony_ci if (isBlock(updateMark)) { 71e484b35bSopenharmony_ci updateMark = updateMark.end; 72e484b35bSopenharmony_ci } 73e484b35bSopenharmony_ci element.element.insertAfter(end, updateMark); 74e484b35bSopenharmony_ci element.element.insertAfter(start, updateMark); 75e484b35bSopenharmony_ci element.updateMark = end; 76e484b35bSopenharmony_ci } else { 77e484b35bSopenharmony_ci element.element.insertBefore(start, element.end); 78e484b35bSopenharmony_ci element.element.insertBefore(end, element.end); 79e484b35bSopenharmony_ci } 80e484b35bSopenharmony_ci newBlock.element = element.element; 81e484b35bSopenharmony_ci } else { 82e484b35bSopenharmony_ci element.appendChild(start); 83e484b35bSopenharmony_ci element.appendChild(end); 84e484b35bSopenharmony_ci newBlock.element = element; 85e484b35bSopenharmony_ci element.block = newBlock; 86e484b35bSopenharmony_ci } 87e484b35bSopenharmony_ci return newBlock; 88e484b35bSopenharmony_ci} 89e484b35bSopenharmony_ci 90e484b35bSopenharmony_cilet lastestBlockId = 1; 91e484b35bSopenharmony_ci 92e484b35bSopenharmony_ci/** 93e484b35bSopenharmony_ci * Create and return a block starter. 94e484b35bSopenharmony_ci * @param {Vm} vm - Vm object. 95e484b35bSopenharmony_ci * @return {Comment} A block starter. 96e484b35bSopenharmony_ci */ 97e484b35bSopenharmony_cifunction createBlockStart(vm: Vm): Comment { 98e484b35bSopenharmony_ci const doc = vm._app.doc; 99e484b35bSopenharmony_ci const anchor = doc.createComment('start'); 100e484b35bSopenharmony_ci return anchor; 101e484b35bSopenharmony_ci} 102e484b35bSopenharmony_ci 103e484b35bSopenharmony_ci/** 104e484b35bSopenharmony_ci * Create and return a block ender. 105e484b35bSopenharmony_ci * @param {Vm} vm - Vm object. 106e484b35bSopenharmony_ci * @return {Comment} A block starter. 107e484b35bSopenharmony_ci */ 108e484b35bSopenharmony_cifunction createBlockEnd(vm: Vm): Comment { 109e484b35bSopenharmony_ci const doc = vm._app.doc; 110e484b35bSopenharmony_ci const anchor = doc.createComment('end'); 111e484b35bSopenharmony_ci anchor.destroyHook = function() { 112e484b35bSopenharmony_ci if (anchor.watchers !== undefined) { 113e484b35bSopenharmony_ci anchor.watchers.forEach(function(watcher) { 114e484b35bSopenharmony_ci watcher.teardown(); 115e484b35bSopenharmony_ci }); 116e484b35bSopenharmony_ci anchor.watchers = []; 117e484b35bSopenharmony_ci } 118e484b35bSopenharmony_ci }; 119e484b35bSopenharmony_ci return anchor; 120e484b35bSopenharmony_ci} 121e484b35bSopenharmony_ci 122e484b35bSopenharmony_ci/** 123e484b35bSopenharmony_ci * Attach target to a certain dest using appendChild by default. 124e484b35bSopenharmony_ci * @param {Element} target - If the dest is a frag block then insert before the ender. 125e484b35bSopenharmony_ci * @param {FragBlockInterface | Element} dest - A certain dest. 126e484b35bSopenharmony_ci * @return {*} 127e484b35bSopenharmony_ci */ 128e484b35bSopenharmony_ciexport function attachTarget(target: Element, dest: FragBlockInterface | Element): any { 129e484b35bSopenharmony_ci if (isBlock(dest)) { 130e484b35bSopenharmony_ci const before = dest.end; 131e484b35bSopenharmony_ci const after = dest.updateMark; 132e484b35bSopenharmony_ci if (dest.children) { 133e484b35bSopenharmony_ci dest.children.push(target); 134e484b35bSopenharmony_ci } 135e484b35bSopenharmony_ci if (after) { 136e484b35bSopenharmony_ci const signal = moveTarget(target, after); 137e484b35bSopenharmony_ci if (isBlock(target)) { 138e484b35bSopenharmony_ci dest.updateMark = target.end; 139e484b35bSopenharmony_ci } else { 140e484b35bSopenharmony_ci dest.updateMark = target; 141e484b35bSopenharmony_ci } 142e484b35bSopenharmony_ci return signal; 143e484b35bSopenharmony_ci } else if (isBlock(target)) { 144e484b35bSopenharmony_ci dest.element.insertBefore(target.start, before); 145e484b35bSopenharmony_ci dest.element.insertBefore(target.end, before); 146e484b35bSopenharmony_ci } else { 147e484b35bSopenharmony_ci return dest.element.insertBefore(target, before); 148e484b35bSopenharmony_ci } 149e484b35bSopenharmony_ci } else { 150e484b35bSopenharmony_ci if (isBlock(target)) { 151e484b35bSopenharmony_ci dest.appendChild(target.start); 152e484b35bSopenharmony_ci dest.appendChild(target.end); 153e484b35bSopenharmony_ci } else { 154e484b35bSopenharmony_ci return dest.appendChild(target); 155e484b35bSopenharmony_ci } 156e484b35bSopenharmony_ci } 157e484b35bSopenharmony_ci} 158e484b35bSopenharmony_ci 159e484b35bSopenharmony_ci/** 160e484b35bSopenharmony_ci * Move target before a certain element. The target maybe block or element. 161e484b35bSopenharmony_ci * @param {Element | FragBlockInterface} target - Block or element. 162e484b35bSopenharmony_ci * @param {Node} after - Node object after moving. 163e484b35bSopenharmony_ci * @return {*} 164e484b35bSopenharmony_ci */ 165e484b35bSopenharmony_ciexport function moveTarget(target: Element | FragBlockInterface, after: Node): any { 166e484b35bSopenharmony_ci if (isBlock(target)) { 167e484b35bSopenharmony_ci return moveBlock(target, after); 168e484b35bSopenharmony_ci } 169e484b35bSopenharmony_ci return moveElement(target, after); 170e484b35bSopenharmony_ci} 171e484b35bSopenharmony_ci 172e484b35bSopenharmony_ci/** 173e484b35bSopenharmony_ci * Move element before a certain element. 174e484b35bSopenharmony_ci * @param {Element} element - Element object. 175e484b35bSopenharmony_ci * @param {Node} after - Node object after moving. 176e484b35bSopenharmony_ci * @return {*} 177e484b35bSopenharmony_ci */ 178e484b35bSopenharmony_cifunction moveElement(element: Element, after: Node): any { 179e484b35bSopenharmony_ci const parent = after.parentNode as Element; 180e484b35bSopenharmony_ci if (parent && parent.children.indexOf(after) !== -1) { 181e484b35bSopenharmony_ci return parent.insertAfter(element, after); 182e484b35bSopenharmony_ci } 183e484b35bSopenharmony_ci} 184e484b35bSopenharmony_ci 185e484b35bSopenharmony_ci/** 186e484b35bSopenharmony_ci * Move all elements of the block before a certain element. 187e484b35bSopenharmony_ci * @param {FragBlockInterface} fragBlock - Frag block. 188e484b35bSopenharmony_ci * @param {Node} after - Node object after moving. 189e484b35bSopenharmony_ci */ 190e484b35bSopenharmony_cifunction moveBlock(fragBlock: FragBlockInterface, after: Node): any { 191e484b35bSopenharmony_ci const parent = after.parentNode as Element; 192e484b35bSopenharmony_ci if (parent) { 193e484b35bSopenharmony_ci let el = fragBlock.start as Node; 194e484b35bSopenharmony_ci let signal; 195e484b35bSopenharmony_ci const group = [el]; 196e484b35bSopenharmony_ci while (el && el !== fragBlock.end) { 197e484b35bSopenharmony_ci el = el.nextSibling; 198e484b35bSopenharmony_ci group.push(el); 199e484b35bSopenharmony_ci } 200e484b35bSopenharmony_ci let temp = after; 201e484b35bSopenharmony_ci group.every((el) => { 202e484b35bSopenharmony_ci signal = parent.insertAfter(el, temp); 203e484b35bSopenharmony_ci temp = el; 204e484b35bSopenharmony_ci return signal !== -1; 205e484b35bSopenharmony_ci }); 206e484b35bSopenharmony_ci return signal; 207e484b35bSopenharmony_ci } 208e484b35bSopenharmony_ci} 209e484b35bSopenharmony_ci 210e484b35bSopenharmony_ci/** 211e484b35bSopenharmony_ci * Remove target from DOM tree. 212e484b35bSopenharmony_ci * @param {Element | FragBlockInterface} target - If the target is a frag block then call _removeBlock 213e484b35bSopenharmony_ci * @param {boolean} [preserveBlock] - Preserve block. 214e484b35bSopenharmony_ci */ 215e484b35bSopenharmony_ciexport function removeTarget(target: Element | FragBlockInterface, preserveBlock?: boolean): void { 216e484b35bSopenharmony_ci if (!preserveBlock) { 217e484b35bSopenharmony_ci preserveBlock = false; 218e484b35bSopenharmony_ci } 219e484b35bSopenharmony_ci if (isBlock(target)) { 220e484b35bSopenharmony_ci removeBlock(target, preserveBlock); 221e484b35bSopenharmony_ci } else { 222e484b35bSopenharmony_ci removeElement(target); 223e484b35bSopenharmony_ci } 224e484b35bSopenharmony_ci if (target.vm) { 225e484b35bSopenharmony_ci target.vm.$emit('hook:onDetached'); 226e484b35bSopenharmony_ci emitSubVmLife(target.vm, 'onDetached'); 227e484b35bSopenharmony_ci target.vm.$emit('hook:destroyed'); 228e484b35bSopenharmony_ci } 229e484b35bSopenharmony_ci} 230e484b35bSopenharmony_ci 231e484b35bSopenharmony_ci/** 232e484b35bSopenharmony_ci * Remove an element. 233e484b35bSopenharmony_ci * @param {Element | Comment} target - Target element. 234e484b35bSopenharmony_ci */ 235e484b35bSopenharmony_cifunction removeElement(target: Element | Comment): void { 236e484b35bSopenharmony_ci const parent = target.parentNode as Element; 237e484b35bSopenharmony_ci if (parent) { 238e484b35bSopenharmony_ci parent.removeChild(target); 239e484b35bSopenharmony_ci } 240e484b35bSopenharmony_ci} 241e484b35bSopenharmony_ci 242e484b35bSopenharmony_ci/** 243e484b35bSopenharmony_ci * Remove a frag block. 244e484b35bSopenharmony_ci * @param {FragBlockInterface} fragBlock - Frag block. 245e484b35bSopenharmony_ci * @param {boolean} [preserveBlock] - If preserve block. 246e484b35bSopenharmony_ci */ 247e484b35bSopenharmony_cifunction removeBlock(fragBlock: FragBlockInterface, preserveBlock?: boolean): void { 248e484b35bSopenharmony_ci if (!preserveBlock) { 249e484b35bSopenharmony_ci preserveBlock = false; 250e484b35bSopenharmony_ci } 251e484b35bSopenharmony_ci const result = []; 252e484b35bSopenharmony_ci let el = fragBlock.start.nextSibling; 253e484b35bSopenharmony_ci while (el && el !== fragBlock.end) { 254e484b35bSopenharmony_ci result.push(el); 255e484b35bSopenharmony_ci el = el.nextSibling; 256e484b35bSopenharmony_ci } 257e484b35bSopenharmony_ci if (!preserveBlock) { 258e484b35bSopenharmony_ci removeElement(fragBlock.start); 259e484b35bSopenharmony_ci } 260e484b35bSopenharmony_ci result.forEach((el) => { 261e484b35bSopenharmony_ci removeElement(el); 262e484b35bSopenharmony_ci }); 263e484b35bSopenharmony_ci if (!preserveBlock) { 264e484b35bSopenharmony_ci removeElement(fragBlock.end); 265e484b35bSopenharmony_ci } 266e484b35bSopenharmony_ci} 267