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