<commit_before>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
*/

// https://developer.mozilla.org/en-US/docs/WebAPI/Browser

var cordova = require('cordova'),
    channel = require('cordova/channel'),
    modulemapper = require('cordova/modulemapper');

var origOpenFunc = modulemapper.getOriginalSymbol(window, 'window.open');
var browserWrap;

var IABExecs = {

    close: function (win, lose) {
        if (browserWrap) {
            browserWrap.parentNode.removeChild(browserWrap);
            browserWrap = null;
            if (typeof(win) == "function") win({type:'exit'});
        }
    },

    /*
     * Reveal browser if opened hidden
     */
    show: function (win, lose) {
        console.error('[FirefoxOS] show not implemented');
    },

    open: function (win, lose, args) {
        var strUrl = args[0],
            target = args[1],
            features_string = args[2] || "location=yes", //location=yes is default
            features = {},
            url,
            elem;

        var features_list = features_string.split(',');
        features_list.forEach(function(feature) {
            var tup = feature.split('=');
            if (tup[1] == 'yes') {
                tup[1] = true;
            } else if (tup[1] == 'no') {
                tup[1] = false;
            } else {
                var number = parseInt(tup[1]);
                if (!isNaN(number)) {
                    tup[1] = number;
                }
            }
            features[tup[0]] = tup[1];
        });

        function updateIframeSizeNoLocation() {
            browserWrap.style.width = window.innerWidth + 'px';
            browserWrap.style.height = window.innerHeight + 'px';
            browserWrap.browser.style.height = (window.innerHeight - 60) + 'px';
            browserWrap.browser.style.width = browserWrap.style.width;
        }

        if (target === '_system') {
            origOpenFunc.apply(window, [strUrl, '_blank']);
        } else if (target === '_blank') {
            var browserElem = document.createElement('iframe');
            browserElem.setAttribute('mozbrowser', true);
            // make this loaded in its own child process
            browserElem.setAttribute('remote', true);
            browserElem.setAttribute('src', strUrl);
            if (browserWrap) {
                document.body.removeChild(browserWrap);
            }
            browserWrap = document.createElement('div');
            // assign browser element to browserWrap for future reference
            browserWrap.browser = browserElem;

            browserWrap.classList.add('inAppBrowserWrap');
            // position fixed so that it works even when page is scrolled
            browserWrap.style.position = 'fixed';
            browserElem.style.position = 'absolute';
            browserElem.style.border = 0;
            browserElem.style.top = '60px';
            browserElem.style.left = '0px';
            updateIframeSizeNoLocation();

            var menu = document.createElement('menu');
            menu.setAttribute('type', 'toolbar');
            var close = document.createElement('li');
            var back = document.createElement('li');
            var forward = document.createElement('li');

            close.appendChild(document.createTextNode('×'));
            back.appendChild(document.createTextNode('<'));
            forward.appendChild(document.createTextNode('>'));

            close.classList.add('inAppBrowserClose');
            back.classList.add('inAppBrowserBack');
            forward.classList.add('inAppBrowserForward');

            function checkForwardBackward() {
                var backReq = browserElem.getCanGoBack();
                backReq.onsuccess = function() {
                    if (this.result) {
                        back.classList.remove('disabled');
                    } else {
                        back.classList.add('disabled');
                    }
                }
                var forwardReq = browserElem.getCanGoForward();
                forwardReq.onsuccess = function() {
                    if (this.result) {
                        forward.classList.remove('disabled');
                    } else {
                        forward.classList.add('disabled');
                    }
                }
            };

            browserElem.addEventListener('mozbrowserloadend', checkForwardBackward);

            close.addEventListener('click', function () {
                setTimeout(function () {
                    IABExecs.close(win, lose);
                }, 0);
            }, false);

            back.addEventListener('click', function () {
                browserElem.goBack();
            }, false);

            forward.addEventListener('click', function () {
                browserElem.goForward();
            }, false);

            menu.appendChild(back);
            menu.appendChild(forward);
            menu.appendChild(close);

            browserWrap.appendChild(menu);
            browserWrap.appendChild(browserElem);
            document.body.appendChild(browserWrap);

            //we use mozbrowserlocationchange instead of mozbrowserloadstart to get the url
            browserElem.addEventListener('mozbrowserlocationchange', function(e){
                win({
                    type:'loadstart',
                    url : e.detail
                })
            }, false);
            browserElem.addEventListener('mozbrowserloadend', function(e){
                win({type:'loadstop'})
            }, false);
            browserElem.addEventListener('mozbrowsererror', function(e){
                win({type:'loaderror'})
            }, false);
            browserElem.addEventListener('mozbrowserclose', function(e){
                win({type:'exit'})
            }, false);
        } else {
            window.location = strUrl;
        }
    },
    injectScriptCode: function (code, bCB) {
        console.error('[FirefoxOS] injectScriptCode not implemented');
    },
    injectScriptFile: function (file, bCB) {
        console.error('[FirefoxOS] injectScriptFile not implemented');
    }
};

module.exports = IABExecs;

require('cordova/exec/proxy').add('InAppBrowser', module.exports);
<commit_msg>zIndex fixed<commit_after>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
*/

// https://developer.mozilla.org/en-US/docs/WebAPI/Browser

var cordova = require('cordova'),
    channel = require('cordova/channel'),
    modulemapper = require('cordova/modulemapper');

var origOpenFunc = modulemapper.getOriginalSymbol(window, 'window.open');
var browserWrap;

var IABExecs = {

    close: function (win, lose) {
        if (browserWrap) {
            browserWrap.parentNode.removeChild(browserWrap);
            browserWrap = null;
            if (typeof(win) == "function") win({type:'exit'});
        }
    },

    /*
     * Reveal browser if opened hidden
     */
    show: function (win, lose) {
        console.error('[FirefoxOS] show not implemented');
    },

    open: function (win, lose, args) {
        var strUrl = args[0],
            target = args[1],
            features_string = args[2] || "location=yes", //location=yes is default
            features = {},
            url,
            elem;

        var features_list = features_string.split(',');
        features_list.forEach(function(feature) {
            var tup = feature.split('=');
            if (tup[1] == 'yes') {
                tup[1] = true;
            } else if (tup[1] == 'no') {
                tup[1] = false;
            } else {
                var number = parseInt(tup[1]);
                if (!isNaN(number)) {
                    tup[1] = number;
                }
            }
            features[tup[0]] = tup[1];
        });

        function updateIframeSizeNoLocation() {
            browserWrap.style.width = window.innerWidth + 'px';
            browserWrap.style.height = window.innerHeight + 'px';
            browserWrap.style.zIndex = '999999999';
            browserWrap.browser.style.height = (window.innerHeight - 60) + 'px';
            browserWrap.browser.style.width = browserWrap.style.width;
        }

        if (target === '_system') {
            origOpenFunc.apply(window, [strUrl, '_blank']);
        } else if (target === '_blank') {
            var browserElem = document.createElement('iframe');
            browserElem.setAttribute('mozbrowser', true);
            // make this loaded in its own child process
            browserElem.setAttribute('remote', true);
            browserElem.setAttribute('src', strUrl);
            if (browserWrap) {
                document.body.removeChild(browserWrap);
            }
            browserWrap = document.createElement('div');
            // assign browser element to browserWrap for future reference
            browserWrap.browser = browserElem;

            browserWrap.classList.add('inAppBrowserWrap');
            // position fixed so that it works even when page is scrolled
            browserWrap.style.position = 'fixed';
            browserElem.style.position = 'absolute';
            browserElem.style.border = 0;
            browserElem.style.top = '60px';
            browserElem.style.left = '0px';
            updateIframeSizeNoLocation();

            var menu = document.createElement('menu');
            menu.setAttribute('type', 'toolbar');
            var close = document.createElement('li');
            var back = document.createElement('li');
            var forward = document.createElement('li');

            close.appendChild(document.createTextNode('×'));
            back.appendChild(document.createTextNode('<'));
            forward.appendChild(document.createTextNode('>'));

            close.classList.add('inAppBrowserClose');
            back.classList.add('inAppBrowserBack');
            forward.classList.add('inAppBrowserForward');

            function checkForwardBackward() {
                var backReq = browserElem.getCanGoBack();
                backReq.onsuccess = function() {
                    if (this.result) {
                        back.classList.remove('disabled');
                    } else {
                        back.classList.add('disabled');
                    }
                }
                var forwardReq = browserElem.getCanGoForward();
                forwardReq.onsuccess = function() {
                    if (this.result) {
                        forward.classList.remove('disabled');
                    } else {
                        forward.classList.add('disabled');
                    }
                }
            };

            browserElem.addEventListener('mozbrowserloadend', checkForwardBackward);

            close.addEventListener('click', function () {
                setTimeout(function () {
                    IABExecs.close(win, lose);
                }, 0);
            }, false);

            back.addEventListener('click', function () {
                browserElem.goBack();
            }, false);

            forward.addEventListener('click', function () {
                browserElem.goForward();
            }, false);

            menu.appendChild(back);
            menu.appendChild(forward);
            menu.appendChild(close);

            browserWrap.appendChild(menu);
            browserWrap.appendChild(browserElem);
            document.body.appendChild(browserWrap);

            //we use mozbrowserlocationchange instead of mozbrowserloadstart to get the url
            browserElem.addEventListener('mozbrowserlocationchange', function(e){
                win({
                    type:'loadstart',
                    url : e.detail
                })
            }, false);
            browserElem.addEventListener('mozbrowserloadend', function(e){
                win({type:'loadstop'})
            }, false);
            browserElem.addEventListener('mozbrowsererror', function(e){
                win({type:'loaderror'})
            }, false);
            browserElem.addEventListener('mozbrowserclose', function(e){
                win({type:'exit'})
            }, false);
        } else {
            window.location = strUrl;
        }
    },
    injectScriptCode: function (code, bCB) {
        console.error('[FirefoxOS] injectScriptCode not implemented');
    },
    injectScriptFile: function (file, bCB) {
        console.error('[FirefoxOS] injectScriptFile not implemented');
    }
};

module.exports = IABExecs;

require('cordova/exec/proxy').add('InAppBrowser', module.exports);
<|endoftext|>


<commit_before>const {contains} = require("../util/dom")

function isEditorContent(dom) {
  return dom.classList.contains("ProseMirror-content")
}

// : (DOMNode) → number
// Get the position before a given a DOM node in a document.
function posBeforeFromDOM(node) {
  let pos = 0, add = 0
  for (let cur = node; !isEditorContent(cur); cur = cur.parentNode) {
    let attr = cur.getAttribute("pm-offset")
    if (attr) { pos += +attr + add; add = 1 }
  }
  return pos
}

const posFromDOMResult = {pos: 0, inLeaf: -1}

// : (DOMNode, number) → number
function posFromDOM(dom, domOffset, bias = 0) {
  if (domOffset == null) {
    domOffset = Array.prototype.indexOf.call(dom.parentNode.childNodes, dom)
    dom = dom.parentNode
  }

  // Move up to the wrapping container, counting local offset along
  // the way.
  let innerOffset = 0, tag
  for (;;) {
    let adjust = 0
    if (dom.nodeType == 3) {
      innerOffset += domOffset
    } else if (tag = dom.getAttribute("pm-offset") && !childContainer(dom)) {
      let size = +dom.getAttribute("pm-size")
      if (dom.nodeType == 1 && !dom.firstChild) innerOffset = bias > 0 ? size : 0
      else if (domOffset == dom.childNodes.length) innerOffset = size
      else innerOffset = Math.min(innerOffset, size)
      let inLeaf = posFromDOMResult.inLeaf = posBeforeFromDOM(dom)
      posFromDOMResult.pos = inLeaf + innerOffset
      return posFromDOMResult
    } else if (dom.hasAttribute("pm-container")) {
      break
    } else if (domOffset == dom.childNodes.length) {
      if (domOffset) adjust = 1
      else adjust = bias > 0 ? 1 : 0
    }

    let parent = dom.parentNode
    domOffset = adjust < 0 ? 0 : Array.prototype.indexOf.call(parent.childNodes, dom) + adjust
    dom = parent
    bias = 0
  }

  let start = isEditorContent(dom) ? 0 : posBeforeFromDOM(dom) + 1, before = 0

  for (let child = dom.childNodes[domOffset - 1]; child; child = child.previousSibling) {
    if (child.nodeType == 1 && (tag = child.getAttribute("pm-offset"))) {
      before += +tag + +child.getAttribute("pm-size")
      break
    }
  }
  posFromDOMResult.inLeaf = -1
  posFromDOMResult.pos = start + before + innerOffset
  return posFromDOMResult
}
exports.posFromDOM = posFromDOM

// : (DOMNode) → ?DOMNode
function childContainer(dom) {
  return dom.hasAttribute("pm-container") ? dom : dom.querySelector("[pm-container]")
}
exports.childContainer = childContainer

// : (ProseMirror, number) → {node: DOMNode, offset: number}
// Find the DOM node and offset into that node that the given document
// position refers to.
function DOMFromPos(pm, pos, loose) {
  if (!loose && pm.operation && pm.doc != pm.operation.doc)
    throw new RangeError("Resolving a position in an outdated DOM structure")

  let container = pm.content, offset = pos
  for (;;) {
    for (let child = container.firstChild, i = 0;; child = child.nextSibling, i++) {
      if (!child) {
        if (offset && !loose) throw new RangeError("Failed to find node at " + pos)
        return {node: container, offset: i}
      }

      let size = child.nodeType == 1 && child.getAttribute("pm-size")
      if (size) {
        if (!offset) return {node: container, offset: i}
        size = +size
        if (offset < size) {
          container = childContainer(child)
          if (!container) {
            return leafAt(child, offset)
          } else {
            offset--
            break
          }
        } else {
          offset -= size
        }
      }
    }
  }
}
exports.DOMFromPos = DOMFromPos

// : (ProseMirror, number) → {node: DOMNode, offset: number}
// The same as DOMFromPos, but searching from the bottom instead of
// the top. This is needed in domchange.js, when there is an arbitrary
// DOM change somewhere in our document, and we can no longer rely on
// the DOM structure around the selection.
function DOMFromPosFromEnd(pm, pos) {
  let container = pm.content, dist = (pm.operation ? pm.operation.doc : pm.doc).content.size - pos
  for (;;) {
    for (let child = container.lastChild, i = container.childNodes.length;; child = child.previousSibling, i--) {
      if (!child) return {node: container, offset: i}

      let size = child.nodeType == 1 && child.getAttribute("pm-size")
      if (size) {
        if (!dist) return {node: container, offset: i}
        size = +size
        if (dist < size) {
          container = childContainer(child)
          if (!container) {
            return leafAt(child, size - dist)
          } else {
            dist--
            break
          }
        } else {
          dist -= size
        }
      }
    }
  }
}
exports.DOMFromPosFromEnd = DOMFromPosFromEnd

// : (ProseMirror, number) → DOMNode
function DOMAfterPos(pm, pos) {
  let {node, offset} = DOMFromPos(pm, pos)
  if (node.nodeType != 1 || offset == node.childNodes.length)
    throw new RangeError("No node after pos " + pos)
  return node.childNodes[offset]
}
exports.DOMAfterPos = DOMAfterPos

// : (DOMNode, number) → {node: DOMNode, offset: number}
function leafAt(node, offset) {
  for (;;) {
    let child = node.firstChild
    if (!child) return {node, offset}
    if (child.nodeType != 1) return {node: child, offset}
    node = child
  }
}

function windowRect() {
  return {left: 0, right: window.innerWidth,
          top: 0, bottom: window.innerHeight}
}

function parentNode(node) {
  let parent = node.parentNode
  return parent.nodeType == 11 ? parent.host : parent
}

function scrollIntoView(pm, pos) {
  if (!pos) pos = pm.sel.range.head || pm.sel.range.from
  let coords = coordsAtPos(pm, pos)
  for (let parent = pm.content;; parent = parentNode(parent)) {
    let {scrollThreshold, scrollMargin} = pm.options
    let atBody = parent == document.body
    let rect = atBody ? windowRect() : parent.getBoundingClientRect()
    let moveX = 0, moveY = 0
    if (coords.top < rect.top + scrollThreshold)
      moveY = -(rect.top - coords.top + scrollMargin)
    else if (coords.bottom > rect.bottom - scrollThreshold)
      moveY = coords.bottom - rect.bottom + scrollMargin
    if (coords.left < rect.left + scrollThreshold)
      moveX = -(rect.left - coords.left + scrollMargin)
    else if (coords.right > rect.right - scrollThreshold)
      moveX = coords.right - rect.right + scrollMargin
    if (moveX || moveY) {
      if (atBody) {
        window.scrollBy(moveX, moveY)
      } else {
        if (moveY) parent.scrollTop += moveY
        if (moveX) parent.scrollLeft += moveX
      }
    }
    if (atBody) break
  }
}
exports.scrollIntoView = scrollIntoView

function findOffsetInNode(node, coords) {
  let closest, dxClosest = 2e8, coordsClosest, offset = 0
  for (let child = node.firstChild; child; child = child.nextSibling) {
    let rects
    if (child.nodeType == 1) rects = child.getClientRects()
    else if (child.nodeType == 3) rects = textRange(child).getClientRects()
    else continue

    for (let i = 0; i < rects.length; i++) {
      let rect = rects[i]
      if (rect.top <= coords.top && rect.bottom >= coords.top) {
        let dx = rect.left > coords.left ? rect.left - coords.left
            : rect.right < coords.left ? coords.left - rect.right : 0
        if (dx < dxClosest) {
          closest = child
          dxClosest = dx
          coordsClosest = dx && closest.nodeType == 3 ? {left: rect.right < coords.left ? rect.right : rect.left, top: coords.top} : coords
          if (child.nodeType == 1 && !child.firstChild)
            offset = i + (coords.left >= (rect.left + rect.right) / 2 ? 1 : 0)
          continue
        }
      }
      if (!closest && (coords.left >= rect.right || coords.left >= rect.left && coords.top >= rect.bottom))
        offset = i + 1
    }
  }
  if (closest && closest.nodeType == 3) return findOffsetInText(closest, coordsClosest)
  if (!closest || !closest.childNodes.length) return {node, offset}
  return findOffsetInNode(closest, coordsClosest)
}

function findOffsetInText(node, coords) {
  let len = node.nodeValue.length
  let range = document.createRange()
  for (let i = 0; i < len; i++) {
    range.setEnd(node, i + 1)
    range.setStart(node, i)
    let rect = singleRect(range, 1)
    if (rect.top == rect.bottom) continue
    if (rect.left - 1 <= coords.left && rect.right + 1 >= coords.left &&
        rect.top - 1 <= coords.top && rect.bottom + 1 >= coords.top)
      return {node, offset: i + (coords.left >= (rect.left + rect.right) / 2 ? 1 : 0)}
  }
  return {node, offset: 0}
}

function targetKludge(dom, coords) {
  if (/^[uo]l$/i.test(dom.nodeName)) {
    for (let child = dom.firstChild; child; child = child.nextSibling) {
      if (child.nodeType != 1 || !child.hasAttribute("pm-offset") || !/^li$/i.test(child.nodeName)) continue
      let childBox = child.getBoundingClientRect()
      if (coords.left > childBox.left - 2) break
      if (childBox.top <= coords.top && childBox.bottom >= coords.top) return child
    }
  }
  return dom
}

// Given an x,y position on the editor, get the position in the document.
function posAtCoords(pm, coords) {
  let elt = targetKludge(document.elementFromPoint(coords.left, coords.top + 1), coords)
  if (!contains(pm.content, elt)) return null

  let {node, offset} = findOffsetInNode(elt, coords), bias = -1
  if (node.nodeType == 1 && !node.firstChild) {
    let rect = node.getBoundingClientRect()
    bias = rect.left != rect.right && coords.left > (rect.left + rect.right) / 2 ? 1 : -1
  }
  return posFromDOM(node, offset, bias)
}
exports.posAtCoords = posAtCoords

function textRange(node, from, to) {
  let range = document.createRange()
  range.setEnd(node, to == null ? node.nodeValue.length : to)
  range.setStart(node, from || 0)
  return range
}

function singleRect(object, bias) {
  let rects = object.getClientRects()
  return !rects.length ? object.getBoundingClientRect() : rects[bias < 0 ? 0 : rects.length - 1]
}

// : (ProseMirror, number) → ClientRect
// Given a position in the document model, get a bounding box of the
// character at that position, relative to the window.
function coordsAtPos(pm, pos) {
  let {node, offset} = DOMFromPos(pm, pos)
  let side, rect
  if (node.nodeType == 3) {
    if (offset < node.nodeValue.length) {
      rect = singleRect(textRange(node, offset, offset + 1), -1)
      side = "left"
    }
    if ((!rect || rect.left == rect.right) && offset) {
      rect = singleRect(textRange(node, offset - 1, offset), 1)
      side = "right"
    }
  } else if (node.firstChild) {
    if (offset < node.childNodes.length) {
      let child = node.childNodes[offset]
      rect = singleRect(child.nodeType == 3 ? textRange(child) : child, -1)
      side = "left"
    }
    if ((!rect || rect.top == rect.bottom) && offset) {
      let child = node.childNodes[offset - 1]
      rect = singleRect(child.nodeType == 3 ? textRange(child) : child, 1)
      side = "right"
    }
  } else {
    rect = node.getBoundingClientRect()
    side = "left"
  }
  let x = rect[side]
  return {top: rect.top, bottom: rect.bottom, left: x, right: x}
}
exports.coordsAtPos = coordsAtPos
<commit_msg>Reduce false positive inLeaf results in posAtCoords<commit_after>const {contains} = require("../util/dom")

function isEditorContent(dom) {
  return dom.classList.contains("ProseMirror-content")
}

// : (DOMNode) → number
// Get the position before a given a DOM node in a document.
function posBeforeFromDOM(node) {
  let pos = 0, add = 0
  for (let cur = node; !isEditorContent(cur); cur = cur.parentNode) {
    let attr = cur.getAttribute("pm-offset")
    if (attr) { pos += +attr + add; add = 1 }
  }
  return pos
}

const posFromDOMResult = {pos: 0, inLeaf: -1}

// : (DOMNode, number) → number
function posFromDOM(dom, domOffset, bias = 0) {
  if (domOffset == null) {
    domOffset = Array.prototype.indexOf.call(dom.parentNode.childNodes, dom)
    dom = dom.parentNode
  }

  // Move up to the wrapping container, counting local offset along
  // the way.
  let innerOffset = 0, tag
  for (;;) {
    let adjust = 0
    if (dom.nodeType == 3) {
      innerOffset += domOffset
    } else if (tag = dom.getAttribute("pm-offset") && !childContainer(dom)) {
      let size = +dom.getAttribute("pm-size")
      if (dom.nodeType == 1 && !dom.firstChild) innerOffset = bias > 0 ? size : 0
      else if (domOffset == dom.childNodes.length) innerOffset = size
      else innerOffset = Math.min(innerOffset, size)
      let inLeaf = posFromDOMResult.inLeaf = posBeforeFromDOM(dom)
      posFromDOMResult.pos = inLeaf + innerOffset
      return posFromDOMResult
    } else if (dom.hasAttribute("pm-container")) {
      break
    } else if (domOffset == dom.childNodes.length) {
      if (domOffset) adjust = 1
      else adjust = bias > 0 ? 1 : 0
    }

    let parent = dom.parentNode
    domOffset = adjust < 0 ? 0 : Array.prototype.indexOf.call(parent.childNodes, dom) + adjust
    dom = parent
    bias = 0
  }

  let start = isEditorContent(dom) ? 0 : posBeforeFromDOM(dom) + 1, before = 0

  for (let child = dom.childNodes[domOffset - 1]; child; child = child.previousSibling) {
    if (child.nodeType == 1 && (tag = child.getAttribute("pm-offset"))) {
      before += +tag + +child.getAttribute("pm-size")
      break
    }
  }
  posFromDOMResult.inLeaf = -1
  posFromDOMResult.pos = start + before + innerOffset
  return posFromDOMResult
}
exports.posFromDOM = posFromDOM

// : (DOMNode) → ?DOMNode
function childContainer(dom) {
  return dom.hasAttribute("pm-container") ? dom : dom.querySelector("[pm-container]")
}
exports.childContainer = childContainer

// : (ProseMirror, number) → {node: DOMNode, offset: number}
// Find the DOM node and offset into that node that the given document
// position refers to.
function DOMFromPos(pm, pos, loose) {
  if (!loose && pm.operation && pm.doc != pm.operation.doc)
    throw new RangeError("Resolving a position in an outdated DOM structure")

  let container = pm.content, offset = pos
  for (;;) {
    for (let child = container.firstChild, i = 0;; child = child.nextSibling, i++) {
      if (!child) {
        if (offset && !loose) throw new RangeError("Failed to find node at " + pos)
        return {node: container, offset: i}
      }

      let size = child.nodeType == 1 && child.getAttribute("pm-size")
      if (size) {
        if (!offset) return {node: container, offset: i}
        size = +size
        if (offset < size) {
          container = childContainer(child)
          if (!container) {
            return leafAt(child, offset)
          } else {
            offset--
            break
          }
        } else {
          offset -= size
        }
      }
    }
  }
}
exports.DOMFromPos = DOMFromPos

// : (ProseMirror, number) → {node: DOMNode, offset: number}
// The same as DOMFromPos, but searching from the bottom instead of
// the top. This is needed in domchange.js, when there is an arbitrary
// DOM change somewhere in our document, and we can no longer rely on
// the DOM structure around the selection.
function DOMFromPosFromEnd(pm, pos) {
  let container = pm.content, dist = (pm.operation ? pm.operation.doc : pm.doc).content.size - pos
  for (;;) {
    for (let child = container.lastChild, i = container.childNodes.length;; child = child.previousSibling, i--) {
      if (!child) return {node: container, offset: i}

      let size = child.nodeType == 1 && child.getAttribute("pm-size")
      if (size) {
        if (!dist) return {node: container, offset: i}
        size = +size
        if (dist < size) {
          container = childContainer(child)
          if (!container) {
            return leafAt(child, size - dist)
          } else {
            dist--
            break
          }
        } else {
          dist -= size
        }
      }
    }
  }
}
exports.DOMFromPosFromEnd = DOMFromPosFromEnd

// : (ProseMirror, number) → DOMNode
function DOMAfterPos(pm, pos) {
  let {node, offset} = DOMFromPos(pm, pos)
  if (node.nodeType != 1 || offset == node.childNodes.length)
    throw new RangeError("No node after pos " + pos)
  return node.childNodes[offset]
}
exports.DOMAfterPos = DOMAfterPos

// : (DOMNode, number) → {node: DOMNode, offset: number}
function leafAt(node, offset) {
  for (;;) {
    let child = node.firstChild
    if (!child) return {node, offset}
    if (child.nodeType != 1) return {node: child, offset}
    node = child
  }
}

function windowRect() {
  return {left: 0, right: window.innerWidth,
          top: 0, bottom: window.innerHeight}
}

function parentNode(node) {
  let parent = node.parentNode
  return parent.nodeType == 11 ? parent.host : parent
}

function scrollIntoView(pm, pos) {
  if (!pos) pos = pm.sel.range.head || pm.sel.range.from
  let coords = coordsAtPos(pm, pos)
  for (let parent = pm.content;; parent = parentNode(parent)) {
    let {scrollThreshold, scrollMargin} = pm.options
    let atBody = parent == document.body
    let rect = atBody ? windowRect() : parent.getBoundingClientRect()
    let moveX = 0, moveY = 0
    if (coords.top < rect.top + scrollThreshold)
      moveY = -(rect.top - coords.top + scrollMargin)
    else if (coords.bottom > rect.bottom - scrollThreshold)
      moveY = coords.bottom - rect.bottom + scrollMargin
    if (coords.left < rect.left + scrollThreshold)
      moveX = -(rect.left - coords.left + scrollMargin)
    else if (coords.right > rect.right - scrollThreshold)
      moveX = coords.right - rect.right + scrollMargin
    if (moveX || moveY) {
      if (atBody) {
        window.scrollBy(moveX, moveY)
      } else {
        if (moveY) parent.scrollTop += moveY
        if (moveX) parent.scrollLeft += moveX
      }
    }
    if (atBody) break
  }
}
exports.scrollIntoView = scrollIntoView

function findOffsetInNode(node, coords) {
  let closest, dxClosest = 2e8, coordsClosest, offset = 0
  for (let child = node.firstChild, childIndex = 0; child; child = child.nextSibling, childIndex++) {
    let rects
    if (child.nodeType == 1) rects = child.getClientRects()
    else if (child.nodeType == 3) rects = textRange(child).getClientRects()
    else continue

    for (let i = 0; i < rects.length; i++) {
      let rect = rects[i]
      if (rect.top <= coords.top && rect.bottom >= coords.top) {
        let dx = rect.left > coords.left ? rect.left - coords.left
            : rect.right < coords.left ? coords.left - rect.right : 0
        if (dx < dxClosest) {
          closest = child
          dxClosest = dx
          coordsClosest = dx && closest.nodeType == 3 ? {left: rect.right < coords.left ? rect.right : rect.left, top: coords.top} : coords
          if (child.nodeType == 1 && dx)
            offset = childIndex + (coords.left >= (rect.left + rect.right) / 2 ? 1 : 0)
          continue
        }
      }
      if (!closest && (coords.left >= rect.right || coords.left >= rect.left && coords.top >= rect.bottom))
        offset = i + 1
    }
  }
  if (closest && closest.nodeType == 3) return findOffsetInText(closest, coordsClosest)
  if (!closest || (dxClosest && closest.nodeType == 1)) return {node, offset}
  return findOffsetInNode(closest, coordsClosest)
}

function findOffsetInText(node, coords) {
  let len = node.nodeValue.length
  let range = document.createRange()
  for (let i = 0; i < len; i++) {
    range.setEnd(node, i + 1)
    range.setStart(node, i)
    let rect = singleRect(range, 1)
    if (rect.top == rect.bottom) continue
    if (rect.left - 1 <= coords.left && rect.right + 1 >= coords.left &&
        rect.top - 1 <= coords.top && rect.bottom + 1 >= coords.top)
      return {node, offset: i + (coords.left >= (rect.left + rect.right) / 2 ? 1 : 0)}
  }
  return {node, offset: 0}
}

function targetKludge(dom, coords) {
  if (/^[uo]l$/i.test(dom.nodeName)) {
    for (let child = dom.firstChild; child; child = child.nextSibling) {
      if (child.nodeType != 1 || !child.hasAttribute("pm-offset") || !/^li$/i.test(child.nodeName)) continue
      let childBox = child.getBoundingClientRect()
      if (coords.left > childBox.left - 2) break
      if (childBox.top <= coords.top && childBox.bottom >= coords.top) return child
    }
  }
  return dom
}

// Given an x,y position on the editor, get the position in the document.
function posAtCoords(pm, coords) {
  let elt = targetKludge(document.elementFromPoint(coords.left, coords.top + 1), coords)
  if (!contains(pm.content, elt)) return null

  let {node, offset} = findOffsetInNode(elt, coords), bias = -1
  if (node.nodeType == 1 && !node.firstChild) {
    let rect = node.getBoundingClientRect()
    bias = rect.left != rect.right && coords.left > (rect.left + rect.right) / 2 ? 1 : -1
  }
  return posFromDOM(node, offset, bias)
}
exports.posAtCoords = posAtCoords

function textRange(node, from, to) {
  let range = document.createRange()
  range.setEnd(node, to == null ? node.nodeValue.length : to)
  range.setStart(node, from || 0)
  return range
}

function singleRect(object, bias) {
  let rects = object.getClientRects()
  return !rects.length ? object.getBoundingClientRect() : rects[bias < 0 ? 0 : rects.length - 1]
}

// : (ProseMirror, number) → ClientRect
// Given a position in the document model, get a bounding box of the
// character at that position, relative to the window.
function coordsAtPos(pm, pos) {
  let {node, offset} = DOMFromPos(pm, pos)
  let side, rect
  if (node.nodeType == 3) {
    if (offset < node.nodeValue.length) {
      rect = singleRect(textRange(node, offset, offset + 1), -1)
      side = "left"
    }
    if ((!rect || rect.left == rect.right) && offset) {
      rect = singleRect(textRange(node, offset - 1, offset), 1)
      side = "right"
    }
  } else if (node.firstChild) {
    if (offset < node.childNodes.length) {
      let child = node.childNodes[offset]
      rect = singleRect(child.nodeType == 3 ? textRange(child) : child, -1)
      side = "left"
    }
    if ((!rect || rect.top == rect.bottom) && offset) {
      let child = node.childNodes[offset - 1]
      rect = singleRect(child.nodeType == 3 ? textRange(child) : child, 1)
      side = "right"
    }
  } else {
    rect = node.getBoundingClientRect()
    side = "left"
  }
  let x = rect[side]
  return {top: rect.top, bottom: rect.bottom, left: x, right: x}
}
exports.coordsAtPos = coordsAtPos
<|endoftext|>


<commit_before>// CAMIC is an instance of camicroscope core
// $CAMIC in there
let $CAMIC = null;
let $minorCAMIC = null;
// for all instances of UI components
const $UI = new Map();

const $D = {
  pages: {
    home: '../table.html',
    table: '../table.html',
  },
  params: null, // parameter from url - slide Id and status in it (object).
  overlayers: null, // array for each layers
  templates: null, // json schema for prue-form
};

// initialize viewer page
function initialize() {
  var checkPackageIsReady = setInterval(function() {
    if (IsPackageLoading) {
      clearInterval(checkPackageIsReady);
      // init UI -- some of them need to wait data loader to load data
      // because UI components need data to initialize
      initUIcomponents();
      // create a viewer and set up
      initCore();
    }
  }, 100);
  // loading the form template data
  // FormTempaltesLoader();

  // loading the overlayers data
  // OverlayersLoader();
}

// setting core functionalities
function initCore() {
  // start initial
  // TODO zoom info and mmp
  const opt = {};
  // set states if exist
  if ($D.params.states) {
    opt.states = $D.params.states;
  }
  // create the message queue
  $UI.message = new MessageQueue();
  try {
    const slideQuery = {};
    slideQuery.id = $D.params.slideId;
    slideQuery.name = $D.params.slide;
    slideQuery.location = $D.params.location;
    $CAMIC = new CaMic('main_viewer', slideQuery, opt);
  } catch (error) {
    Loading.close();
    $UI.message.addError('Core Initialization Failed');
    console.error(error);
    return;
  }

  $CAMIC.loadImg(async function(e) {
    Loading.open(document.body, `Loading Data ...`);
    // image loaded
    if (e.hasError) {
      $UI.message.addError(e.message);
      Loading.close();
    } else {
      $D.params.data = e;
      // loading heatmap data
      $D.heatMapData = await $CAMIC.store.getHeatmap($D.params.slideId, $D.params.execId).then((d)=> d[0]);
      const slide = $D.params.slideId;
      const exec = $D.heatMapData.provenance.analysis.execution_id;

      if (ImgloaderMode!='imgbox') {
        // query from DB
        const editData = await $CAMIC.store.findHeatmapEdit(getUserId(), slide, exec).then((d)=>d[0]);
        // const editData = await $CAMIC.store.findHeatmapEdit('test', slide, exec).then(d=>d[0]);
        $D.editedDataClusters = new EditDataCluster();
        if (editData&&Array.isArray(editData.data)&&editData.data.length > 0) {
          setEditedDataClusters(editData.data);
        }
      } else {
        $D.editedDataClusters = new EditDataCluster();
      }

      if (!$D.heatMapData) {
        redirect($D.pages.table, `No Heatmap's Data Found. Redirecting To Table.`);
      }

      $UI.slideInfos = new CaMessage({
      /* opts that need to think of*/
        id: 'cames',
        defaultText: `Slide: ${$D.params.data.name}`,
      });

      // spyglass
      $UI.spyglass = new Spyglass({
        targetViewer: $CAMIC.viewer,
        imgsrc: $D.params.data.url,
      });
    }
  });

  $CAMIC.viewer.addHandler('open', function() {
    Loading.open(document.body, `Loading Data ...`);
    // load the heatmap data

    var checkImagingHelperIsReady = setInterval(function() {
      if ($CAMIC.viewer.imagingHelper &&
        $CAMIC.viewer.imagingHelper._haveImage &&
        $D.heatMapData &&
        $D.editedDataClusters &&
        $UI.editedListSideMenu) {
        clearInterval(checkImagingHelperIsReady);

        const opt = {
          hasCover: true,
          opacity: .65, // inputs[2].value,
          coverOpacity: .3,
          data: $D.heatMapData.data,
          editedData: $D.editedDataClusters,
          size: $D.heatMapData.provenance.analysis.size,
          fields: $D.heatMapData.provenance.analysis.fields,
          color: '#1034A6', // inputs[3].value
        };

        if ($D.heatMapData.provenance.analysis.setting) {
          opt.mode = $D.heatMapData.provenance.analysis.setting.mode;
          if (opt.mode === 'binal') {
            opt.color = $D.heatMapData.provenance.analysis.setting.colors[0];
          } else if (opt.mode === 'gradient') {
            let prevColors = $D.heatMapData.provenance.analysis.setting.colors || [];
            opt.colors = prevColors;
            opt.steps = prevColors.length + 1;
          }
          if ($D.heatMapData.provenance.analysis.setting.field) {
            opt.currentFieldName = $D.heatMapData.provenance.analysis.setting.field;
          }
        }
        $CAMIC.viewer.createHeatmap(opt);

        // create heatmap control
        $UI.heatmapcontrol = new HeatmapControl({
          mode: opt.mode,
          currentField: opt.currentFieldName,
          fields: $D.heatMapData.provenance.analysis.fields,
          opacities: [{
            name: 'heat',
            value: 0.65,
          }, {
            name: 'cover',
            value: 0.3,
          }],
          onChange: heatmapSettingChanged,
          onOpacityChange: heatmapOpacityChanaged,
        });

        $UI.settingsSideMenu.addContent($UI.heatmapcontrol.elt);

        // create heatmap editor

        $UI.heatmapEditorPanel = new HeatmapEditorPanel({
          fields: $D.heatMapData.provenance.analysis.fields,
          // editedDate:$D.
          onFieldChange: editorPenChange,
          onReset: function() {
            if (confirm('Do You Want To Clear Edited Data?')) {
              $CAMIC.viewer.canvasDrawInstance.clear();
            }
          }, // clearEditData
          onSave: saveEditData,
        });
        $UI.editorSideMenu.addContent($UI.heatmapEditorPanel.elt);

        // create edited data list
        $UI.heatmapEditedDataPanel = new HeatmapEditedDataPanel({
          // data:$D.heatMapData.editedClusters,
          data: $D.editedDataClusters,
          // editedDate:$D.
          onDBClick: locateEditData,
          onDelete: onDeleteEditData,
        });

        $UI.editedListSideMenu.addContent($UI.heatmapEditedDataPanel.elt);

        // TODO create save botton if user is admin
        if (ImgloaderMode!='imgbox') {
          const btnDiv = createThreshold();
          $UI.settingsSideMenu.addContent(btnDiv);
        } else {
          const btnDiv = createExportEditData();
          $UI.editedListSideMenu.addContent(btnDiv);
        }

        Loading.close();
      }
    }, 500);
  });
}

// initialize all UI components
function initUIcomponents() {
  /* create UI components */

  // create the tool bar
  $UI.toolbar = new CaToolbar({
  /* opts that need to think of*/
    id: 'ca_tools',
    zIndex: 601,
    hasMainTools: false,
    // mainToolsCallback:mainMenuChange,
    subTools: [
      {
        name: 'editeddate',
        icon: 'view_list', // material icons' name
        title: 'Edited Data List',
        type: 'check', // btn/check/dropdown
        value: 'editeddate',
        callback: toggleHeatMapDataList,
      },
      // setting
      {
        name: 'settings',
        icon: 'settings', // material icons' name
        title: 'Settings',
        type: 'check', // btn/check/dropdown
        value: 'settings',
        callback: toggleHeatMapSettings,
      }, {
        name: 'editor',
        icon: 'create', // material icons' name
        title: 'editor',
        type: 'check', // btn/check/dropdown
        value: 'editor',
        callback: toggleHeatMapEditor,
      },
      // home
      // {
      //   icon:'home',// material icons' name
      //   title:'Home',
      //   type:'btn',// btn/check/dropdown
      //   value:'home',
      //   callback:goHome
      // },
      //
      // pen
      // {
      //   icon:'create',// material icons' name
      //   title:'Draw',
      //   type:'multistates',
      //   callback:draw
      // },
      // magnifier
      {
        name: 'magnifier',
        icon: 'search',
        title: 'Magnifier',
        type: 'dropdown',
        value: 'magn',
        dropdownList: [
          // free draw
          {
            // icon:'linear_scale',
            value: 0.5,
            title: '0.5',
            checked: true,
          },
          // rectangle fraw
          {
            // icon:'timeline',
            value: 1,
            title: '1.0',
          },
          {
            // icon:'timeline',
            value: 2,
            title: '2.0',
          },
        ],
        callback: toggleMagnifier,
      },
      // measurement tool
      {
        name: 'measurement',
        icon: 'space_bar',
        title: 'Measurement',
        type: 'check',
        value: 'measure',
        // dropdownList:[
        //   // free draw
        //   {
        //     icon:'linear_scale',
        //     value:'straight',
        //     title:'Line',
        //     checked:true
        //   },
        //   // rectangle draw
        //   {
        //     icon:'timeline',
        //     value:'coordinate',
        //     title:'Coordinate'
        //   }
        // ],
        callback: toggleMeasurement,
      },
      // download TODO
      // {
      //   icon:'file_download',
      //   title:'Download Image',
      //   type:'btn',
      //   value:'download',
      //   callback:imageDownload
      // },
      // share
      // {
      //   icon:'share',
      //   title:'Share View',
      //   type:'btn',
      //   value:'share',
      //   callback:shareURL
      // },
      {
        name: 'sbsviewer',
        icon: 'view_carousel',
        title: 'Side By Side Viewer',
        value: 'dbviewers',
        type: 'check',
        callback: toggleViewerMode,
      }, {
        name: 'viewer',
        icon: 'insert_photo',
        type: 'btn',
        value: 'viewer',
        title: 'Viewer',
        callback: function() {
          if (window.location.search.length > 0) {
            window.location.href = '../viewer/viewer.html' + window.location.search;
          } else {
            window.location.href = '../viewer/viewer.html';
          }
        },
      }, {
        name: 'bugs',
        icon: 'bug_report',
        title: 'Bug Report',
        value: 'bugs',
        type: 'btn',
        callback: ()=>{
          window.open('https://goo.gl/forms/mgyhx4ADH0UuEQJ53', '_blank').focus();
        },
      },

    ],
  });

  // create two side menus for tools
  $UI.settingsSideMenu = new SideMenu({
    id: 'settings_menu',
    width: 300,
    // , isOpen:true
    callback: toggleHeatMapSettings,
  });
  const title = document.createElement('div');
  title.classList.add('item_head');
  title.textContent = 'Heatmap Settings';
  $UI.settingsSideMenu.addContent(title);

  var checkIsReady = setInterval(function() {
    if ($CAMIC &&
      $CAMIC.viewer &&
      $CAMIC.viewer.imagingHelper &&
      $CAMIC.viewer.imagingHelper._haveImage &&
      $D.heatMapData &&
      $D.editedDataClusters) {
      clearInterval(checkIsReady);
      // create editor side menu
      $UI.editorSideMenu = new SideMenu({
        id: 'editor_menu',
        width: 300,
        // , isOpen:true
        callback: toggleHeatMapEditor,
      });
      const title = document.createElement('div');
      title.classList.add('item_head');
      title.textContent = 'Heatmap Editor';
      $UI.editorSideMenu.addContent(title);

      // create edited data list side menu
      $UI.editedListSideMenu = new SideMenu({
        id: 'edit_list_menu',
        width: 300,
        // , isOpen:true
        callback: toggleHeatMapEditor,
      });
      const title1 = document.createElement('div');
      title1.classList.add('item_head');
      title1.textContent = 'Edited Data List';
      $UI.editedListSideMenu.addContent(title1);

      $CAMIC.viewer.canvasDrawInstance.drawMode = 'grid';
      function getGridSizeInImage(size) {
        const correctValue = 0.1;
        let [w, h] = size;
        w = w*$CAMIC.viewer.imagingHelper.imgWidth;
        h = h*$CAMIC.viewer.imagingHelper.imgHeight;
        if (w > 400) return [w, h];
        return [w - correctValue, h - correctValue];
      }
      $CAMIC.viewer.canvasDrawInstance.size = getGridSizeInImage($D.heatMapData.provenance.analysis.size);
    }
  });
}

function setEditedDataClusters(editData) {
  editData.forEach((d)=>{
    const cluster = new EditDataCollection(d.index, d.name, d.value, d.color, d.data);
    $D.editedDataClusters.addCluster(cluster);
  });
}

function redirect(url, text = '', sec = 5) {
  let timer = sec;
  if (!timer) {
    window.location.href = url;
  }
  setInterval(function() {
    if (!timer) {
      window.location.href = url;
    }
    if (Loading.instance&&Loading.instance.parentNode) {
      Loading.text.textContent = `${text} ${timer}s.`;
    } else {
      Loading.open(document.body, `${text} ${timer}s.`);
    }
    // Hint Message for clients that page is going to redirect to Flex table in 5s
    timer--;
  }, 1000);
}
function createThreshold() {
  const div = document.createElement('div');
  div.classList.add('hmep-container');
  div.style.border = 'none';
  const div1 = document.createElement('div');
  div1.classList.add('btn-panel');

  const btn = document.createElement('button');
  btn.classList.add('action');
  btn.style.float = 'right';
  btn.textContent = 'Save';
  div.appendChild(div1);
  div1.appendChild(btn);
  btn.addEventListener('click', onUpdateHeatmapFields);
  return div;
}
function createExportEditData() {
  const div = document.createElement('div');
  div.classList.add('hmep-container');
  div.style.border = 'none';
  const div1 = document.createElement('div');
  div1.classList.add('btn-panel');

  const btn = document.createElement('button');
  btn.classList.add('action');
  btn.style.float = 'right';
  btn.textContent = 'Export Edit Data';
  div.appendChild(div1);
  div1.appendChild(btn);
  btn.addEventListener('click', onExportEditData);
  return div;
}
function getUserId() {
  const token = getCookie('token');
  const tokenData = parseJwt(token);
  return tokenData.name;
}
<commit_msg>try to move check up<commit_after>// CAMIC is an instance of camicroscope core
// $CAMIC in there
let $CAMIC = null;
let $minorCAMIC = null;
// for all instances of UI components
const $UI = new Map();

const $D = {
  pages: {
    home: '../table.html',
    table: '../table.html',
  },
  params: null, // parameter from url - slide Id and status in it (object).
  overlayers: null, // array for each layers
  templates: null, // json schema for prue-form
};

// initialize viewer page
function initialize() {
  var checkPackageIsReady = setInterval(function() {
    if (IsPackageLoading) {
      clearInterval(checkPackageIsReady);
      // init UI -- some of them need to wait data loader to load data
      // because UI components need data to initialize
      initUIcomponents();
      // create a viewer and set up
      initCore();
    }
  }, 100);
  // loading the form template data
  // FormTempaltesLoader();

  // loading the overlayers data
  // OverlayersLoader();
}

// setting core functionalities
function initCore() {
  // start initial
  // TODO zoom info and mmp
  const opt = {};
  // set states if exist
  if ($D.params.states) {
    opt.states = $D.params.states;
  }
  // create the message queue
  $UI.message = new MessageQueue();
  try {
    const slideQuery = {};
    slideQuery.id = $D.params.slideId;
    slideQuery.name = $D.params.slide;
    slideQuery.location = $D.params.location;
    $CAMIC = new CaMic('main_viewer', slideQuery, opt);
  } catch (error) {
    Loading.close();
    $UI.message.addError('Core Initialization Failed');
    console.error(error);
    return;
  }

  $CAMIC.loadImg(async function(e) {
    Loading.open(document.body, `Loading Data ...`);
    // image loaded
    if (e.hasError) {
      $UI.message.addError(e.message);
      Loading.close();
    } else {
      $D.params.data = e;
      // loading heatmap data
      $D.heatMapData = await $CAMIC.store.getHeatmap($D.params.slideId, $D.params.execId).then((d)=> d[0]);
      const slide = $D.params.slideId;
      const exec = $D.heatMapData.provenance.analysis.execution_id;

      if (ImgloaderMode!='imgbox') {
        // query from DB
        const editData = await $CAMIC.store.findHeatmapEdit(getUserId(), slide, exec).then((d)=>d[0]);
        // const editData = await $CAMIC.store.findHeatmapEdit('test', slide, exec).then(d=>d[0]);
        $D.editedDataClusters = new EditDataCluster();
        if (editData&&Array.isArray(editData.data)&&editData.data.length > 0) {
          setEditedDataClusters(editData.data);
        }
      } else {
        $D.editedDataClusters = new EditDataCluster();
      }

      if (!$D.heatMapData) {
        redirect($D.pages.table, `No Heatmap's Data Found. Redirecting To Table.`);
      }

      $UI.slideInfos = new CaMessage({
      /* opts that need to think of*/
        id: 'cames',
        defaultText: `Slide: ${$D.params.data.name}`,
      });

      // spyglass
      $UI.spyglass = new Spyglass({
        targetViewer: $CAMIC.viewer,
        imgsrc: $D.params.data.url,
      });
    }
  });

  $CAMIC.viewer.addHandler('open', function() {
    Loading.open(document.body, `Loading Data ...`);
    // load the heatmap data

    var checkImagingHelperIsReady = setInterval(function() {
      if ($CAMIC.viewer.imagingHelper &&
        $CAMIC.viewer.imagingHelper._haveImage &&
        $D.heatMapData &&
        $D.editedDataClusters &&
        $UI.editedListSideMenu) {
        clearInterval(checkImagingHelperIsReady);

        const opt = {
          hasCover: true,
          opacity: .65, // inputs[2].value,
          coverOpacity: .3,
          data: $D.heatMapData.data,
          editedData: $D.editedDataClusters,
          size: $D.heatMapData.provenance.analysis.size,
          fields: $D.heatMapData.provenance.analysis.fields,
          color: '#1034A6', // inputs[3].value
        };

        if (
          $D.heatMapData.provenance.analysis.setting &&
          $D.heatMapData.provenance.analysis.setting.colors &&
          $D.heatMapData.provenance.analysis.setting.colors.length) {
          opt.mode = $D.heatMapData.provenance.analysis.setting.mode;
          if (opt.mode === 'binal') {
            opt.color = $D.heatMapData.provenance.analysis.setting.colors[0];
          } else if (opt.mode === 'gradient') {
            opt.colors = $D.heatMapData.provenance.analysis.setting.colors;
            opt.steps = $D.heatMapData.provenance.analysis.setting.colors.length;
          }
          if ($D.heatMapData.provenance.analysis.setting.field) {
            opt.currentFieldName = $D.heatMapData.provenance.analysis.setting.field;
          }
        }
        $CAMIC.viewer.createHeatmap(opt);

        // create heatmap control
        $UI.heatmapcontrol = new HeatmapControl({
          mode: opt.mode,
          currentField: opt.currentFieldName,
          fields: $D.heatMapData.provenance.analysis.fields,
          opacities: [{
            name: 'heat',
            value: 0.65,
          }, {
            name: 'cover',
            value: 0.3,
          }],
          onChange: heatmapSettingChanged,
          onOpacityChange: heatmapOpacityChanaged,
        });

        $UI.settingsSideMenu.addContent($UI.heatmapcontrol.elt);

        // create heatmap editor

        $UI.heatmapEditorPanel = new HeatmapEditorPanel({
          fields: $D.heatMapData.provenance.analysis.fields,
          // editedDate:$D.
          onFieldChange: editorPenChange,
          onReset: function() {
            if (confirm('Do You Want To Clear Edited Data?')) {
              $CAMIC.viewer.canvasDrawInstance.clear();
            }
          }, // clearEditData
          onSave: saveEditData,
        });
        $UI.editorSideMenu.addContent($UI.heatmapEditorPanel.elt);

        // create edited data list
        $UI.heatmapEditedDataPanel = new HeatmapEditedDataPanel({
          // data:$D.heatMapData.editedClusters,
          data: $D.editedDataClusters,
          // editedDate:$D.
          onDBClick: locateEditData,
          onDelete: onDeleteEditData,
        });

        $UI.editedListSideMenu.addContent($UI.heatmapEditedDataPanel.elt);

        // TODO create save botton if user is admin
        if (ImgloaderMode!='imgbox') {
          const btnDiv = createThreshold();
          $UI.settingsSideMenu.addContent(btnDiv);
        } else {
          const btnDiv = createExportEditData();
          $UI.editedListSideMenu.addContent(btnDiv);
        }

        Loading.close();
      }
    }, 500);
  });
}

// initialize all UI components
function initUIcomponents() {
  /* create UI components */

  // create the tool bar
  $UI.toolbar = new CaToolbar({
  /* opts that need to think of*/
    id: 'ca_tools',
    zIndex: 601,
    hasMainTools: false,
    // mainToolsCallback:mainMenuChange,
    subTools: [
      {
        name: 'editeddate',
        icon: 'view_list', // material icons' name
        title: 'Edited Data List',
        type: 'check', // btn/check/dropdown
        value: 'editeddate',
        callback: toggleHeatMapDataList,
      },
      // setting
      {
        name: 'settings',
        icon: 'settings', // material icons' name
        title: 'Settings',
        type: 'check', // btn/check/dropdown
        value: 'settings',
        callback: toggleHeatMapSettings,
      }, {
        name: 'editor',
        icon: 'create', // material icons' name
        title: 'editor',
        type: 'check', // btn/check/dropdown
        value: 'editor',
        callback: toggleHeatMapEditor,
      },
      // home
      // {
      //   icon:'home',// material icons' name
      //   title:'Home',
      //   type:'btn',// btn/check/dropdown
      //   value:'home',
      //   callback:goHome
      // },
      //
      // pen
      // {
      //   icon:'create',// material icons' name
      //   title:'Draw',
      //   type:'multistates',
      //   callback:draw
      // },
      // magnifier
      {
        name: 'magnifier',
        icon: 'search',
        title: 'Magnifier',
        type: 'dropdown',
        value: 'magn',
        dropdownList: [
          // free draw
          {
            // icon:'linear_scale',
            value: 0.5,
            title: '0.5',
            checked: true,
          },
          // rectangle fraw
          {
            // icon:'timeline',
            value: 1,
            title: '1.0',
          },
          {
            // icon:'timeline',
            value: 2,
            title: '2.0',
          },
        ],
        callback: toggleMagnifier,
      },
      // measurement tool
      {
        name: 'measurement',
        icon: 'space_bar',
        title: 'Measurement',
        type: 'check',
        value: 'measure',
        // dropdownList:[
        //   // free draw
        //   {
        //     icon:'linear_scale',
        //     value:'straight',
        //     title:'Line',
        //     checked:true
        //   },
        //   // rectangle draw
        //   {
        //     icon:'timeline',
        //     value:'coordinate',
        //     title:'Coordinate'
        //   }
        // ],
        callback: toggleMeasurement,
      },
      // download TODO
      // {
      //   icon:'file_download',
      //   title:'Download Image',
      //   type:'btn',
      //   value:'download',
      //   callback:imageDownload
      // },
      // share
      // {
      //   icon:'share',
      //   title:'Share View',
      //   type:'btn',
      //   value:'share',
      //   callback:shareURL
      // },
      {
        name: 'sbsviewer',
        icon: 'view_carousel',
        title: 'Side By Side Viewer',
        value: 'dbviewers',
        type: 'check',
        callback: toggleViewerMode,
      }, {
        name: 'viewer',
        icon: 'insert_photo',
        type: 'btn',
        value: 'viewer',
        title: 'Viewer',
        callback: function() {
          if (window.location.search.length > 0) {
            window.location.href = '../viewer/viewer.html' + window.location.search;
          } else {
            window.location.href = '../viewer/viewer.html';
          }
        },
      }, {
        name: 'bugs',
        icon: 'bug_report',
        title: 'Bug Report',
        value: 'bugs',
        type: 'btn',
        callback: ()=>{
          window.open('https://goo.gl/forms/mgyhx4ADH0UuEQJ53', '_blank').focus();
        },
      },

    ],
  });

  // create two side menus for tools
  $UI.settingsSideMenu = new SideMenu({
    id: 'settings_menu',
    width: 300,
    // , isOpen:true
    callback: toggleHeatMapSettings,
  });
  const title = document.createElement('div');
  title.classList.add('item_head');
  title.textContent = 'Heatmap Settings';
  $UI.settingsSideMenu.addContent(title);

  var checkIsReady = setInterval(function() {
    if ($CAMIC &&
      $CAMIC.viewer &&
      $CAMIC.viewer.imagingHelper &&
      $CAMIC.viewer.imagingHelper._haveImage &&
      $D.heatMapData &&
      $D.editedDataClusters) {
      clearInterval(checkIsReady);
      // create editor side menu
      $UI.editorSideMenu = new SideMenu({
        id: 'editor_menu',
        width: 300,
        // , isOpen:true
        callback: toggleHeatMapEditor,
      });
      const title = document.createElement('div');
      title.classList.add('item_head');
      title.textContent = 'Heatmap Editor';
      $UI.editorSideMenu.addContent(title);

      // create edited data list side menu
      $UI.editedListSideMenu = new SideMenu({
        id: 'edit_list_menu',
        width: 300,
        // , isOpen:true
        callback: toggleHeatMapEditor,
      });
      const title1 = document.createElement('div');
      title1.classList.add('item_head');
      title1.textContent = 'Edited Data List';
      $UI.editedListSideMenu.addContent(title1);

      $CAMIC.viewer.canvasDrawInstance.drawMode = 'grid';
      function getGridSizeInImage(size) {
        const correctValue = 0.1;
        let [w, h] = size;
        w = w*$CAMIC.viewer.imagingHelper.imgWidth;
        h = h*$CAMIC.viewer.imagingHelper.imgHeight;
        if (w > 400) return [w, h];
        return [w - correctValue, h - correctValue];
      }
      $CAMIC.viewer.canvasDrawInstance.size = getGridSizeInImage($D.heatMapData.provenance.analysis.size);
    }
  });
}

function setEditedDataClusters(editData) {
  editData.forEach((d)=>{
    const cluster = new EditDataCollection(d.index, d.name, d.value, d.color, d.data);
    $D.editedDataClusters.addCluster(cluster);
  });
}

function redirect(url, text = '', sec = 5) {
  let timer = sec;
  if (!timer) {
    window.location.href = url;
  }
  setInterval(function() {
    if (!timer) {
      window.location.href = url;
    }
    if (Loading.instance&&Loading.instance.parentNode) {
      Loading.text.textContent = `${text} ${timer}s.`;
    } else {
      Loading.open(document.body, `${text} ${timer}s.`);
    }
    // Hint Message for clients that page is going to redirect to Flex table in 5s
    timer--;
  }, 1000);
}
function createThreshold() {
  const div = document.createElement('div');
  div.classList.add('hmep-container');
  div.style.border = 'none';
  const div1 = document.createElement('div');
  div1.classList.add('btn-panel');

  const btn = document.createElement('button');
  btn.classList.add('action');
  btn.style.float = 'right';
  btn.textContent = 'Save';
  div.appendChild(div1);
  div1.appendChild(btn);
  btn.addEventListener('click', onUpdateHeatmapFields);
  return div;
}
function createExportEditData() {
  const div = document.createElement('div');
  div.classList.add('hmep-container');
  div.style.border = 'none';
  const div1 = document.createElement('div');
  div1.classList.add('btn-panel');

  const btn = document.createElement('button');
  btn.classList.add('action');
  btn.style.float = 'right';
  btn.textContent = 'Export Edit Data';
  div.appendChild(div1);
  div1.appendChild(btn);
  btn.addEventListener('click', onExportEditData);
  return div;
}
function getUserId() {
  const token = getCookie('token');
  const tokenData = parseJwt(token);
  return tokenData.name;
}
<|endoftext|>


<commit_before>var fs = require("fs");
var List = require("./vm-utils/linked-list.js");

var inputFile = process.argv[2] || "test-output.json";
var inputIsAST = !! inputFile.match(/.json$/);

fs.readFile(inputFile, "utf8", function (err, source) {
	if (err) throw err;
	var code;
	if (inputIsAST) {
		code = JSON.parse(source);
	}
	else {
		throw Error("input must be JSON");
		//var parser = require("./lang.js");
		//code = parser.parse(source);
	}
	out("returned ", evl(code));
	//out("returned " + evl(["print", ["List"]]));
});

function out () {
	var args = Array.prototype.slice.call(arguments);
	console.log.apply(console, ["VM:"].concat(args));
}

function lispify (exp) {
	if (exp instanceof Array) {
		return "(" + exp.map(lispify).join(" ") + ")";
	}
	return "" + exp;
}

function cloneArray (arr) {
	var clone = [];
	for (var i = 0; i < arr.length; i++) {
		clone.push(arr[i]);
	}
	return clone;
}

function Message (type, data) {
	this.type = type;
	this.data = data;
}

var globalNS = {
	print: argArrayWrap(function () {
		var s = ([]).join.call(arguments, " ");
		console.log(s);
		return s;
	}),
	"var": argArrayWrap(function (name, value) {
		return assignVariable(name, value, "declaration");
	}),
	"=": argArrayWrap(assignVariable),
	"==": argArrayWrap(function (a, b) {
		if (a instanceof List && b instanceof List) {
			return List.equal(a, b);
		}
		return a === b;
	}),
	"!=": argArrayWrap(function (a, b) {
		if (a instanceof List && b instanceof List) {
			return !List.equal(a, b);
		}
		return a !== b;
	}),
	"!": argArrayWrap(function (a) {
		return !a;
	}),
	"^": argArrayWrap(function (a, b) {
		return Math.pow(a, b);
	}),
	"++": argArrayWrap(function (a, b) {
		if (a instanceof List && b instanceof List) {
			return a.concat(b);
		}
		return ("" + a) + b;
	}),
	"if": argArrayWrap(function (condition, ifBody, elseBody, inTailPosition) {
		if (evl(condition)) {
			return evl(ifBody, inTailPosition);
		}
		else if (elseBody !== undefined) {
			return evl(elseBody, inTailPosition);
		}
		return false;
	}),
	"while": argArrayWrap(function (condition, body) {
		var result = false;
		while (evl(condition)) {
			result = evl(body);
			if (result instanceof Message) {
				if (result.type === "break") {
					return result.data;
				}
				else if (result.type === "next") {
					result = result.data;
				}
				else if (result.type === "return") {
					return result;
				}
			}
		}
		return result;
	}),
	"for": argArrayWrap(function (loopVar, collection, body) {
		var result;
		var results = [];
		var current = list = evl(collection);
		var indexName, valueName;
		var i = 0;
		if (loopVar instanceof Array) {
			// index and value named
			indexName = loopVar[0];
			valueName = loopVar[1];
		}
		else {
			// just value named
			valueName = loopVar;
		}
		while (current) {
			// create loop scope
			var loopScope = {};
			loopScope[valueName] = current.head;
			if (indexName) {
				// set named array index
				loopScope[indexName] = i;
				i += 1;
			}
			scopes.push(loopScope);
			// push loop
			result = evl(body);
			if (result instanceof Message) {
				if (result.type === "break") {
					results.push(result.data);
					scopes.pop();
					break;
				}
				else if (result.type === "return") {
					scopes.pop();
					return result.data;
				}
				else if (result.type === "next") {
					result = result.data;
				}
			}
			results.push(result);
			scopes.pop();
			current = current.tail;
		}
		var resultList = List.fromArray(results);
		return resultList;
	}),
	"break": argArrayWrap(function (value) {
		return new Message("break", value);
	}),
	"return": argArrayWrap(function (value) {
		return new Message("return", value);
	}),
	"next": argArrayWrap(function (value) {
		return new Message("next", value);
	}),
	"->": argArrayWrap(function (argNames, body) {
		var func = function () {
			var args = arguments;
			var callingFunction = currentFunction;
			currentFunction = func;
			// save calling scope to return to after the function call
			var oldScopes = scopes;
			var tcoCount = 0;
			do {
				//console.log("TCO Count:", tcoCount++);
				scopes = func.closure;
				scopes.push({});
				for (var i = 0; i < argNames.length; i++) {
					//console.log(argNames[i], "=", args[i]);
					assignVariable(argNames[i], args[i], "declaration", "don't eval");
				}
				var result = evl(body, true);
				scopes.pop();
				// for next iteration of TCO
				if (result instanceof Message) {
					if (result.type === "tailCall") {
						args = result.data;
						continue;
					}
					if (result.type === "return") {
						result = result.data;
					}
				}
				break;
			} while (true);
			scopes = oldScopes;
			currentFunction = callingFunction;
			return result;
		};
		func.closure = cloneArray(scopes);
		return func;
	}),
	"List": argArrayWrap(function () {
		return List.fromArray(arguments);
	}),
	"List:compose": argArrayWrap(function (heads, tail) {
		var list = evl(tail);
		if (!(list instanceof List)) {
			throw Error("tail of a list must be a list. Variable '" + tail + "' is not.");
		}
		for (var i = heads.length - 1; i >= 0; i--) {
			list = list.add(evl(heads[i]));
		}
		return list;
	}),
	head: argArrayWrap(function (list) {
		return list.head;
	}),
	tail: argArrayWrap(function (list) {
		return list.tail;
	}),
	length:  argArrayWrap(function (list) {
		return list.length;
	}),
	ensure: argArrayWrap(function () {
		for (var i = 0; i < arguments.length; i++) {
			if (!evl(arguments[i])) {
				if (arguments[i] === "==") {
					console.log(lispify(arguments[i][1]) + " => " + evl(arguments[i][1]));
					console.log(lispify(arguments[i][2]) + " => " + evl(arguments[i][2]));
				}
				throw Error("Check failed: " + lispify(arguments[i]));
			}
		}
		return true;
	}),
	show_scopes: function () {
		out(JSON.stringify(scopes));
	},
	timer: function () {
		var start = Date.now();
		return function () {
			return Date.now() - start;
		}
	}
};

function argArrayWrap (func) {
	func.isWrapped = true;
	return func;
}

[
	"var",
	"=",
	"if",
	"while",
	"for",
	"->",
	"List:compose",
	"ensure",
].forEach(function (command) {
	globalNS[command].dontEval = true;
});
globalNS["let"] = globalNS["var"];
globalNS["def"] = globalNS["var"];

var scopes = [globalNS];
var currentFunction, tailCall = false;

function getVal (identifier) {
	for (var i = scopes.length - 1; i >= 0; i--) {
		if (identifier in scopes[i]) {
			return scopes[i][identifier];
		}
	}
	throw Error("The variable '" + identifier + "' does not exist");
}

function setVal (identifier, val) {
	for (var i = scopes.length - 1; i >= 0; i--) {
		if (scopes[i][identifier] !== undefined) {
			scopes[i][identifier] = val;
			return val;
		}
	}
	scopes[scopes.length - 1][identifier] = val;
	return val;
}

function assignVariable (left, right, declaration, dontEval) {
	var i;
	var result = ((right === undefined  || dontEval) ? right : evl(right));
	var topScope = scopes[scopes.length - 1];
	if (left instanceof Array) {
		var current = result;
		if (left[0] === "List") {
			for (i = 1; i < left.length; i++) {
				if (declaration) {
					// declaring new variables
					if (current) {
						topScope[left[i]] = current.head;
						current = current.tail;
					}
					else {
						topScope[left[i]] = undefined;
					}
				}
				else {
					// assigning existing variables
					getVal(left[i]);
					if (current) {
						setVal(left[i], current.head);
						current = current.tail;
					}
					else {
						setVal(left[i], undefined);
					}
				}
			}
		}
		else if (left[0] === "List:compose") {
			var heads = left[1];
			for (i = 0; i < heads.length; i++) {
				if (declaration) {
					// declaring new variables
					if (current) {
						topScope[heads[i]] = current.head;
						current = current.tail;
					}
					else {
						topScope[heads[i]] = undefined;
					}
				}
				else {
					// assigning existing variables
					getVal(heads[i]);
					if (current) {
						setVal(heads[i], current.head);
						current = current.tail;
					}
					else {
						setVal(heads[i], undefined);
					}
				}
			}
			if (declaration) {
				topScope[left[2]] = current;
			}
			else {
				setVal(left[2], current);
			}
		}
		else {
			throw Error("invalid left side in assignment");
		}
		return result;
	}
	if (declaration) {
		return topScope[left] = result;
	}
	else {
		getVal(left);
		return setVal(left, result);
	}
}

["+", "-", "*", "/", "%", "<", ">", ">=", "<=", "&&", "||"].forEach(function (op) {
	var body = "return a " + op + " b";
	globalNS[op] = argArrayWrap(new Function("a", "b", body));
});

function evl (exp, inTailPosition) {
	if (exp instanceof Array) {
		// evaluate block expressions in tern and return the last
		if (exp[0] === "do") {
			scopes.push({});
			var result;
			// evaluate each expression in the block
			for (var i = 1; i < exp.length - 1; i++) {
				result = evl(exp[i]);
				if (result instanceof Message) {
					scopes.pop();
					return result;
				}
			}
			result = evl(exp[i], inTailPosition);
			scopes.pop();
			return result;
		}
		else {
			var args, func = evl(exp[0]);
			if (!func.isWrapped && func.length > 0) {
				throw Error("unwrapped function: " + exp[0]);
			}
			if (func.dontEval) {
				// special case for "if" to add tail position flag
				if(exp[0] === "if" && inTailPosition) {
					return func(exp[1], exp[2], exp[3], inTailPosition);
				}

				// Call the function to evaluate the expression
				switch (exp.length) {
					case 1:  return func();
					case 2:  return func(exp[1]);
					case 3:  return func(exp[1], exp[2]);
					case 4:  return func(exp[1], exp[2], exp[3]);
					case 5:  return func(exp[1], exp[2], exp[3], exp[4]);
					case 6:  return func(exp[1], exp[2], exp[3], exp[4], exp[6]);
					default: return func.apply(null, exp.slice(1));
				}
			}
			else {
				if (inTailPosition) {
					// return arguments for tail call
					if (func === currentFunction) {
						// evaluate each argument
						args = [];
						for (var i = 1; i < exp.length; i++) {
							args.push(evl(exp[i]));
						}
						return new Message("tailCall", args);
					}
				}
				// call the function
				if (typeof func !== 'function') {
					console.log("not a function: ", func);
					console.log("in expression: ", exp);
					process.exit();
				}
				switch (exp.length) {
					case 1:  return func();
					case 2:  return func(evl(exp[1]));
					case 3:  return func(evl(exp[1]), evl(exp[2]));
					case 4:  return func(evl(exp[1]), evl(exp[2]),
						evl(exp[3]));
					case 5:  return func(evl(exp[1]), evl(exp[2]),
						evl(exp[3]), evl(exp[4]));
					case 6:  return func(evl(exp[1]), evl(exp[2]),
						evl(exp[3]), evl(exp[4]), evl(exp[5]));
					default: return func.apply(null, exp.slice(1));
				}
			}

		}
	}
	// boolean literals
	else if (exp === true || exp === false) {
		return exp;
	}
	// number literals
	else if (typeof exp === "number") {
			return exp;
	}
	// string literals
	else if (exp.charAt(0) === "'") {
		return exp.substr(1, exp.length - 2);
	}
	// dereference variables
	else {
		return getVal(exp);
	}
}<commit_msg>cleaned up metafunction definitions<commit_after>var fs = require("fs");
var List = require("./vm-utils/linked-list.js");

var inputFile = process.argv[2] || "test-output.json";
var inputIsAST = !! inputFile.match(/.json$/);

fs.readFile(inputFile, "utf8", function (err, source) {
	if (err) throw err;
	var code;
	if (inputIsAST) {
		code = JSON.parse(source);
	}
	else {
		throw Error("input must be JSON");
		//var parser = require("./lang.js");
		//code = parser.parse(source);
	}
	out("returned ", evl(code));
	//out("returned " + evl(["print", ["List"]]));
});

function out () {
	var args = Array.prototype.slice.call(arguments);
	console.log.apply(console, ["VM:"].concat(args));
}

function lispify (exp) {
	if (exp instanceof Array) {
		return "(" + exp.map(lispify).join(" ") + ")";
	}
	return "" + exp;
}

function cloneArray (arr) {
	var clone = [];
	for (var i = 0; i < arr.length; i++) {
		clone.push(arr[i]);
	}
	return clone;
}

function Message (type, data) {
	this.type = type;
	this.data = data;
}

// mark a function as a meta-function meaning its
// arguments are not evaluated before it is called
function meta (func) {
	func.isMeta = true;
	return func;
}

var globalNS = {
	print: function () {
		var s = ([]).join.call(arguments, " ");
		console.log(s);
		return s;
	},
	"var": meta(function (name, value) {
		return assignVariable(name, value, "declaration");
	}),
	"=": meta(assignVariable),
	"==": function (a, b) {
		if (a instanceof List && b instanceof List) {
			return List.equal(a, b);
		}
		return a === b;
	},
	"!=": function (a, b) {
		if (a instanceof List && b instanceof List) {
			return !List.equal(a, b);
		}
		return a !== b;
	},
	"!": function (a) {
		return !a;
	},
	"||": meta(function (a, b) {
		return evl(a) || evl(b);
	}),
	"&&": meta(function (a, b) {
		return evl(a) && evl(b);
	}),
	"^": function (a, b) {
		return Math.pow(a, b);
	},
	"++": function (a, b) {
		if (a instanceof List && b instanceof List) {
			return a.concat(b);
		}
		return ("" + a) + b;
	},
	"if": meta(function (condition, ifBody, elseBody, inTailPosition) {
		if (evl(condition)) {
			return evl(ifBody, inTailPosition);
		}
		else if (elseBody !== undefined) {
			return evl(elseBody, inTailPosition);
		}
		return false;
	}),
	"while": meta(function (condition, body) {
		var result = false;
		while (evl(condition)) {
			result = evl(body);
			if (result instanceof Message) {
				if (result.type === "break") {
					return result.data;
				}
				else if (result.type === "next") {
					result = result.data;
				}
				else if (result.type === "return") {
					return result;
				}
			}
		}
		return result;
	}),
	"for": meta(function (loopVar, collection, body) {
		var result;
		var results = [];
		var current = list = evl(collection);
		var indexName, valueName;
		var i = 0;
		if (loopVar instanceof Array) {
			// index and value named
			indexName = loopVar[0];
			valueName = loopVar[1];
		}
		else {
			// just value named
			valueName = loopVar;
		}
		while (current) {
			// create loop scope
			var loopScope = {};
			loopScope[valueName] = current.head;
			if (indexName) {
				// set named array index
				loopScope[indexName] = i;
				i += 1;
			}
			scopes.push(loopScope);
			// push loop
			result = evl(body);
			if (result instanceof Message) {
				if (result.type === "break") {
					results.push(result.data);
					scopes.pop();
					break;
				}
				else if (result.type === "return") {
					scopes.pop();
					return result.data;
				}
				else if (result.type === "next") {
					result = result.data;
				}
			}
			results.push(result);
			scopes.pop();
			current = current.tail;
		}
		var resultList = List.fromArray(results);
		return resultList;
	}),
	"break": function (value) {
		return new Message("break", value);
	},
	"return": function (value) {
		return new Message("return", value);
	},
	"next": function (value) {
		return new Message("next", value);
	},
	"->": meta(function (argNames, body) {
		var func = function () {
			var args = arguments;
			var callingFunction = currentFunction;
			currentFunction = func;
			// save calling scope to return to after the function call
			var oldScopes = scopes;
			var tcoCount = 0;
			do {
				//console.log("TCO Count:", tcoCount++);
				scopes = func.closure;
				scopes.push({});
				for (var i = 0; i < argNames.length; i++) {
					//console.log(argNames[i], "=", args[i]);
					assignVariable(argNames[i], args[i], "declaration", "don't eval");
				}
				var result = evl(body, true);
				scopes.pop();
				// for next iteration of TCO
				if (result instanceof Message) {
					if (result.type === "tailCall") {
						args = result.data;
						continue;
					}
					if (result.type === "return") {
						result = result.data;
					}
				}
				break;
			} while (true);
			scopes = oldScopes;
			currentFunction = callingFunction;
			return result;
		};
		func.closure = cloneArray(scopes);
		return func;
	}),
	"List": function () {
		return List.fromArray(arguments);
	},
	"List:compose": meta(function (heads, tail) {
		var list = evl(tail);
		if (!(list instanceof List)) {
			throw Error("tail of a list must be a list. Variable '" + tail + "' is not.");
		}
		for (var i = heads.length - 1; i >= 0; i--) {
			list = list.add(evl(heads[i]));
		}
		return list;
	}),
	head: function (list) {
		return list.head;
	},
	tail: function (list) {
		return list.tail;
	},
	length: function (list) {
		return list.length;
	},
	ensure: meta(function () {
		for (var i = 0; i < arguments.length; i++) {
			if (!evl(arguments[i])) {
				if (arguments[i] === "==") {
					console.log(lispify(arguments[i][1]) + " => " + evl(arguments[i][1]));
					console.log(lispify(arguments[i][2]) + " => " + evl(arguments[i][2]));
				}
				throw Error("Check failed: " + lispify(arguments[i]));
			}
		}
		return true;
	}),
	show_scopes: function () {
		out(JSON.stringify(scopes));
	},
	timer: function () {
		var start = Date.now();
		return function () {
			return Date.now() - start;
		}
	}
};
// duplicate var to let and def
// they will behave differently later
globalNS["let"] = globalNS["var"];
globalNS["def"] = globalNS["var"];

var scopes = [globalNS];
var currentFunction, tailCall = false;

function getVal (identifier) {
	for (var i = scopes.length - 1; i >= 0; i--) {
		if (identifier in scopes[i]) {
			return scopes[i][identifier];
		}
	}
	throw Error("The variable '" + identifier + "' does not exist");
}

function setVal (identifier, val) {
	for (var i = scopes.length - 1; i >= 0; i--) {
		if (scopes[i][identifier] !== undefined) {
			scopes[i][identifier] = val;
			return val;
		}
	}
	scopes[scopes.length - 1][identifier] = val;
	return val;
}

function assignVariable (left, right, declaration, dontEval) {
	var i;
	var result = ((right === undefined  || dontEval) ? right : evl(right));
	var topScope = scopes[scopes.length - 1];
	if (left instanceof Array) {
		var current = result;
		if (left[0] === "List") {
			for (i = 1; i < left.length; i++) {
				if (declaration) {
					// declaring new variables
					if (current) {
						topScope[left[i]] = current.head;
						current = current.tail;
					}
					else {
						topScope[left[i]] = undefined;
					}
				}
				else {
					// assigning existing variables
					getVal(left[i]);
					if (current) {
						setVal(left[i], current.head);
						current = current.tail;
					}
					else {
						setVal(left[i], undefined);
					}
				}
			}
		}
		else if (left[0] === "List:compose") {
			var heads = left[1];
			for (i = 0; i < heads.length; i++) {
				if (declaration) {
					// declaring new variables
					if (current) {
						topScope[heads[i]] = current.head;
						current = current.tail;
					}
					else {
						topScope[heads[i]] = undefined;
					}
				}
				else {
					// assigning existing variables
					getVal(heads[i]);
					if (current) {
						setVal(heads[i], current.head);
						current = current.tail;
					}
					else {
						setVal(heads[i], undefined);
					}
				}
			}
			if (declaration) {
				topScope[left[2]] = current;
			}
			else {
				setVal(left[2], current);
			}
		}
		else {
			throw Error("invalid left side in assignment");
		}
		return result;
	}
	if (declaration) {
		return topScope[left] = result;
	}
	else {
		getVal(left);
		return setVal(left, result);
	}
}

["+", "-", "*", "/", "%", "<", ">", ">=", "<="].forEach(function (op) {
	var body = "return a " + op + " b";
	globalNS[op] = new Function("a", "b", body);
});

function evl (exp, inTailPosition) {
	if (exp instanceof Array) {
		// evaluate block expressions in tern and return the last
		if (exp[0] === "do") {
			scopes.push({});
			var result;
			// evaluate each expression in the block
			for (var i = 1; i < exp.length - 1; i++) {
				result = evl(exp[i]);
				if (result instanceof Message) {
					scopes.pop();
					return result;
				}
			}
			result = evl(exp[i], inTailPosition);
			scopes.pop();
			return result;
		}
		else {
			var args, func = evl(exp[0]);
			if (func.isMeta) {
				// special case for "if" to add tail position flag
				if(exp[0] === "if" && inTailPosition) {
					return func(exp[1], exp[2], exp[3], inTailPosition);
				}

				// Call the function to evaluate the expression
				switch (exp.length) {
					case 1:  return func();
					case 2:  return func(exp[1]);
					case 3:  return func(exp[1], exp[2]);
					case 4:  return func(exp[1], exp[2], exp[3]);
					case 5:  return func(exp[1], exp[2], exp[3], exp[4]);
					case 6:  return func(exp[1], exp[2], exp[3], exp[4], exp[6]);
					default: return func.apply(null, exp.slice(1));
				}
			}
			else {
				if (inTailPosition) {
					// return arguments for tail call
					if (func === currentFunction) {
						// evaluate each argument
						args = [];
						for (var i = 1; i < exp.length; i++) {
							args.push(evl(exp[i]));
						}
						return new Message("tailCall", args);
					}
				}
				// call the function
				if (typeof func !== 'function') {
					console.log("not a function: ", func);
					console.log("in expression: ", exp);
					process.exit();
				}
				switch (exp.length) {
					case 1:  return func();
					case 2:  return func(evl(exp[1]));
					case 3:  return func(evl(exp[1]), evl(exp[2]));
					case 4:  return func(evl(exp[1]), evl(exp[2]),
						evl(exp[3]));
					case 5:  return func(evl(exp[1]), evl(exp[2]),
						evl(exp[3]), evl(exp[4]));
					case 6:  return func(evl(exp[1]), evl(exp[2]),
						evl(exp[3]), evl(exp[4]), evl(exp[5]));
					default: return func.apply(null, exp.slice(1));
				}
			}

		}
	}
	// boolean literals
	else if (exp === true || exp === false) {
		return exp;
	}
	// number literals
	else if (typeof exp === "number") {
			return exp;
	}
	// string literals
	else if (exp.charAt(0) === "'") {
		return exp.substr(1, exp.length - 2);
	}
	// dereference variables
	else {
		return getVal(exp);
	}
}<|endoftext|>


<commit_before>var five = require("johnny-five");
var Raspi = require("raspi-io");
var RaspiCam = require("raspicam");

var camera = new RaspiCam({
  mode: "photo",
  output: "picture.jpg",
  timeout: 500
});

var board = new five.Board({
  io: new Raspi()
});

var button;

board.on("ready", () => {
  button = new five.Button({
    pin: "GPIO18",
    isPullup: true
  });

  button.on("down", (value) => {
    var time = new Date().getTime();

    camera.set( "output", "picture_" + time +".jpg" );

    camera.start();

    console.log("Pressed");
  });

  button.on("up", () => {
    console.log("Released");
  });
});

camera.on("read", (error, timestamp, filename) => {
  if(error)
    return;
  console.log("Picture Saved, " + filename);
});

camera.on("exit", (timestamp) => {
  console.log("Finished");
});
<commit_msg>Camara<commit_after>var RaspiCam = require("raspicam");

var camera = new RaspiCam({
  mode: "photo",
  output: "picture.jpg",
  timeout: 500
});

camera.on("read", (error, timestamp, filename) => {
  if(error)
    return;
  console.log("Picture Saved, " + filename);
});

camera.on("exit", (timestamp) => {
  console.log("Finished");
});

camera.start();<|endoftext|>


<commit_before>var newBlock = require('./new-block.js');

var lgLogic = require('./_lg-logic');

/**
 * lost-waffle: Creates a block that is a fraction of the size of its
 * containing element's width AND height with a gutter on the right
 * and bottom.
 *
 * @param {string} [fraction] - This is a simple fraction of the containing
 *   element's width and height.
 *
 * @param {integer} [cycle] - Lost works by assigning a margin-right/bottom
 *   to all elements except the last row (no margin-bottom) and the last
 *   column (no margin-right). It does this by default by using the
 *   denominator of the fraction you pick. To override this default use this
 *   param., e.g.: .foo { lost-waffle: 2/4 2; }
 *
 * @param {length} [gutter] - The margin on the right and bottom side of the
 *   element used to create a gutter. Typically this is left alone and the
 *   global $gutter will be used, but you can override it here if you want
 *   certain elements to have a particularly large or small gutter (pass 0
 *   for no gutter at all).
 *
 * @param {string} [flex|no-flex] - Determines whether this element should
 *   use Flexbox or not.
 *
 * @example
 *   section {
 *     height: 100%;
 *   }
 *   div {
 *     lost-waffle: 1/3;
 *   }
 */
module.exports = function lostWaffleDecl(css, settings) {
  css.walkDecls('lost-waffle', function lostWaffleDeclFunction(decl) {
    var declArr = [];
    var lostWaffle;
    var lostWaffleCycle;
    var lostWaffleRounder = settings.rounder;
    var lostWaffleGutter = settings.gutter;
    var lostWaffleFlexbox = settings.flexbox;

    function cloneAllBefore(props) {
      Object.keys(props).forEach(function traverseProps(prop) {
        decl.cloneBefore({
          prop: prop,
          value: props[prop]
        });
      });
    }

    if (settings.cycle === 'auto') {
      lostWaffleCycle = decl.value.split('/')[1];
    } else {
      lostWaffleCycle = settings.cycle;
    }

    declArr = decl.value.split(' ');
    lostWaffle = declArr[0];

    if (declArr[1] !== undefined && declArr[1].search(/^\d/) !== -1) {
      lostWaffleCycle = declArr[1];
    }

    if (declArr[1] === 'flex' || declArr[1] === 'no-flex' || declArr[1] === 'auto') {
      lostWaffleCycle = declArr[0].split('/')[1];
    }

    if (declArr[2] !== undefined && declArr[2].search(/^\d/) !== -1) {
      lostWaffleGutter = declArr[2];
    }

    if (declArr.indexOf('flex') !== -1) {
      lostWaffleFlexbox = 'flex';
    }

    if (declArr.indexOf('no-flex') !== -1) {
      lostWaffleFlexbox = 'no-flex';
    }

    decl.parent.nodes.forEach(function lostWaffleRounderFunction(declaration) {
      if (declaration.prop === 'lost-waffle-rounder') {
        lostWaffleRounder = declaration.value;
        declaration.remove();
      }
    });

    decl.parent.nodes.forEach(function lostWaffleCycleFunction(declaration) {
      if (declaration.prop === 'lost-waffle-cycle') {
        lostWaffleCycle = declaration.value;

        declaration.remove();
      }
    });

    decl.parent.nodes.forEach(function lostWaffleGutterFunction(declaration) {
      if (declaration.prop === 'lost-waffle-gutter') {
        lostWaffleGutter = declaration.value;

        declaration.remove();
      }
    });

    decl.parent.nodes.forEach(function lostWaffleFlexboxFunction(declaration) {
      if (declaration.prop === 'lost-waffle-flexbox') {
        if (declaration.prop === 'flex') {
          lostWaffleFlexbox = 'flex';
        }

        declaration.remove();
      }
    });

    if (lostWaffleFlexbox === 'flex') {
      decl.cloneBefore({
        prop: 'flex',
        value: '0 0 auto'
      });

      if (lostWaffleCycle !== 0) {
        newBlock(
          decl,
          ':nth-last-child(-n + ' + lostWaffleCycle + ')',
          ['margin-bottom'],
          [0]
        );

        newBlock(
          decl,
          ':nth-child(' + lostWaffleCycle + 'n)',
          ['margin-right', 'margin-left'],
          [0, 'auto']
        );
      }

      newBlock(
        decl,
        ':last-child',
        ['margin-right', 'margin-bottom'],
        [0, 0]
      );

      newBlock(
        decl,
        ':nth-child(1n)',
        ['margin-right', 'margin-bottom', 'margin-left'],
        [lostWaffleGutter, lostWaffleGutter, 0]
      );
    } else {
      if (lostWaffleCycle !== 0) {
        newBlock(
          decl,
          ':nth-last-child(-n + ' + lostWaffleCycle + ')',
          ['margin-bottom'],
          [0]
        );

        if (settings.clearing === 'left') {
          newBlock(
            decl,
            ':nth-child(' + lostWaffleCycle + 'n + 1)',
            ['clear'],
            ['left']
          );
        } else {
          newBlock(
            decl,
            ':nth-child(' + lostWaffleCycle + 'n + 1)',
            ['clear'],
            ['both']
          );
        }

        newBlock(
          decl,
          ':nth-child(' + lostWaffleCycle + 'n)',
          ['margin-right', 'float'],
          [0, 'right']
        );
      }

      newBlock(
        decl,
        ':last-child',
        ['margin-right', 'margin-bottom'],
        [0, 0]
      );

      newBlock(
        decl,
        ':nth-child(1n)',
        ['float', 'margin-right', 'margin-bottom', 'clear'],
        ['left', lostWaffleGutter, lostWaffleGutter, 'none']
      );
    }

    if (lostWaffleGutter !== '0') {
      cloneAllBefore({
        width: lgLogic.calcValue(lostWaffle, lostWaffleGutter, lostWaffleRounder),
        height: lgLogic.calcValue(lostWaffle, lostWaffleGutter, lostWaffleRounder)
      });
    } else {
      cloneAllBefore({
        width: 'calc(' + lostWaffleRounder + '% * ' + lostWaffle + ')',
        height: 'calc(' + lostWaffleRounder + '% * ' + lostWaffle + ')'
      });
    }

    decl.remove();
  });
};
<commit_msg>Simplifies lost-waffle with lgLogic<commit_after>var newBlock = require('./new-block.js');

var lgLogic = require('./_lg-logic');

/**
 * lost-waffle: Creates a block that is a fraction of the size of its
 * containing element's width AND height with a gutter on the right
 * and bottom.
 *
 * @param {string} [fraction] - This is a simple fraction of the containing
 *   element's width and height.
 *
 * @param {integer} [cycle] - Lost works by assigning a margin-right/bottom
 *   to all elements except the last row (no margin-bottom) and the last
 *   column (no margin-right). It does this by default by using the
 *   denominator of the fraction you pick. To override this default use this
 *   param., e.g.: .foo { lost-waffle: 2/4 2; }
 *
 * @param {length} [gutter] - The margin on the right and bottom side of the
 *   element used to create a gutter. Typically this is left alone and the
 *   global $gutter will be used, but you can override it here if you want
 *   certain elements to have a particularly large or small gutter (pass 0
 *   for no gutter at all).
 *
 * @param {string} [flex|no-flex] - Determines whether this element should
 *   use Flexbox or not.
 *
 * @example
 *   section {
 *     height: 100%;
 *   }
 *   div {
 *     lost-waffle: 1/3;
 *   }
 */
module.exports = function lostWaffleDecl(css, settings) {
  css.walkDecls('lost-waffle', function lostWaffleDeclFunction(decl) {
    var declArr = [];
    var lostWaffle;
    var lostWaffleCycle;
    var lostWaffleRounder = settings.rounder;
    var lostWaffleGutter = settings.gutter;
    var lostWaffleFlexbox = settings.flexbox;

    function cloneAllBefore(props) {
      Object.keys(props).forEach(function traverseProps(prop) {
        decl.cloneBefore({
          prop: prop,
          value: props[prop]
        });
      });
    }

    if (settings.cycle === 'auto') {
      lostWaffleCycle = decl.value.split('/')[1];
    } else {
      lostWaffleCycle = settings.cycle;
    }

    declArr = decl.value.split(' ');
    lostWaffle = declArr[0];

    if (declArr[1] !== undefined && declArr[1].search(/^\d/) !== -1) {
      lostWaffleCycle = declArr[1];
    }

    if (declArr[1] === 'flex' || declArr[1] === 'no-flex' || declArr[1] === 'auto') {
      lostWaffleCycle = declArr[0].split('/')[1];
    }

    if (declArr[2] !== undefined && declArr[2].search(/^\d/) !== -1) {
      lostWaffleGutter = declArr[2];
    }

    if (declArr.indexOf('flex') !== -1) {
      lostWaffleFlexbox = 'flex';
    }

    if (declArr.indexOf('no-flex') !== -1) {
      lostWaffleFlexbox = 'no-flex';
    }

    decl.parent.nodes.forEach(function lostWaffleRounderFunction(declaration) {
      if (declaration.prop === 'lost-waffle-rounder') {
        lostWaffleRounder = declaration.value;
        declaration.remove();
      }
    });

    decl.parent.nodes.forEach(function lostWaffleCycleFunction(declaration) {
      if (declaration.prop === 'lost-waffle-cycle') {
        lostWaffleCycle = declaration.value;

        declaration.remove();
      }
    });

    decl.parent.nodes.forEach(function lostWaffleGutterFunction(declaration) {
      if (declaration.prop === 'lost-waffle-gutter') {
        lostWaffleGutter = declaration.value;

        declaration.remove();
      }
    });

    decl.parent.nodes.forEach(function lostWaffleFlexboxFunction(declaration) {
      if (declaration.prop === 'lost-waffle-flexbox') {
        if (declaration.prop === 'flex') {
          lostWaffleFlexbox = 'flex';
        }

        declaration.remove();
      }
    });

    if (lostWaffleFlexbox === 'flex') {
      decl.cloneBefore({
        prop: 'flex',
        value: '0 0 auto'
      });

      if (lostWaffleCycle !== 0) {
        newBlock(
          decl,
          ':nth-last-child(-n + ' + lostWaffleCycle + ')',
          ['margin-bottom'],
          [0]
        );

        newBlock(
          decl,
          ':nth-child(' + lostWaffleCycle + 'n)',
          ['margin-right', 'margin-left'],
          [0, 'auto']
        );
      }

      newBlock(
        decl,
        ':last-child',
        ['margin-right', 'margin-bottom'],
        [0, 0]
      );

      newBlock(
        decl,
        ':nth-child(1n)',
        ['margin-right', 'margin-bottom', 'margin-left'],
        [lostWaffleGutter, lostWaffleGutter, 0]
      );
    } else {
      if (lostWaffleCycle !== 0) {
        newBlock(
          decl,
          ':nth-last-child(-n + ' + lostWaffleCycle + ')',
          ['margin-bottom'],
          [0]
        );

        if (settings.clearing === 'left') {
          newBlock(
            decl,
            ':nth-child(' + lostWaffleCycle + 'n + 1)',
            ['clear'],
            ['left']
          );
        } else {
          newBlock(
            decl,
            ':nth-child(' + lostWaffleCycle + 'n + 1)',
            ['clear'],
            ['both']
          );
        }

        newBlock(
          decl,
          ':nth-child(' + lostWaffleCycle + 'n)',
          ['margin-right', 'float'],
          [0, 'right']
        );
      }

      newBlock(
        decl,
        ':last-child',
        ['margin-right', 'margin-bottom'],
        [0, 0]
      );

      newBlock(
        decl,
        ':nth-child(1n)',
        ['float', 'margin-right', 'margin-bottom', 'clear'],
        ['left', lostWaffleGutter, lostWaffleGutter, 'none']
      );
    }

    cloneAllBefore({
      width: lgLogic.calcValue(lostWaffle, lostWaffleGutter, lostWaffleRounder),
      height: lgLogic.calcValue(lostWaffle, lostWaffleGutter, lostWaffleRounder)
    });

    decl.remove();
  });
};
<|endoftext|>