<commit_before>/*global System, declare, it, xit, describe, xdescribe, beforeEach, afterEach, before, after*/
import { createDOMEnvironment } from "../rendering/dom-helper.js";
import { MorphicEnv } from "../index.js";
import { Text, World } from "../index.js";
import { expect } from "mocha-es6";
import { pt, Color, Rectangle, Transform, rect } from "lively.graphics";
import { dummyFontMetric as fontMetric } from "./test-helpers.js";

// FIXME! FontMetric should work in nodejs with jsdom as well!!!
var inBrowser = System.get("@system-env").browser ? it :
  (title) => { console.warn(`Test ${title} is currently only supported in a browser`); return xit(title); }

var describeInBrowser = System.get("@system-env").browser ? describe :
  (title, fn) => { console.warn(`Suite ${title} is currently only supported in a browser`); return xdescribe(title, fn); }

const padding = 20;

function text(string, props) {
  return new Text({
    name: "text",
    textString: string,
    fontFamily: "Monaco, monosonpace",
    fontSize: 10,
    extent: pt(100,100),
    padding,
    fontMetric,
    ...props
  });
}

function range(startRow, startCol, endRow, endCol) {
  return {start: {row: startRow, column: startCol}, end: {row: endRow, column: endCol}}
}

var world, sut;
function createDummyWorld() {
  world = new World({name: "world", extent: pt(300,300), submorphs: [
    text("text\nfor tests", {
      position: pt(10.10),
      fill: Color.gray.lighter(2)
    })]})
  sut = world.get("text");
  return world;
}

var env;
async function createMorphicEnv() {
  env = new MorphicEnv(await createDOMEnvironment());
  env.domEnv.document.body.style = "margin: 0";
  MorphicEnv.pushDefault(env);
  await env.setWorld(createDummyWorld());
}
async function destroyMorphicEnv() {
  MorphicEnv.popDefault().uninstall();
}

describe("text selection", () => {

  it("selection / line string", () => {
    var t = text("hello\n world", {});

    t.selection = range(1,1,1,1);
    expect(t.selectionOrLineString()).equals(" world");
    t.selection = range(1,1,1,3);
    expect(t.selectionOrLineString()).equals("wo");
  });

//   xit("uninitialized", () => {
//     var t = text("hello\n world", {});
//     expect(t.selection).containSubset({start: {row: 0, column: 0}, end: {row: 0, column: 0}})
//   });

});

describe("anchors", () => {

  it("adds anchor by id", () => {
    var t = text("hello\nworld", {}),
        a = t.addAnchor({id: "test", column: 1, row: 1});
    expect(t.anchors).to.have.length(1);
    expect(t.addAnchor({id: "test"})).equals(a);
    expect(t.anchors).to.have.length(1);
    t.removeAnchor(a);
    expect(t.anchors).to.have.length(0);
    t.addAnchor({id: "test"})
    expect(t.anchors).to.have.length(1);
    t.removeAnchor("test");
    expect(t.anchors).to.have.length(0);
  });

  it("insert moves anchors around", () => {
    var t = text("hello\nworld", {}),
        a = t.addAnchor({id: "test", column: 1, row: 1});
    t.insertText("abc", {row: 1, column: 0});
    expect(a.position).deep.equals({row: 1, column: 4}, "1 before anchor");
    t.insertText("xy", {row: 1, column: 4});
    expect(a.position).deep.equals({row: 1, column: 6}, "2 directly before anchor");
    t.insertText("123", {row: 1, column: 7});
    expect(a.position).deep.equals({row: 1, column: 6}, "3 after anchor");
    t.insertText("123", {row: 0, column: 0});
    expect(a.position).deep.equals({row: 1, column: 6}, "4 line before anchor");
    t.insertText("123\n456", {row: 0, column: 0});
    expect(a.position).deep.equals({row: 2, column: 6}, "5 new line before anchor");
  });

  it("delete moves anchors around", () => {
    var t = text("hello\nworld", {}),
        a = t.addAnchor({id: "test", column: 1, row: 1});
    t.deleteText(range(1,0,1,1));
    expect(a.position).deep.equals({row: 1, column: 0}, "1 before anchor");
    t.deleteText(range(0,2,1,0));
    expect(a.position).deep.equals({row: 0, column: 2}, "2 line before");
    t.deleteText(range(0,2,0,5));
    expect(a.position).deep.equals({row: 0, column: 2}, "3 after anchor");
    t.deleteText(range(0,1,0,5));
    expect(a.position).deep.equals({row: 0, column: 1}, "4 crossing anchor");
  });

});

describe("rendered text", () => {

  beforeEach(() => createMorphicEnv());
  afterEach(() => destroyMorphicEnv());

  inBrowser("only renders visible part of scrolled text", async () => {
    var lineHeight = sut.renderer.chunks[0].height;
    Object.assign(sut, {
      clipMode: "auto",
      extent: pt(100,2*lineHeight), position: pt(0,0),
      textString: [0,1,2,3,4,5,6,7,8,9].join("\n"),
      scroll: pt(0, lineHeight*2+padding-1)
    });

    await sut.whenRendered();

    var node = env.renderer.getNodeForMorph(sut),
        b = node.querySelector(".text-layer").getBoundingClientRect(),
        textBounds = new Rectangle(b.left, b.top, b.width, b.height);

    expect(textBounds.top()).equals(-2*lineHeight-padding+1, "text layer not scrolled");
    expect(textBounds.height).equals(lineHeight*10 + 2*padding, "text layer does not have size of all lines");
    expect(node.querySelector(".text-layer").textContent).equals("123", "text  layer renders more than necessary");
  });

  it("can resize on content change", async () => {
    sut.textString = "Hello hello";
    await sut.whenRendered();
    expect(sut.width).equals(11*fontMetric.width + 2*padding);
    sut.textString = "foo";
    await sut.whenRendered();
    expect(sut.width).equals(3*fontMetric.width + 2*padding);
  });

});

describe("scroll", () => {

  beforeEach(() => createMorphicEnv());
  afterEach(() => destroyMorphicEnv());

  it("cursor into view", () => {
    var lineHeight = fontMetric.height;
    Object.assign(sut, {
      clipMode: "auto",
      extent: pt(100,2*lineHeight+2*padding),
      textString: [0,1,2,3,4,5,6,7,8,9].join("\n"),
    });
    expect(sut.scrollExtent).equals(pt(100, sut.document.lines.length * lineHeight + 2*padding, "scrollExtent not as expected"));
    sut.cursorPosition = { column: 0, row: 3 }
    sut.scrollCursorIntoView();
    expect(sut.scroll).equals(pt(0,lineHeight*2-padding));
    sut.cursorPosition = {column: 0, row: 0};
    sut.scrollCursorIntoView();
    expect(sut.scroll).equals(pt(0,0))
  });

});

describe("text key events", () => {

  beforeEach(() => createMorphicEnv());
  afterEach(() => destroyMorphicEnv());

  it("text entry via keydown", async () => {
    sut.focus();
    await env.eventDispatcher.simulateDOMEvents(
      {type: "input", data: 'l'},
      {type: "input", data: 'o'},
      {type: "input", data: 'l'},
      {type: "keydown", key: "Enter"});
    expect(sut).property("textString").equals("lol\ntext\nfor tests");
  });

  it("backspace", async () => {
    sut.focus();
    env.eventDispatcher.simulateDOMEvents(
      {type: "input", data: 'l'},
      {type: "input", data: 'o'},
      {type: "input", data: 'l'},
      {type: "input", data: 'w'},
      {type: "input", data: 'u'},
      {type: "input", data: 't'});

    expect(sut).property("textString").equals("lolwuttext\nfor tests");
    env.eventDispatcher.simulateDOMEvents(
      {type: "keydown", key: "Backspace"},
      {type: "keydown", key: "Backspace"},
      {type: "keydown", key: "Backspace"},
      {type: "input", data: ' '});

    expect(sut).property("textString").equals("lol text\nfor tests");
  });

  it("entry clears selection", async () => {
    sut.focus();
    sut.selection = range(0,0,0,4);
    env.eventDispatcher.simulateDOMEvents(
      {type: "input", data: 'w'},
      {type: "input", data: 'o'},
      {type: "input", data: 'w'});
    expect(sut).property("textString").equals("wow\nfor tests");
  });

});

describe("text mouse events", () => {

  beforeEach(() => createMorphicEnv());
  afterEach(() => destroyMorphicEnv());

  it("click sets cursor", () => {
    var {position: {x,y}, fontFamily, fontSize, textString} = sut,
        clickPos = pt(x+fontMetric.width*3 + 2 + padding, y+fontMetric.height*2 - 5 + padding); // second line

    expect(sut.selection).stringEquals("Selection(0/0 -> 0/0)");
    env.eventDispatcher.simulateDOMEvents({target: sut, type: "click", position: clickPos});

    var clickIndex = sut.document.positionToIndex({row: 1, column: 3});
    expect(clickIndex).equals(8);
    expect(sut.selection).stringEquals("Selection(1/3 -> 1/3)");
  });

  it("drag sets selection", () => {
    var {position: {x,y}, fontFamily, fontSize, textString} = sut,
        {width: charW, height: charH} = fontMetric;

    var dragStartPos =    pt(charW+padding-2, charH+padding-2),
        dragOvershotPos = pt(3*charW+padding+10, charH*2+padding+10),
        dragEndPos =      pt(3*charW+padding+2, charH*2+padding-charH/2);

    expect(sut.selection).stringEquals("Selection(0/0 -> 0/0)");

    env.eventDispatcher.simulateDOMEvents(
      {type: "pointerdown", target: sut, position: dragStartPos},
      {type: "pointermove", target: sut, position: dragOvershotPos}, // simulate overshoot
      {type: "pointermove", target: sut, position: dragEndPos},
      {type: "pointerup", target: sut, position: dragEndPos}
    );

    var dragEndIndex = sut.document.positionToIndex({row: 1, column: 1});
    expect(dragEndIndex).equals(6);
    expect(sut.selection).stringEquals("Selection(0/0 -> 1/2)");
  });

});
<commit_msg>text: add tests for double- and triple-click<commit_after>/*global System, declare, it, xit, describe, xdescribe, beforeEach, afterEach, before, after*/
import { createDOMEnvironment } from "../rendering/dom-helper.js";
import { MorphicEnv } from "../index.js";
import { Text, World } from "../index.js";
import { expect } from "mocha-es6";
import { pt, Color, Rectangle, Transform, rect } from "lively.graphics";
import { dummyFontMetric as fontMetric } from "./test-helpers.js";

// FIXME! FontMetric should work in nodejs with jsdom as well!!!
var inBrowser = System.get("@system-env").browser ? it :
  (title) => { console.warn(`Test ${title} is currently only supported in a browser`); return xit(title); }

var describeInBrowser = System.get("@system-env").browser ? describe :
  (title, fn) => { console.warn(`Suite ${title} is currently only supported in a browser`); return xdescribe(title, fn); }

const padding = 20;

function text(string, props) {
  return new Text({
    name: "text",
    textString: string,
    fontFamily: "Monaco, monosonpace",
    fontSize: 10,
    extent: pt(100,100),
    padding,
    fontMetric,
    ...props
  });
}

function range(startRow, startCol, endRow, endCol) {
  return {start: {row: startRow, column: startCol}, end: {row: endRow, column: endCol}}
}

var world, sut;
function createDummyWorld() {
  world = new World({name: "world", extent: pt(300,300), submorphs: [
    text("text\nfor tests", {
      position: pt(10.10),
      fill: Color.gray.lighter(2)
    })]})
  sut = world.get("text");
  return world;
}

var env;
async function createMorphicEnv() {
  env = new MorphicEnv(await createDOMEnvironment());
  env.domEnv.document.body.style = "margin: 0";
  MorphicEnv.pushDefault(env);
  await env.setWorld(createDummyWorld());
}
async function destroyMorphicEnv() {
  MorphicEnv.popDefault().uninstall();
}

describe("text selection", () => {

  it("selection / line string", () => {
    var t = text("hello\n world", {});

    t.selection = range(1,1,1,1);
    expect(t.selectionOrLineString()).equals(" world");
    t.selection = range(1,1,1,3);
    expect(t.selectionOrLineString()).equals("wo");
  });

//   xit("uninitialized", () => {
//     var t = text("hello\n world", {});
//     expect(t.selection).containSubset({start: {row: 0, column: 0}, end: {row: 0, column: 0}})
//   });

});

describe("anchors", () => {

  it("adds anchor by id", () => {
    var t = text("hello\nworld", {}),
        a = t.addAnchor({id: "test", column: 1, row: 1});
    expect(t.anchors).to.have.length(1);
    expect(t.addAnchor({id: "test"})).equals(a);
    expect(t.anchors).to.have.length(1);
    t.removeAnchor(a);
    expect(t.anchors).to.have.length(0);
    t.addAnchor({id: "test"})
    expect(t.anchors).to.have.length(1);
    t.removeAnchor("test");
    expect(t.anchors).to.have.length(0);
  });

  it("insert moves anchors around", () => {
    var t = text("hello\nworld", {}),
        a = t.addAnchor({id: "test", column: 1, row: 1});
    t.insertText("abc", {row: 1, column: 0});
    expect(a.position).deep.equals({row: 1, column: 4}, "1 before anchor");
    t.insertText("xy", {row: 1, column: 4});
    expect(a.position).deep.equals({row: 1, column: 6}, "2 directly before anchor");
    t.insertText("123", {row: 1, column: 7});
    expect(a.position).deep.equals({row: 1, column: 6}, "3 after anchor");
    t.insertText("123", {row: 0, column: 0});
    expect(a.position).deep.equals({row: 1, column: 6}, "4 line before anchor");
    t.insertText("123\n456", {row: 0, column: 0});
    expect(a.position).deep.equals({row: 2, column: 6}, "5 new line before anchor");
  });

  it("delete moves anchors around", () => {
    var t = text("hello\nworld", {}),
        a = t.addAnchor({id: "test", column: 1, row: 1});
    t.deleteText(range(1,0,1,1));
    expect(a.position).deep.equals({row: 1, column: 0}, "1 before anchor");
    t.deleteText(range(0,2,1,0));
    expect(a.position).deep.equals({row: 0, column: 2}, "2 line before");
    t.deleteText(range(0,2,0,5));
    expect(a.position).deep.equals({row: 0, column: 2}, "3 after anchor");
    t.deleteText(range(0,1,0,5));
    expect(a.position).deep.equals({row: 0, column: 1}, "4 crossing anchor");
  });

});

describe("rendered text", () => {

  beforeEach(() => createMorphicEnv());
  afterEach(() => destroyMorphicEnv());

  inBrowser("only renders visible part of scrolled text", async () => {
    var lineHeight = sut.renderer.chunks[0].height;
    Object.assign(sut, {
      clipMode: "auto",
      extent: pt(100,2*lineHeight), position: pt(0,0),
      textString: [0,1,2,3,4,5,6,7,8,9].join("\n"),
      scroll: pt(0, lineHeight*2+padding-1)
    });

    await sut.whenRendered();

    var node = env.renderer.getNodeForMorph(sut),
        b = node.querySelector(".text-layer").getBoundingClientRect(),
        textBounds = new Rectangle(b.left, b.top, b.width, b.height);

    expect(textBounds.top()).equals(-2*lineHeight-padding+1, "text layer not scrolled");
    expect(textBounds.height).equals(lineHeight*10 + 2*padding, "text layer does not have size of all lines");
    expect(node.querySelector(".text-layer").textContent).equals("123", "text  layer renders more than necessary");
  });

  it("can resize on content change", async () => {
    sut.textString = "Hello hello";
    await sut.whenRendered();
    expect(sut.width).equals(11*fontMetric.width + 2*padding);
    sut.textString = "foo";
    await sut.whenRendered();
    expect(sut.width).equals(3*fontMetric.width + 2*padding);
  });

});

describe("scroll", () => {

  beforeEach(() => createMorphicEnv());
  afterEach(() => destroyMorphicEnv());

  it("cursor into view", () => {
    var lineHeight = fontMetric.height;
    Object.assign(sut, {
      clipMode: "auto",
      extent: pt(100,2*lineHeight+2*padding),
      textString: [0,1,2,3,4,5,6,7,8,9].join("\n"),
    });
    expect(sut.scrollExtent).equals(pt(100, sut.document.lines.length * lineHeight + 2*padding, "scrollExtent not as expected"));
    sut.cursorPosition = { column: 0, row: 3 }
    sut.scrollCursorIntoView();
    expect(sut.scroll).equals(pt(0,lineHeight*2-padding));
    sut.cursorPosition = {column: 0, row: 0};
    sut.scrollCursorIntoView();
    expect(sut.scroll).equals(pt(0,0))
  });

});

describe("text key events", () => {

  beforeEach(() => createMorphicEnv());
  afterEach(() => destroyMorphicEnv());

  it("text entry via keydown", async () => {
    sut.focus();
    await env.eventDispatcher.simulateDOMEvents(
      {type: "input", data: 'l'},
      {type: "input", data: 'o'},
      {type: "input", data: 'l'},
      {type: "keydown", key: "Enter"});
    expect(sut).property("textString").equals("lol\ntext\nfor tests");
  });

  it("backspace", async () => {
    sut.focus();
    env.eventDispatcher.simulateDOMEvents(
      {type: "input", data: 'l'},
      {type: "input", data: 'o'},
      {type: "input", data: 'l'},
      {type: "input", data: 'w'},
      {type: "input", data: 'u'},
      {type: "input", data: 't'});

    expect(sut).property("textString").equals("lolwuttext\nfor tests");
    env.eventDispatcher.simulateDOMEvents(
      {type: "keydown", key: "Backspace"},
      {type: "keydown", key: "Backspace"},
      {type: "keydown", key: "Backspace"},
      {type: "input", data: ' '});

    expect(sut).property("textString").equals("lol text\nfor tests");
  });

  it("entry clears selection", async () => {
    sut.focus();
    sut.selection = range(0,0,0,4);
    env.eventDispatcher.simulateDOMEvents(
      {type: "input", data: 'w'},
      {type: "input", data: 'o'},
      {type: "input", data: 'w'});
    expect(sut).property("textString").equals("wow\nfor tests");
  });

});

describe("text mouse events", () => {

  beforeEach(() => createMorphicEnv());
  afterEach(() => destroyMorphicEnv());

  it("click sets cursor", () => {
    var {position: {x,y}, fontFamily, fontSize, textString} = sut,
        clickPos = pt(x+fontMetric.width*3 + 2 + padding, y+fontMetric.height*2 - 5 + padding); // second line

    expect(sut.selection).stringEquals("Selection(0/0 -> 0/0)");
    env.eventDispatcher.simulateDOMEvents({target: sut, type: "click", position: clickPos});

    var clickIndex = sut.document.positionToIndex({row: 1, column: 3});
    expect(clickIndex).equals(8);
    expect(sut.selection).stringEquals("Selection(1/3 -> 1/3)");
  });

  it("drag sets selection", () => {
    var {position: {x,y}, fontFamily, fontSize, textString} = sut,
        {width: charW, height: charH} = fontMetric;

    var dragStartPos =    pt(charW+padding-2, charH+padding-2),
        dragOvershotPos = pt(3*charW+padding+10, charH*2+padding+10),
        dragEndPos =      pt(3*charW+padding+2, charH*2+padding-charH/2);

    expect(sut.selection).stringEquals("Selection(0/0 -> 0/0)");

    env.eventDispatcher.simulateDOMEvents(
      {type: "pointerdown", target: sut, position: dragStartPos},
      {type: "pointermove", target: sut, position: dragOvershotPos}, // simulate overshoot
      {type: "pointermove", target: sut, position: dragEndPos},
      {type: "pointerup", target: sut, position: dragEndPos}
    );

    var dragEndIndex = sut.document.positionToIndex({row: 1, column: 1});
    expect(dragEndIndex).equals(6);
    expect(sut.selection).stringEquals("Selection(0/0 -> 1/2)");
  });

  it("double-click selects word", () => {
    var {position: {x,y}, fontFamily, fontSize, textString} = sut,
        clickPos = pt(x+fontMetric.width*2 + 2 + padding, y+fontMetric.height*2 - 5 + padding); // second line, second char

    expect(sut.selection).stringEquals("Selection(0/0 -> 0/0)");

    env.eventDispatcher.simulateDOMEvents(
      {target: sut, type: "click", position: clickPos},
      {target: sut, type: "click", position: clickPos});

    expect(sut.selection).stringEquals("Selection(1/0 -> 1/3)");
  });

  it("triple-click selects line", () => {
    var {position: {x,y}, fontFamily, fontSize, textString} = sut,
        clickPos = pt(x+fontMetric.width*2 + 2 + padding, y+fontMetric.height*2 - 5 + padding); // second line, second char

    expect(sut.selection).stringEquals("Selection(0/0 -> 0/0)");

    env.eventDispatcher.simulateDOMEvents(
      {target: sut, type: "click", position: clickPos},
      {target: sut, type: "click", position: clickPos},
      {target: sut, type: "click", position: clickPos});

    expect(sut.selection).stringEquals("Selection(1/0 -> 1/9)");
  });
});
<|endoftext|>


<commit_before>/* eslint-disable import/no-dynamic-require, react/no-danger, no-cond-assign */

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { renderToString } from 'react-dom/server'
import fs from 'fs-extra'
import nodepath from 'path'
import Helmet from 'react-helmet'
//
import DefaultHtml from './DefaultHtml'
import { pathJoin } from './shared'
import { DIST, ROOT } from './paths'

const defaultEntry = './src/index'

export const getConfig = () =>
  require(nodepath.resolve(nodepath.join(process.cwd(), 'static.config.js'))).default

// Normalize routes with parents, full paths, context, etc.
export const normalizeRoutes = routes => {
  const flatRoutes = []

  const recurse = (route, parent = { path: '/' }) => {
    const path = pathJoin(parent.path, route.path)

    if (typeof route.noIndex !== 'undefined') {
      console.log(`=> Warning: Route ${route.path} is using 'noIndex'. Did you mean 'noindex'?`)
      route.noindex = route.noIndex
    }

    const normalizedRoute = {
      ...route,
      parent,
      path,
      noindex: typeof route.noindex === 'undefined' ? parent.noindex : route.noindex,
      hasGetProps: !!route.getProps,
    }

    flatRoutes.push(normalizedRoute)

    if (route.children) {
      route.children.forEach(d => recurse(d, normalizedRoute))
    }
  }
  routes.forEach(d => recurse(d))
  flatRoutes.forEach(route => {
    const found = flatRoutes.find(d => d.path === route.path)
    if (found !== route) {
      console.warn('More than one route is defined for path:', route.path)
    }
  })
  return flatRoutes
}

export const writeRoutesToStatic = async ({ config }) => {
  const userConfig = getConfig()
  const HtmlTemplate = config.Html || DefaultHtml
  const Comp = require(nodepath.join(ROOT, userConfig.entry || defaultEntry)).default

  return Promise.all(
    config.routes.map(async route => {
      // Fetch initialProps from each route
      const initialProps = route.getProps && (await route.getProps({ route, dev: false }))
      if (initialProps) {
        await fs.outputFile(
          nodepath.join(DIST, route.path, 'routeData.json'),
          JSON.stringify(initialProps || {}),
        )
      }

      const URL = route.path

      // Inject initialProps into static build
      class InitialPropsContext extends Component {
        static childContextTypes = {
          initialProps: PropTypes.object,
          URL: PropTypes.string,
        }
        getChildContext () {
          return {
            initialProps,
            URL,
          }
        }
        render () {
          return this.props.children
        }
      }

      const ContextualComp = <InitialPropsContext>{Comp}</InitialPropsContext>

      let staticMeta = {}
      if (config.preRenderMeta) {
        // Allow the user to perform custom rendering logic (important for styles and helmet)
        staticMeta = {
          ...staticMeta,
          ...(await config.preRenderMeta(ContextualComp)),
        }
      }

      const appHtml = renderToString(ContextualComp)

      // Extract head calls using Helmet
      const helmet = Helmet.renderStatic()
      const head = {
        htmlProps: helmet.htmlAttributes.toComponent(),
        bodyProps: helmet.bodyAttributes.toComponent(),
        base: helmet.base.toComponent(),
        link: helmet.link.toComponent(),
        meta: helmet.meta.toComponent(),
        noscript: helmet.noscript.toComponent(),
        script: helmet.script.toComponent(),
        style: helmet.style.toComponent(),
        title: helmet.title.toComponent(),
      }

      staticMeta.head = head

      if (config.postRenderMeta) {
        // Allow the user to perform custom rendering logic (important for styles and helmet)
        staticMeta = {
          ...staticMeta,
          ...(await config.postRenderMeta(appHtml)),
        }
      }

      const Html = ({ children, ...rest }) => (

          {children}

      )
      const Head = ({ children, ...rest }) => (

          {head.base}
          {head.link}
          {head.meta}
          {head.noscript}
          {head.script}
          {head.style}
          {head.title}
          {children}

      )
      const Body = ({ children, ...rest }) => (

          {children}


<commit_before>(function() {
	var div, qfix = $('#qunit-fixture');

	module('text_only option', {
		setup:function() {
			div = $('

Hello

');
			qfix.append(div);
		}
	});

	function submit() {
		div.find('form').trigger('jip:submit');
	}

	test('With text only returned html is displayed as text', 2, function() {
		div.jinplace();

		div.click();

		var textarea = div.find(':input');
		var orig = textarea.val();
		textarea.val(orig.replace(/Hello/, 'Hello
World'));
		submit();

		ok(div.text().indexOf('World') > 0, 'text changed');
		ok(div.text().indexOf('
') >= 0, 'literal html');
	});

	test('With text-only==false returned html is interpreted', 3, function() {
		div.attr('data-text-only', 'false');
		div.jinplace();

		div.click();

		var textarea = div.find(':input');
		var orig = textarea.val();
		textarea.val(orig.replace(/Hello/, 'Hello
World'));
		submit();

		ok(div.text().indexOf('World') > 0, 'text changed');
		ok(div.text().indexOf('
') == -1, 'no literal html');
		equal(div.text(), 'HelloWorld');
	});

	test('With textOnly==false returned html is interpreted', 3, function() {
		div.jinplace({textOnly: false});

		div.click();

		var textarea = div.find(':input');
		var orig = textarea.val();
		textarea.val(orig.replace(/Hello/, 'Hello
World'));
		submit();

		ok(div.text().indexOf('World') > 0, 'text changed');
		ok(div.text().indexOf('
') == -1, 'no literal html');
		equal(div.text(), 'HelloWorld');
	});
})();
<commit_msg>JIN-34 IE6-8 text_only_option failure<commit_after>(function() {
	var div, qfix = $('#qunit-fixture');

	module('text_only option', {
		setup:function() {
			div = $('

Hello

');
			qfix.append(div);
		}
	});

	function submit() {
		div.find('form').trigger('jip:submit');
	}

	test('With text only returned html is displayed as text', 2, function() {
		div.jinplace();

		div.click();

		var textarea = div.find(':input');
		var orig = textarea.val();
		textarea.val(orig.replace(/Hello/, 'Hello
World'));
		submit();

		ok(div.text().indexOf('World') > 0, 'text changed');
		ok(div.text().toLowerCase().indexOf('
') >= 0, 'literal html');
	});

	test('With text-only==false returned html is interpreted', 3, function() {
		div.attr('data-text-only', 'false');
		div.jinplace();

		div.click();

		var textarea = div.find(':input');
		var orig = textarea.val();
		textarea.val(orig.replace(/Hello/, 'Hello
World'));
		submit();

		ok(div.text().indexOf('World') > 0, 'text changed');
		ok(div.text().indexOf('
') == -1, 'no literal html');
		equal(div.text(), 'HelloWorld');
	});

	test('With textOnly==false returned html is interpreted', 3, function() {
		div.jinplace({textOnly: false});

		div.click();

		var textarea = div.find(':input');
		var orig = textarea.val();
		textarea.val(orig.replace(/Hello/, 'Hello
World'));
		submit();

		ok(div.text().indexOf('World') > 0, 'text changed');
		ok(div.text().indexOf('
') == -1, 'no literal html');
		equal(div.text(), 'HelloWorld');
	});
})();
<|endoftext|>


<commit_before>define(["views/dialog"], function(GitDialog) {
    var commands = codebox.require("core/commands/toolbar");
    var app = codebox.require("core/app");
    var box = codebox.require("core/box");
    var dialogs = codebox.require("utils/dialogs");
    var menu = codebox.require("core/commands/menu");
    var box = codebox.require("core/box");
    var Command = codebox.require("models/command");
    var rpc = codebox.require("core/backends/rpc");
    var operations = codebox.require("core/operations");

    // Check git status
    var updateStatus = function() {
        return rpc.execute("git/status")
        .then(function() {
            updateMenu(true);
            return updateBranchesMenu();
        }, function(err) {
            updateMenu(false);
        })
    };

    // Branches menu
    var branchesMenu = Command.register({
        'title': "Switch To Branch",
        'type': "menu",
        'offline': false
    });
    var updateBranchesMenu = function() {
        return rpc.execute("git/branches")
        .then(function(branches) {
            branchesMenu.menu.reset(_.map(branches, function(branch) {
                return {
                    'title': branch.name,
                    'flags': branch.active ? "active" : "",
                    'action': function() {
                        var ref = branch.name;
                        return operations.start("git.checkout", function(op) {
                            return rpc.execute("git/checkout", {
                                'ref': ref
                            })
                        }, {
                            title: "Checkout '"+ref+"'"
                        }).then(updateBranchesMenu);
                    }
                }
            }));
        });
    };

    // Handle http auth
    var handleHttpAuth = function(method) {
        return Q(method())
        .fail(function(err) {
            if (err.code == 401) {
                // Fields for https auth
                var fields = {
                    username: {
                        type: "text",
                        label: "Username"
                    },
                    password: {
                        type: "password",
                        label: "Password"
                    }
                };

                // Passphrase for ssh
                if (err.message.toLowerCase().indexOf("authentication") < 0) {
                    fields = {
                        passphrase: {
                            type: "text",
                            label: "Passphrase"
                        }
                    };
                }

                return dialogs.fields("Need authentication:", fields)
                .then(method);
            }
        })
    };

    // Add menu
    var gitMenu = menu.register("git", {
        title: "Repository",
        offline: false
    });

    var updateMenu = function(state) {
        // Clear menu
        gitMenu.clearMenu();

        // Invalid repository
        if (!state) {
            gitMenu.menuSection([
                {
                    'title': "No GIT Repository detected",
                    'type': "label",
                    'icons': {
                        'menu': "warning",
                    }
                },
                {
                    'title': "Initialize Local Repository",
                    'offline': false,
                    'action': function() {
                        return operations.start("git.init", function(op) {
                            return rpc.execute("git/init")
                        }, {
                            title: "Initializing GIT repository"
                        });
                    }
                },
                {
                    'title': "Clone Remote Repository",
                    'offline': false,
                    'action': function() {
                        return operations.start("git.clone", function(op) {
                            return dialogs.prompt("Clone Remote Repository", "Remote repository URI:").then(function(url) {
                                if (!url) return;
                                return handleHttpAuth(function(creds) {
                                    return rpc.execute("git/clone", {
                                        'url': url,
                                        'auth': creds || {}
                                    });
                                });
                            });
                        }, {
                            title: "Cloning GIT repository"
                        });
                    }
                }
            ]);
        } else {
            gitMenu.menuSection([
                {
                    'title': "Commit",
                    'shortcuts': ["mod+shift+C"],
                    'offline': false,
                    'action': function() {
                        dialogs.open(GitDialog);
                    }
                }
            ]).menuSection([
                {
                    'title': "Synchronize",
                    'shortcuts': ["mod+S"],
                    'offline': false,
                    'action': function() {
                        return operations.start("git.sync", function(op) {
                            return handleHttpAuth(function(creds) {
                                return rpc.execute("git/sync", {
                                    'auth': creds || {}
                                });
                            });
                        }, {
                            title: "Pushing & Pulling"
                        });
                    }
                },
                {
                    'title': "Push",
                    'shortcuts': ["mod+P"],
                    'offline': false,
                    'action': function() {
                        return operations.start("git.push", function(op) {
                            return handleHttpAuth(function(creds) {
                                return rpc.execute("git/push", {
                                    'auth': creds || {}
                                });
                            });
                        }, {
                            title: "Pushing"
                        });
                    }
                },
                {
                    'title': "Pull",
                    'shortcuts': ["shift+mod+P"],
                    'offline': false,
                    'action': function() {
                        return operations.start("git.pull", function(op) {
                            return handleHttpAuth(function(creds) {
                                return rpc.execute("git/pull", {
                                    'auth': creds || {}
                                });
                            });
                        }, {
                            title: "Pulling"
                        });
                    }
                }
            ]).menuSection([
                branchesMenu,
                {
                    'title': "Refresh branches",
                    'offline': false,
                    'action': updateBranchesMenu,
                }
            ]).menuSection([
                {
                    'title': "Create a branch",
                    'offline': false,
                    'action': function() {
                        dialogs.prompt("Create a branch", "Enter the name for the new branch:").then(function(name) {
                            if (!name) return;
                            operations.start("git.branch.create", function(op) {
                                return rpc.execute("git/branch/create", {
                                    'name': name
                                })
                            }, {
                                title: "Creating branch '"+name+"'"
                            });
                        });
                    }
                },
                {
                    'title': "Delete a branch",
                    'offline': false,
                    'action': function() {
                        dialogs.prompt("Delete a branch", "Enter the name of the branch you want to delete:").then(function(name) {
                            if (!name) return;
                            operations.start("git.branch.delete", function(op) {
                                return rpc.execute("git/branch/delete", {
                                    'name': name
                                })
                            }, {
                                title: "Deleting branch '"+name+"'"
                            }).then(updateBranchesMenu);
                        });
                    }
                }
            ]);
        }
    };

    box.on("box:git", function() {
        updateStatus();
    });
    updateStatus();
});

<commit_msg>Fix error display with git operations<commit_after>define(["views/dialog"], function(GitDialog) {
    var Q = codebox.require("hr/promise");
    var commands = codebox.require("core/commands/toolbar");
    var app = codebox.require("core/app");
    var box = codebox.require("core/box");
    var dialogs = codebox.require("utils/dialogs");
    var menu = codebox.require("core/commands/menu");
    var box = codebox.require("core/box");
    var Command = codebox.require("models/command");
    var rpc = codebox.require("core/backends/rpc");
    var operations = codebox.require("core/operations");

    // Check git status
    var updateStatus = function() {
        return rpc.execute("git/status")
        .then(function() {
            updateMenu(true);
            return updateBranchesMenu();
        }, function(err) {
            updateMenu(false);
        })
    };

    // Branches menu
    var branchesMenu = Command.register({
        'title': "Switch To Branch",
        'type': "menu",
        'offline': false
    });
    var updateBranchesMenu = function() {
        return rpc.execute("git/branches")
        .then(function(branches) {
            branchesMenu.menu.reset(_.map(branches, function(branch) {
                return {
                    'title': branch.name,
                    'flags': branch.active ? "active" : "",
                    'action': function() {
                        var ref = branch.name;
                        return operations.start("git.checkout", function(op) {
                            return rpc.execute("git/checkout", {
                                'ref': ref
                            })
                        }, {
                            title: "Checkout '"+ref+"'"
                        }).then(updateBranchesMenu);
                    }
                }
            }));
        });
    };

    // Handle http auth
    var handleHttpAuth = function(method) {
        return Q(method())
        .fail(function(err) {
            if (err.code == 401) {
                // Fields for https auth
                var fields = {
                    username: {
                        type: "text",
                        label: "Username"
                    },
                    password: {
                        type: "password",
                        label: "Password"
                    }
                };

                // Passphrase for ssh
                if (err.message.toLowerCase().indexOf("authentication") < 0) {
                    fields = {
                        passphrase: {
                            type: "text",
                            label: "Passphrase"
                        }
                    };
                }

                return dialogs.fields("Need authentication:", fields)
                .then(method);
            } else {
                return Q.reject(err);
            }
        })
    };

    // Add menu
    var gitMenu = menu.register("git", {
        title: "Repository",
        offline: false
    });

    var updateMenu = function(state) {
        // Clear menu
        gitMenu.clearMenu();

        // Invalid repository
        if (!state) {
            gitMenu.menuSection([
                {
                    'title': "No GIT Repository detected",
                    'type': "label",
                    'icons': {
                        'menu': "warning",
                    }
                },
                {
                    'title': "Initialize Local Repository",
                    'offline': false,
                    'action': function() {
                        return operations.start("git.init", function(op) {
                            return rpc.execute("git/init")
                        }, {
                            title: "Initializing GIT repository"
                        });
                    }
                },
                {
                    'title': "Clone Remote Repository",
                    'offline': false,
                    'action': function() {
                        return operations.start("git.clone", function(op) {
                            return dialogs.prompt("Clone Remote Repository", "Remote repository URI:")
                            .then(function(url) {
                                if (!url) return;
                                return handleHttpAuth(function(creds) {
                                    return rpc.execute("git/clone", {
                                        'url': url,
                                        'auth': creds || {}
                                    });
                                });
                            });
                        }, {
                            title: "Cloning GIT repository"
                        });
                    }
                }
            ]);
        } else {
            gitMenu.menuSection([
                {
                    'title': "Commit",
                    'shortcuts': ["mod+shift+C"],
                    'offline': false,
                    'action': function() {
                        dialogs.open(GitDialog);
                    }
                }
            ]).menuSection([
                {
                    'title': "Synchronize",
                    'shortcuts': ["mod+S"],
                    'offline': false,
                    'action': function() {
                        return operations.start("git.sync", function(op) {
                            return handleHttpAuth(function(creds) {
                                return rpc.execute("git/sync", {
                                    'auth': creds || {}
                                });
                            });
                        }, {
                            title: "Pushing & Pulling"
                        });
                    }
                },
                {
                    'title': "Push",
                    'shortcuts': ["mod+P"],
                    'offline': false,
                    'action': function() {
                        return operations.start("git.push", function(op) {
                            return handleHttpAuth(function(creds) {
                                return rpc.execute("git/push", {
                                    'auth': creds || {}
                                });
                            });
                        }, {
                            title: "Pushing"
                        });
                    }
                },
                {
                    'title': "Pull",
                    'shortcuts': ["shift+mod+P"],
                    'offline': false,
                    'action': function() {
                        return operations.start("git.pull", function(op) {
                            return handleHttpAuth(function(creds) {
                                return rpc.execute("git/pull", {
                                    'auth': creds || {}
                                });
                            });
                        }, {
                            title: "Pulling"
                        });
                    }
                }
            ]).menuSection([
                branchesMenu,
                {
                    'title': "Refresh branches",
                    'offline': false,
                    'action': updateBranchesMenu,
                }
            ]).menuSection([
                {
                    'title': "Create a branch",
                    'offline': false,
                    'action': function() {
                        dialogs.prompt("Create a branch", "Enter the name for the new branch:").then(function(name) {
                            if (!name) return;
                            operations.start("git.branch.create", function(op) {
                                return rpc.execute("git/branch/create", {
                                    'name': name
                                })
                            }, {
                                title: "Creating branch '"+name+"'"
                            });
                        });
                    }
                },
                {
                    'title': "Delete a branch",
                    'offline': false,
                    'action': function() {
                        dialogs.prompt("Delete a branch", "Enter the name of the branch you want to delete:").then(function(name) {
                            if (!name) return;
                            operations.start("git.branch.delete", function(op) {
                                return rpc.execute("git/branch/delete", {
                                    'name': name
                                })
                            }, {
                                title: "Deleting branch '"+name+"'"
                            }).then(updateBranchesMenu);
                        });
                    }
                }
            ]);
        }
    };

    box.on("box:git", function() {
        updateStatus();
    });
    updateStatus();
});

<|endoftext|>


<commit_before>
/**
 * Sticky.js
 * Library for sticky elements written in vanilla javascript. With this library you can easily set sticky elements on your website. It's also responsive.
 *
 * @version 1.1.7
 * @author Rafal Galus <biuro@rafalgalus.pl>
 * @website https://rgalus.github.io/sticky-js/
 * @repo https://github.com/rgalus/sticky-js
 * @license https://github.com/rgalus/sticky-js/blob/master/LICENSE
 */

class Sticky {
  /**
   * Sticky instance constructor
   * @constructor
   * @param {string} selector - Selector which we can find elements
   * @param {string} options - Global options for sticky elements (could be overwritten by data-{option}="" attributes)
   */
  constructor(selector = '', options = {}) {
    this.selector = selector;
    this.elements = [];

    this.version = '1.1.7';

    this.vp = this.getViewportSize();
    this.scrollTop = this.getScrollTopPosition();
    this.body = document.querySelector('body');

    this.options = {
      wrap: options.wrap || false,
      marginTop: options.marginTop || 0,
      stickyFor: options.stickyFor || 0,
      stickyClass: options.stickyClass || null,
      stickyContainer: options.stickyContainer || 'body',
    };

    this.run();
  }

  /**
   * Function that waits for page to be fully loaded and then renders & activates every sticky element found with specified selector
   * @function
   */
  run() {
    // wait for page to be fully loaded
    const pageLoaded = setInterval(() => {
      if (document.readyState === 'complete') {
        clearInterval(pageLoaded);

        const elements = document.querySelectorAll(this.selector);
        this.forEach(elements, (element) => this.renderElement(element));
      }
    }, 10);
  }

  /**
   * Function that assign needed variables for sticky element, that are used in future for calculations and other
   * @function
   * @param {node} element - Element to be rendered
   */
  renderElement(element) {
    // create container for variables needed in future
    element.sticky = {};

    // set default variables
    element.sticky.active = false;

    element.sticky.marginTop = parseInt(element.getAttribute('data-margin-top')) || this.options.marginTop;
    element.sticky.stickyFor = parseInt(element.getAttribute('data-sticky-for')) || this.options.stickyFor;
    element.sticky.stickyClass = element.getAttribute('data-sticky-class') || this.options.stickyClass;
    element.sticky.wrap = element.hasAttribute('data-sticky-wrap') ? true : this.options.wrap;
    // @todo attribute for stickyContainer
    // element.sticky.stickyContainer = element.getAttribute('data-sticky-container') || this.options.stickyContainer;
    element.sticky.stickyContainer = this.options.stickyContainer;

    element.sticky.container = this.getStickyContainer(element);
    element.sticky.container.rect = this.getRectangle(element.sticky.container);

    element.sticky.rect = this.getRectangle(element);

    // fix when element is image that has not yet loaded and width, height = 0
    if (element.tagName.toLowerCase() === 'img') {
      element.onload = () => element.sticky.rect = this.getRectangle(element);
    }

    if (element.sticky.wrap) {
      this.wrapElement(element);
    }

    // activate rendered element
    this.activate(element);
  }

  /**
   * Wraps element into placeholder element
   * @function
   * @param {node} element - Element to be wrapped
   */
  wrapElement(element) {
    element.insertAdjacentHTML('beforebegin', '');
    element.previousSibling.appendChild(element);
  }

  /**
   * Function that activates element when specified conditions are met and then initalise events
   * @function
   * @param {node} element - Element to be activated
   */
   activate(element) {
    if (
      ((element.sticky.rect.top + element.sticky.rect.height) < (element.sticky.container.rect.top + element.sticky.container.rect.height))
      && (element.sticky.stickyFor < this.vp.width)
      && !element.sticky.active
    ) {
      element.sticky.active = true;
    }

    if (this.elements.indexOf(element) < 0) {
      this.elements.push(element);
    }

    if (!element.sticky.resizeEvent) {
      this.initResizeEvents(element);
      element.sticky.resizeEvent = true;
    }

    if (!element.sticky.scrollEvent) {
      this.initScrollEvents(element);
      element.sticky.scrollEvent = true;
    }

    this.setPosition(element);
   }

  /**
   * Function which is adding onResizeEvents to window listener and assigns function to element as resizeListener
   * @function
   * @param {node} element - Element for which resize events are initialised
   */
   initResizeEvents(element) {
    element.sticky.resizeListener = () => this.onResizeEvents(element);
    window.addEventListener('resize', element.sticky.resizeListener);
   }

  /**
   * Removes element listener from resize event
   * @function
   * @param {node} element - Element from which listener is deleted
   */
   destroyResizeEvents(element) {
    window.removeEventListener('resize', element.sticky.resizeListener);
   }

  /**
   * Function which is fired when user resize window. It checks if element should be activated or deactivated and then run setPosition function
   * @function
   * @param {node} element - Element for which event function is fired
   */
   onResizeEvents(element) {
    this.vp = this.getViewportSize();

    element.sticky.rect = this.getRectangle(element);
    element.sticky.container.rect = this.getRectangle(element.sticky.container);

    if (
      ((element.sticky.rect.top + element.sticky.rect.height) < (element.sticky.container.rect.top + element.sticky.container.rect.height))
      && (element.sticky.stickyFor < this.vp.width)
      && !element.sticky.active
    ) {
      element.sticky.active = true;
    } else if (
      ((element.sticky.rect.top + element.sticky.rect.height) >= (element.sticky.container.rect.top + element.sticky.container.rect.height))
      || element.sticky.stickyFor >= this.vp.width
      && element.sticky.active
    ) {
      element.sticky.active = false;
    }

    this.setPosition(element);
   }

  /**
   * Function which is adding onScrollEvents to window listener and assigns function to element as scrollListener
   * @function
   * @param {node} element - Element for which scroll events are initialised
   */
   initScrollEvents(element) {
    element.sticky.scrollListener = () => this.onScrollEvents(element);
    window.addEventListener('scroll', element.sticky.scrollListener);
   }

  /**
   * Removes element listener from scroll event
   * @function
   * @param {node} element - Element from which listener is deleted
   */
   destroyScrollEvents(element) {
    window.removeEventListener('scroll', element.sticky.scrollListener);
   }

  /**
   * Function which is fired when user scroll window. If element is active, function is invoking setPosition function
   * @function
   * @param {node} element - Element for which event function is fired
   */
   onScrollEvents(element) {
    this.scrollTop = this.getScrollTopPosition();

    if (element.sticky.active) {
      this.setPosition(element);
    }
   }

  /**
   * Main function for the library. Here are some condition calculations and css appending for sticky element when user scroll window
   * @function
   * @param {node} element - Element that will be positioned if it's active
   */
   setPosition(element) {
    this.css(element, { position: '', width: '', top: '', left: '' });

    if ((this.vp.height < element.sticky.rect.height) || !element.sticky.active) {
      return;
    }

    if (!element.sticky.rect.width) {
      element.sticky.rect = this.getRectangle(element);
    }

    if (element.sticky.wrap) {
      this.css(element.parentNode, {
        display: 'block',
        width: element.sticky.rect.width + 'px',
        height: element.sticky.rect.height + 'px',
      });
    }

    if (
      element.sticky.rect.top === 0
      && element.sticky.container === this.body
    ) {
      this.css(element, {
        position: 'fixed',
        top: element.sticky.rect.top + 'px',
        left: element.sticky.rect.left + 'px',
        width: element.sticky.rect.width + 'px',
      });
    } else if (this.scrollTop > (element.sticky.rect.top - element.sticky.marginTop)) {
      this.css(element, {
        position: 'fixed',
        width: element.sticky.rect.width + 'px',
        left: element.sticky.rect.left + 'px',
      });

      if (
        (this.scrollTop + element.sticky.rect.height + element.sticky.marginTop)
        > (element.sticky.container.rect.top + element.sticky.container.offsetHeight)
      ) {

        if (element.sticky.stickyClass) {
          element.classList.remove(element.sticky.stickyClass);
        }

        this.css(element, {
          top: (element.sticky.container.rect.top + element.sticky.container.offsetHeight) - (this.scrollTop + element.sticky.rect.height) + 'px' }
        );
      } else {
        if (element.sticky.stickyClass) {
          element.classList.add(element.sticky.stickyClass);
        }

        this.css(element, { top: element.sticky.marginTop + 'px' });
      }
    } else {
      if (element.sticky.stickyClass) {
        element.classList.remove(element.sticky.stickyClass);
      }

      this.css(element, { position: '', width: '', top: '', left: '' });

      if (element.sticky.wrap) {
        this.css(element.parentNode, { display: '', width: '', height: '' });
      }
    }
   }

  /**
   * Function that updates element sticky rectangle (with sticky container), then activate or deactivate element, then update position if it's active
   * @function
   */
   update() {
    this.forEach(this.elements, (element) => {
      element.sticky.rect = this.getRectangle(element);
      element.sticky.container.rect = this.getRectangle(element.sticky.container);

      this.activate(element);
      this.setPosition(element);
    });
   }

  /**
   * Destroys sticky element, remove listeners
   * @function
   */
   destroy() {
    this.forEach(this.elements, (element) => {
      this.destroyResizeEvents(element);
      this.destroyScrollEvents(element);
      delete element.sticky;
    });
   }

  /**
   * Function that returns container element in which sticky element is stuck (if is not specified, then it's stuck to body)
   * @function
   * @param {node} element - Element which sticky container are looked for
   * @return {node} element - Sticky container
   */
   getStickyContainer(element) {
    let container = element.parentNode;

    while (
      !container.hasAttribute('data-sticky-container')
      && !container.parentNode.querySelector(element.sticky.stickyContainer)
      && container !== this.body
    ) {
      container = container.parentNode;
    }

    return container;
   }

  /**
   * Function that returns element rectangle & position (width, height, top, left)
   * @function
   * @param {node} element - Element which position & rectangle are returned
   * @return {object}
   */
  getRectangle(element) {
    this.css(element, { position: '', width: '', top: '', left: '' });

    const width = Math.max(element.offsetWidth, element.clientWidth, element.scrollWidth);
    const height = Math.max(element.offsetHeight, element.clientHeight, element.scrollHeight);

    let top = 0;
    let left = 0;

    do {
      top += element.offsetTop || 0;
      left += element.offsetLeft || 0;
      element = element.offsetParent;
    } while(element);

    return { top, left, width, height };
  }

  /**
   * Function that returns viewport dimensions
   * @function
   * @return {object}
   */
  getViewportSize() {
    return {
      width: Math.max(document.documentElement.clientWidth, window.innerWidth || 0),
      height: Math.max(document.documentElement.clientHeight, window.innerHeight || 0),
    };
  }

  /**
   * Function that returns scroll position offset from top
   * @function
   * @return {number}
   */
  getScrollTopPosition() {
    return (window.pageYOffset || document.scrollTop)  - (document.clientTop || 0) || 0;
  }

  /**
   * Helper function for loops
   * @helper
   * @param {array}
   * @param {function} callback - Callback function (no need for explanation)
   */
  forEach(array, callback) {
    for (let i = 0, len = array.length; i < len; i++) {
      callback(array[i]);
    }
  }

  /**
   * Helper function to add/remove css properties for specified element.
   * @helper
   * @param {node} element - DOM element
   * @param {object} properties - CSS properties that will be added/removed from specified element
   */
  css(element, properties) {
    for (let property in properties) {
      if (properties.hasOwnProperty(property)) {
        element.style[property] = properties[property];
      }
    }
  }
}

/**
 * Export function that supports AMD, CommonJS and Plain Browser.
 */
((root, factory) => {
  if (typeof exports !== 'undefined') {
    module.exports = factory;
  } else if (typeof define === 'function' && define.amd) {
    define([], factory);
  } else {
    root.Sticky = factory;
  }
})(this, Sticky);
<commit_msg>Fix #9 issue with wrong sticky position after refresh<commit_after>
/**
 * Sticky.js
 * Library for sticky elements written in vanilla javascript. With this library you can easily set sticky elements on your website. It's also responsive.
 *
 * @version 1.1.7
 * @author Rafal Galus <biuro@rafalgalus.pl>
 * @website https://rgalus.github.io/sticky-js/
 * @repo https://github.com/rgalus/sticky-js
 * @license https://github.com/rgalus/sticky-js/blob/master/LICENSE
 */

class Sticky {
  /**
   * Sticky instance constructor
   * @constructor
   * @param {string} selector - Selector which we can find elements
   * @param {string} options - Global options for sticky elements (could be overwritten by data-{option}="" attributes)
   */
  constructor(selector = '', options = {}) {
    this.selector = selector;
    this.elements = [];

    this.version = '1.1.7';

    this.vp = this.getViewportSize();
    this.body = document.querySelector('body');

    this.options = {
      wrap: options.wrap || false,
      marginTop: options.marginTop || 0,
      stickyFor: options.stickyFor || 0,
      stickyClass: options.stickyClass || null,
      stickyContainer: options.stickyContainer || 'body',
    };

    this.updateScrollTopPosition = this.updateScrollTopPosition.bind(this);

    this.updateScrollTopPosition();
    window.addEventListener('load', this.updateScrollTopPosition);
    window.addEventListener('scroll', this.updateScrollTopPosition);

    this.run();
  }

  /**
   * Function that waits for page to be fully loaded and then renders & activates every sticky element found with specified selector
   * @function
   */
  run() {
    // wait for page to be fully loaded
    const pageLoaded = setInterval(() => {
      if (document.readyState === 'complete') {
        clearInterval(pageLoaded);

        const elements = document.querySelectorAll(this.selector);
        this.forEach(elements, (element) => this.renderElement(element));
      }
    }, 10);
  }

  /**
   * Function that assign needed variables for sticky element, that are used in future for calculations and other
   * @function
   * @param {node} element - Element to be rendered
   */
  renderElement(element) {
    // create container for variables needed in future
    element.sticky = {};

    // set default variables
    element.sticky.active = false;

    element.sticky.marginTop = parseInt(element.getAttribute('data-margin-top')) || this.options.marginTop;
    element.sticky.stickyFor = parseInt(element.getAttribute('data-sticky-for')) || this.options.stickyFor;
    element.sticky.stickyClass = element.getAttribute('data-sticky-class') || this.options.stickyClass;
    element.sticky.wrap = element.hasAttribute('data-sticky-wrap') ? true : this.options.wrap;
    // @todo attribute for stickyContainer
    // element.sticky.stickyContainer = element.getAttribute('data-sticky-container') || this.options.stickyContainer;
    element.sticky.stickyContainer = this.options.stickyContainer;

    element.sticky.container = this.getStickyContainer(element);
    element.sticky.container.rect = this.getRectangle(element.sticky.container);

    element.sticky.rect = this.getRectangle(element);

    // fix when element is image that has not yet loaded and width, height = 0
    if (element.tagName.toLowerCase() === 'img') {
      element.onload = () => element.sticky.rect = this.getRectangle(element);
    }

    if (element.sticky.wrap) {
      this.wrapElement(element);
    }

    // activate rendered element
    this.activate(element);
  }

  /**
   * Wraps element into placeholder element
   * @function
   * @param {node} element - Element to be wrapped
   */
  wrapElement(element) {
    element.insertAdjacentHTML('beforebegin', '');
    element.previousSibling.appendChild(element);
  }

  /**
   * Function that activates element when specified conditions are met and then initalise events
   * @function
   * @param {node} element - Element to be activated
   */
   activate(element) {
    if (
      ((element.sticky.rect.top + element.sticky.rect.height) < (element.sticky.container.rect.top + element.sticky.container.rect.height))
      && (element.sticky.stickyFor < this.vp.width)
      && !element.sticky.active
    ) {
      element.sticky.active = true;
    }

    if (this.elements.indexOf(element) < 0) {
      this.elements.push(element);
    }

    if (!element.sticky.resizeEvent) {
      this.initResizeEvents(element);
      element.sticky.resizeEvent = true;
    }

    if (!element.sticky.scrollEvent) {
      this.initScrollEvents(element);
      element.sticky.scrollEvent = true;
    }

    this.setPosition(element);
   }

  /**
   * Function which is adding onResizeEvents to window listener and assigns function to element as resizeListener
   * @function
   * @param {node} element - Element for which resize events are initialised
   */
   initResizeEvents(element) {
    element.sticky.resizeListener = () => this.onResizeEvents(element);
    window.addEventListener('resize', element.sticky.resizeListener);
   }

  /**
   * Removes element listener from resize event
   * @function
   * @param {node} element - Element from which listener is deleted
   */
   destroyResizeEvents(element) {
    window.removeEventListener('resize', element.sticky.resizeListener);
   }

  /**
   * Function which is fired when user resize window. It checks if element should be activated or deactivated and then run setPosition function
   * @function
   * @param {node} element - Element for which event function is fired
   */
   onResizeEvents(element) {
    this.vp = this.getViewportSize();

    element.sticky.rect = this.getRectangle(element);
    element.sticky.container.rect = this.getRectangle(element.sticky.container);

    if (
      ((element.sticky.rect.top + element.sticky.rect.height) < (element.sticky.container.rect.top + element.sticky.container.rect.height))
      && (element.sticky.stickyFor < this.vp.width)
      && !element.sticky.active
    ) {
      element.sticky.active = true;
    } else if (
      ((element.sticky.rect.top + element.sticky.rect.height) >= (element.sticky.container.rect.top + element.sticky.container.rect.height))
      || element.sticky.stickyFor >= this.vp.width
      && element.sticky.active
    ) {
      element.sticky.active = false;
    }

    this.setPosition(element);
   }

  /**
   * Function which is adding onScrollEvents to window listener and assigns function to element as scrollListener
   * @function
   * @param {node} element - Element for which scroll events are initialised
   */
   initScrollEvents(element) {
    element.sticky.scrollListener = () => this.onScrollEvents(element);
    window.addEventListener('scroll', element.sticky.scrollListener);
   }

  /**
   * Removes element listener from scroll event
   * @function
   * @param {node} element - Element from which listener is deleted
   */
   destroyScrollEvents(element) {
    window.removeEventListener('scroll', element.sticky.scrollListener);
   }

  /**
   * Function which is fired when user scroll window. If element is active, function is invoking setPosition function
   * @function
   * @param {node} element - Element for which event function is fired
   */
   onScrollEvents(element) {
    if (element.sticky.active) {
      this.setPosition(element);
    }
   }

  /**
   * Main function for the library. Here are some condition calculations and css appending for sticky element when user scroll window
   * @function
   * @param {node} element - Element that will be positioned if it's active
   */
   setPosition(element) {
    this.css(element, { position: '', width: '', top: '', left: '' });

    if ((this.vp.height < element.sticky.rect.height) || !element.sticky.active) {
      return;
    }

    if (!element.sticky.rect.width) {
      element.sticky.rect = this.getRectangle(element);
    }

    if (element.sticky.wrap) {
      this.css(element.parentNode, {
        display: 'block',
        width: element.sticky.rect.width + 'px',
        height: element.sticky.rect.height + 'px',
      });
    }

    if (
      element.sticky.rect.top === 0
      && element.sticky.container === this.body
    ) {
      this.css(element, {
        position: 'fixed',
        top: element.sticky.rect.top + 'px',
        left: element.sticky.rect.left + 'px',
        width: element.sticky.rect.width + 'px',
      });
    } else if (this.scrollTop > (element.sticky.rect.top - element.sticky.marginTop)) {
      this.css(element, {
        position: 'fixed',
        width: element.sticky.rect.width + 'px',
        left: element.sticky.rect.left + 'px',
      });

      if (
        (this.scrollTop + element.sticky.rect.height + element.sticky.marginTop)
        > (element.sticky.container.rect.top + element.sticky.container.offsetHeight)
      ) {

        if (element.sticky.stickyClass) {
          element.classList.remove(element.sticky.stickyClass);
        }

        this.css(element, {
          top: (element.sticky.container.rect.top + element.sticky.container.offsetHeight) - (this.scrollTop + element.sticky.rect.height) + 'px' }
        );
      } else {
        if (element.sticky.stickyClass) {
          element.classList.add(element.sticky.stickyClass);
        }

        this.css(element, { top: element.sticky.marginTop + 'px' });
      }
    } else {
      if (element.sticky.stickyClass) {
        element.classList.remove(element.sticky.stickyClass);
      }

      this.css(element, { position: '', width: '', top: '', left: '' });

      if (element.sticky.wrap) {
        this.css(element.parentNode, { display: '', width: '', height: '' });
      }
    }
   }

  /**
   * Function that updates element sticky rectangle (with sticky container), then activate or deactivate element, then update position if it's active
   * @function
   */
   update() {
    this.forEach(this.elements, (element) => {
      element.sticky.rect = this.getRectangle(element);
      element.sticky.container.rect = this.getRectangle(element.sticky.container);

      this.activate(element);
      this.setPosition(element);
    });
   }

  /**
   * Destroys sticky element, remove listeners
   * @function
   */
   destroy() {
    this.forEach(this.elements, (element) => {
      this.destroyResizeEvents(element);
      this.destroyScrollEvents(element);
      delete element.sticky;
    });
   }

  /**
   * Function that returns container element in which sticky element is stuck (if is not specified, then it's stuck to body)
   * @function
   * @param {node} element - Element which sticky container are looked for
   * @return {node} element - Sticky container
   */
   getStickyContainer(element) {
    let container = element.parentNode;

    while (
      !container.hasAttribute('data-sticky-container')
      && !container.parentNode.querySelector(element.sticky.stickyContainer)
      && container !== this.body
    ) {
      container = container.parentNode;
    }

    return container;
   }

  /**
   * Function that returns element rectangle & position (width, height, top, left)
   * @function
   * @param {node} element - Element which position & rectangle are returned
   * @return {object}
   */
  getRectangle(element) {
    this.css(element, { position: '', width: '', top: '', left: '' });

    const width = Math.max(element.offsetWidth, element.clientWidth, element.scrollWidth);
    const height = Math.max(element.offsetHeight, element.clientHeight, element.scrollHeight);

    let top = 0;
    let left = 0;

    do {
      top += element.offsetTop || 0;
      left += element.offsetLeft || 0;
      element = element.offsetParent;
    } while(element);

    return { top, left, width, height };
  }

  /**
   * Function that returns viewport dimensions
   * @function
   * @return {object}
   */
  getViewportSize() {
    return {
      width: Math.max(document.documentElement.clientWidth, window.innerWidth || 0),
      height: Math.max(document.documentElement.clientHeight, window.innerHeight || 0),
    };
  }

  /**
   * Function that updates window scroll position
   * @function
   * @return {number}
   */
  updateScrollTopPosition() {
    this.scrollTop = (window.pageYOffset || document.scrollTop)  - (document.clientTop || 0) || 0;
  }

  /**
   * Helper function for loops
   * @helper
   * @param {array}
   * @param {function} callback - Callback function (no need for explanation)
   */
  forEach(array, callback) {
    for (let i = 0, len = array.length; i < len; i++) {
      callback(array[i]);
    }
  }

  /**
   * Helper function to add/remove css properties for specified element.
   * @helper
   * @param {node} element - DOM element
   * @param {object} properties - CSS properties that will be added/removed from specified element
   */
  css(element, properties) {
    for (let property in properties) {
      if (properties.hasOwnProperty(property)) {
        element.style[property] = properties[property];
      }
    }
  }
}

/**
 * Export function that supports AMD, CommonJS and Plain Browser.
 */
((root, factory) => {
  if (typeof exports !== 'undefined') {
    module.exports = factory;
  } else if (typeof define === 'function' && define.amd) {
    define([], factory);
  } else {
    root.Sticky = factory;
  }
})(this, Sticky);
<|endoftext|>


<commit_before>define(["dojo/_base/kernel", "dojo/date", "./buddhist/Date"], function(dojo, dd, buddhistDate){
	dojo.getObject("date.buddhist", true, dojox);
	dojo.experimental("dojox.date.buddhist");

// Utility methods to do arithmetic calculations with buddhist.Dates

dojox.date.buddhist.getDaysInMonth = function(/*buddhist.Date*/dateObject){
	return dd.getDaysInMonth(dateObject.toGregorian());
};

dojox.date.buddhist.isLeapYear = function(/*buddhist.Date*/dateObject){
	return dd.isLeapYear(dateObject.toGregorian());
};

//FIXME: reduce compare, add, diff also
dojox.date.buddhist.compare = function(/*buddhist.Date*/date1, /*buddhist.Date*/date2, /*String?*/portion){
//	summary:
	//		Compare two buddhist date objects by date, time, or both.
	return dd.compare(date1,date2, portion); //FIXME
};

dojox.date.buddhist.add = function(/*dojox.date.buddhist.Date*/date, /*String*/interval, /*int*/amount){
	//	based on and similar to dojo.date.add
	//	summary:
	//		Add to a Date in intervals of different size, from milliseconds to years
	//	date: buddhist.Date
	//		Date object to start with
	//	interval:
	//		A string representing the interval.  One of the following:
	//			"year", "month", "day", "hour", "minute", "second",
	//			"millisecond", "week", "weekday"
	//	amount:
	//		How much to add to the date.

	var newBuddDate = new buddhistDate(date);

	switch(interval){
		case "day":
			newBuddDate.setDate(date.getDate(true) + amount);
			break;
		case "weekday":
			var days, weeks;
			var mod = amount % 5;
			if(!mod){
				days = (amount > 0) ? 5 : -5;
				weeks = (amount > 0) ? ((amount-5)/5) : ((amount+5)/5);
			}else{
				days = mod;
				weeks = parseInt(amount/5);
			}
			// Get weekday value for orig date param
			var strt = date.getDay();
			// Orig date is Sat / positive incrementer
			// Jump over Sun
			var adj = 0;
			if(strt == 6 && amount > 0){
				adj = 1;
			}else if(strt == 0 && amount < 0){
			// Orig date is Sun / negative incrementer
			// Jump back over Sat
				adj = -1;
			}
			// Get weekday val for the new date
			var trgt = strt + days;
			// New date is on Sat or Sun
			if(trgt == 0 || trgt == 6){
				adj = (amount > 0) ? 2 : -2;
			}
			// Increment by number of weeks plus leftover days plus
			// weekend adjustments
			amount = (7 * weeks) + days + adj;
			newBuddDate.setDate(date.getDate(true) + amount);
			break;
		case "year":
			newBuddDate.setFullYear(date.getFullYear() + amount );
			break;
		case "week":
			amount *= 7;
			newBuddDate.setDate(date.getDate(true) + amount);
			break;
		case "month":
			newBuddDate.setMonth(date.getMonth() + amount);
			break;
		case "hour":
			newBuddDate.setHours(date.getHours() + amount );
			break;
		case "minute":
			newBuddDate.setMinutes(date.getMinutes() + amount );
			break;
		case "second":
			newBuddDate.setSeconds(date.getSeconds() + amount );
			break;
		case "millisecond":
			newBuddDate.setMilliseconds(date.getMilliseconds() + amount );
			break;
	}
	return newBuddDate; // dojox.date.buddhist.Date
};

dojox.date.buddhist.difference = function(/*dojox.date.buddhist.Date*/date1, /*dojox.date.buddhist.Date?*/date2, /*String?*/interval){
	//	based on and similar to dojo.date.difference
	//	summary:
	//        date1 - date2
	//	 date2 is hebrew.Date object.  If not specified, the current hebrew.Date is used.
	//	interval:
	//		A string representing the interval.  One of the following:
	//			"year", "month", "day", "hour", "minute", "second",
	//			"millisecond",  "week", "weekday"
	//		Defaults to "day".

	date2 = date2 || new buddhistDate();
	interval = interval || "day";
	var yearDiff = date1.getFullYear() - date2.getFullYear();
	var delta = 1; // Integer return value
	switch(interval){
		case "weekday":
			var days = Math.round(dojox.date.buddhist.difference(date1, date2, "day"));
			var weeks = parseInt(dojox.date.buddhist.difference(date1, date2, "week"));
			var mod = days % 7;

			// Even number of weeks
			if(mod == 0){
				days = weeks*5;
			}else{
				// Weeks plus spare change (< 7 days)
				var adj = 0;
				var aDay = date2.getDay();
				var bDay = date1.getDay();

				weeks = parseInt(days/7);
				mod = days % 7;
				// Mark the date advanced by the number of
				// round weeks (may be zero)
				var dtMark = new buddhistDate(date1);
				dtMark.setDate(dtMark.getDate(true)+(weeks*7));
				var dayMark = dtMark.getDay();

				// Spare change days -- 6 or less
				if(days > 0){
					switch(true){
						// Range starts on Fri
						case aDay == 5:
							adj = -1;
							break;
						// Range starts on Sat
						case aDay == 6:
							adj = 0;
							break;
						// Range ends on Fri
						case bDay == 5:
							adj = -1;
							break;
						// Range ends on Sat
						case bDay == 6:
							adj = -2;
							break;
						// Range contains weekend
						case (dayMark + mod) > 5:
							adj = -2;
					}
				}else if(days < 0){
					switch(true){
						// Range starts on Fri
						case aDay == 5:
							adj = 0;
							break;
						// Range starts on Sat
						case aDay == 6:
							adj = 1;
							break;
						// Range ends on Fri
						case bDay == 5:
							adj = 2;
							break;
						// Range ends on Sat
						case bDay == 6:
							adj = 1;
							break;
						// Range contains weekend
						case (dayMark + mod) < 0:
							adj = 2;
					}
				}
				days += adj;
				days -= (weeks*2);
			}
			delta = days;
			break;
		case "year":
			delta = yearDiff;
			break;
		case "month":
			var startdate =  (date1.toGregorian() > date2.toGregorian()) ? date1 : date2; // more
			var enddate = (date1.toGregorian() > date2.toGregorian()) ? date2 : date1;

			var month1 = startdate.getMonth();
			var month2 = enddate.getMonth();

			if (yearDiff == 0){
				delta = startdate.getMonth() - enddate.getMonth() ;
			}else{
				delta = 12-month2;
				delta +=  month1;
				var i = enddate.getFullYear()+1;
				var e = startdate.getFullYear();
				for (i;   i < e;  i++){
					delta += 12;
				}
			}
			if (date1.toGregorian() < date2.toGregorian()){
				delta = -delta;
			}
			break;
		case "week":
			// Truncate instead of rounding
			// Don't use Math.floor -- value may be negative
			delta = parseInt(dojox.date.buddhist.difference(date1, date2, "day")/7);
			break;
		case "day":
			delta /= 24;
			// fallthrough
		case "hour":
			delta /= 60;
			// fallthrough
		case "minute":
			delta /= 60;
			// fallthrough
		case "second":
			delta /= 1000;
			// fallthrough
		case "millisecond":
			delta *= date1.toGregorian().getTime()- date2.toGregorian().getTime();
	}

	// Round for fractional values and DST leaps
	return Math.round(delta); // Number (integer)
};
return dojo.date.buddhist;
});<commit_msg>refs #13101 - fix a fixme and ws<commit_after>define(["dojo/_base/kernel", "dojo/date", "./buddhist/Date"], function(dojo, dd, buddhistDate){
	dojo.getObject("date.buddhist", true, dojox);
	dojo.experimental("dojox.date.buddhist");

// Utility methods to do arithmetic calculations with buddhist.Dates

dojox.date.buddhist.getDaysInMonth = function(/*buddhist.Date*/dateObject){
	return dd.getDaysInMonth(dateObject.toGregorian());
};

dojox.date.buddhist.isLeapYear = function(/*buddhist.Date*/dateObject){
	return dd.isLeapYear(dateObject.toGregorian());
};

//FIXME: reduce compare, add, diff also
dojox.date.buddhist.compare = function(/*buddhist.Date*/date1, /*buddhist.Date*/date2, /*String?*/portion){
	//	summary:
	//		Compare two buddhist date objects by date, time, or both.
	return dd.compare(date1,date2, portion); // int
};

dojox.date.buddhist.add = function(/*dojox.date.buddhist.Date*/date, /*String*/interval, /*int*/amount){
	//	based on and similar to dojo.date.add
	//	summary:
	//		Add to a Date in intervals of different size, from milliseconds to years
	//	date: buddhist.Date
	//		Date object to start with
	//	interval:
	//		A string representing the interval.  One of the following:
	//			"year", "month", "day", "hour", "minute", "second",
	//			"millisecond", "week", "weekday"
	//	amount:
	//		How much to add to the date.

	var newBuddDate = new buddhistDate(date);

	switch(interval){
		case "day":
			newBuddDate.setDate(date.getDate(true) + amount);
			break;
		case "weekday":
			var days, weeks;
			var mod = amount % 5;
			if(!mod){
				days = (amount > 0) ? 5 : -5;
				weeks = (amount > 0) ? ((amount-5)/5) : ((amount+5)/5);
			}else{
				days = mod;
				weeks = parseInt(amount/5);
			}
			// Get weekday value for orig date param
			var strt = date.getDay();
			// Orig date is Sat / positive incrementer
			// Jump over Sun
			var adj = 0;
			if(strt == 6 && amount > 0){
				adj = 1;
			}else if(strt == 0 && amount < 0){
			// Orig date is Sun / negative incrementer
			// Jump back over Sat
				adj = -1;
			}
			// Get weekday val for the new date
			var trgt = strt + days;
			// New date is on Sat or Sun
			if(trgt == 0 || trgt == 6){
				adj = (amount > 0) ? 2 : -2;
			}
			// Increment by number of weeks plus leftover days plus
			// weekend adjustments
			amount = (7 * weeks) + days + adj;
			newBuddDate.setDate(date.getDate(true) + amount);
			break;
		case "year":
			newBuddDate.setFullYear(date.getFullYear() + amount );
			break;
		case "week":
			amount *= 7;
			newBuddDate.setDate(date.getDate(true) + amount);
			break;
		case "month":
			newBuddDate.setMonth(date.getMonth() + amount);
			break;
		case "hour":
			newBuddDate.setHours(date.getHours() + amount );
			break;
		case "minute":
			newBuddDate.setMinutes(date.getMinutes() + amount );
			break;
		case "second":
			newBuddDate.setSeconds(date.getSeconds() + amount );
			break;
		case "millisecond":
			newBuddDate.setMilliseconds(date.getMilliseconds() + amount );
			break;
	}
	return newBuddDate; // dojox.date.buddhist.Date
};

dojox.date.buddhist.difference = function(/*dojox.date.buddhist.Date*/date1, /*dojox.date.buddhist.Date?*/date2, /*String?*/interval){
	//	based on and similar to dojo.date.difference
	//	summary:
	//        date1 - date2
	//	 date2 is hebrew.Date object.  If not specified, the current hebrew.Date is used.
	//	interval:
	//		A string representing the interval.  One of the following:
	//			"year", "month", "day", "hour", "minute", "second",
	//			"millisecond",  "week", "weekday"
	//		Defaults to "day".

	date2 = date2 || new buddhistDate();
	interval = interval || "day";
	var yearDiff = date1.getFullYear() - date2.getFullYear();
	var delta = 1; // Integer return value
	switch(interval){
		case "weekday":
			var days = Math.round(dojox.date.buddhist.difference(date1, date2, "day"));
			var weeks = parseInt(dojox.date.buddhist.difference(date1, date2, "week"));
			var mod = days % 7;

			// Even number of weeks
			if(mod == 0){
				days = weeks*5;
			}else{
				// Weeks plus spare change (< 7 days)
				var adj = 0;
				var aDay = date2.getDay();
				var bDay = date1.getDay();

				weeks = parseInt(days/7);
				mod = days % 7;
				// Mark the date advanced by the number of
				// round weeks (may be zero)
				var dtMark = new buddhistDate(date1);
				dtMark.setDate(dtMark.getDate(true)+(weeks*7));
				var dayMark = dtMark.getDay();

				// Spare change days -- 6 or less
				if(days > 0){
					switch(true){
						// Range starts on Fri
						case aDay == 5:
							adj = -1;
							break;
						// Range starts on Sat
						case aDay == 6:
							adj = 0;
							break;
						// Range ends on Fri
						case bDay == 5:
							adj = -1;
							break;
						// Range ends on Sat
						case bDay == 6:
							adj = -2;
							break;
						// Range contains weekend
						case (dayMark + mod) > 5:
							adj = -2;
					}
				}else if(days < 0){
					switch(true){
						// Range starts on Fri
						case aDay == 5:
							adj = 0;
							break;
						// Range starts on Sat
						case aDay == 6:
							adj = 1;
							break;
						// Range ends on Fri
						case bDay == 5:
							adj = 2;
							break;
						// Range ends on Sat
						case bDay == 6:
							adj = 1;
							break;
						// Range contains weekend
						case (dayMark + mod) < 0:
							adj = 2;
					}
				}
				days += adj;
				days -= (weeks*2);
			}
			delta = days;
			break;
		case "year":
			delta = yearDiff;
			break;
		case "month":
			var startdate =  (date1.toGregorian() > date2.toGregorian()) ? date1 : date2; // more
			var enddate = (date1.toGregorian() > date2.toGregorian()) ? date2 : date1;

			var month1 = startdate.getMonth();
			var month2 = enddate.getMonth();

			if (yearDiff == 0){
				delta = startdate.getMonth() - enddate.getMonth() ;
			}else{
				delta = 12-month2;
				delta +=  month1;
				var i = enddate.getFullYear()+1;
				var e = startdate.getFullYear();
				for (i;   i < e;  i++){
					delta += 12;
				}
			}
			if (date1.toGregorian() < date2.toGregorian()){
				delta = -delta;
			}
			break;
		case "week":
			// Truncate instead of rounding
			// Don't use Math.floor -- value may be negative
			delta = parseInt(dojox.date.buddhist.difference(date1, date2, "day")/7);
			break;
		case "day":
			delta /= 24;
			// fallthrough
		case "hour":
			delta /= 60;
			// fallthrough
		case "minute":
			delta /= 60;
			// fallthrough
		case "second":
			delta /= 1000;
			// fallthrough
		case "millisecond":
			delta *= date1.toGregorian().getTime()- date2.toGregorian().getTime();
	}

	// Round for fractional values and DST leaps
	return Math.round(delta); // Number (integer)
};
return dojo.date.buddhist;
});<|endoftext|>


<commit_before>$(window).scroll(function() {
    if ($('.navbar').offset().top > 50) {
        $('.navbar-fixed-top').addClass('top-nav-collapse');
    } else {
        $('.navbar-fixed-top').removeClass('top-nav-collapse');
    }
});

$(function() {
    if(navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(function(position) {
            var jqxhr = $.get('/school/find', {
                latitude: position.coords.latitude,
                longitude: position.coords.longitude
            });

            jqxhr.done(function(response) {
                loadMap(position, response);
                // update header with closest school and information about it.
            });

            jqxhr.fail(function(response) {
                console.log(response.responseText);
            });
        }, function() {
            console.log('Geolocation service failed.');
        });
    } else {
        console.log('Browser does not support Geolocation');
    }
});

function loadMap(position, school) {
    var mapOptions = {
        zoom: 13,
        center: new google.maps.LatLng(position.coords.latitude, position.coords.longitude),
        disableDefaultUI: true,
        scrollwheel: false,
        draggable: true,
        //styles: [{"featureType":"water","elementType":"geometry","stylers":[{"color":"#a2daf2"}]},{"featureType":"landscape.man_made","elementType":"geometry","stylers":[{"color":"#f7f1df"}]},{"featureType":"landscape.natural","elementType":"geometry","stylers":[{"color":"#d0e3b4"}]},{"featureType":"landscape.natural.terrain","elementType":"geometry","stylers":[{"visibility":"off"}]},{"featureType":"poi.park","elementType":"geometry","stylers":[{"color":"#bde6ab"}]},{"featureType":"poi","elementType":"labels","stylers":[{"visibility":"off"}]},{"featureType":"poi.medical","elementType":"geometry","stylers":[{"color":"#fbd3da"}]},{"featureType":"poi.business","stylers":[{"visibility":"off"}]},{"featureType":"road","elementType":"geometry.stroke","stylers":[{"visibility":"off"}]},{"featureType":"road","elementType":"labels","stylers":[{"visibility":"off"}]},{"featureType":"road.highway","elementType":"geometry.fill","stylers":[{"color":"#ffe15f"}]},{"featureType":"road.highway","elementType":"geometry.stroke","stylers":[{"color":"#efd151"}]},{"featureType":"road.arterial","elementType":"geometry.fill","stylers":[{"color":"#ffffff"}]},{"featureType":"road.local","elementType":"geometry.fill","stylers":[{"color":"black"}]},{"featureType":"transit.station.airport","elementType":"geometry.fill","stylers":[{"color":"#cfb2db"}]}]
    };

    var mapElement = document.getElementById('map');
    var map = new google.maps.Map(mapElement, mapOptions);
    var origin = position.coords.latitude + ',' + position.coords.longitude;
    var destination = school.latitude + ',' + school.longitude;
    var service = new google.maps.DirectionsService();

    var request = {
        origin: origin,
        destination: destination,
        travelMode: google.maps.DirectionsTravelMode.DRIVING
    };

    service.route(request, function(response, status) {
        if (status == google.maps.DirectionsStatus.OK) {
            new google.maps.DirectionsRenderer({
                map: map,
                directions: response,
                suppressMarkers: true
            });

            var route = response.routes[0].legs[0];
            var startMarker = new google.maps.Marker({
                position: route.start_location,
                map: map,
                animation: google.maps.Animation.DROP,
                title: 'Start Point'
            });

            var startingMarkerContent = '
You are Here
';
            var startInfoWindow = new google.maps.InfoWindow();
            startInfoWindow.setContent(startingMarkerContent);
            startInfoWindow.open(map, startMarker);

            google.maps.event.addListener(startMarker, 'click', function() {
                startInfoWindow.open(map, startMarker);
            });

            var endMarker = new google.maps.Marker({
                position: route.end_location,
                map: map,
                animation: google.maps.Animation.DROP,
                title: 'End Point'
            });

            console.log(school);

            var endingMarkerContent = '
'+
                    '
'+school.name+'
'+
                    '
'+school.address+'
'+
                    school.city+' '+school.state+' '+school.zip;

            if(school.url !== undefined) {
                endingMarkerContent += '
'+school.url+'';
            }

            endingMarkerContent += '

';

            var endInfowindow = new google.maps.InfoWindow();
            endInfowindow.setContent(endingMarkerContent);
            endInfowindow.open(map, endMarker);

            google.maps.event.addListener(endMarker, 'click', function() {
                endInfoWindow.open(map, endMarker);
            });
        } else {
            console.log("Unable to retrieve your route");
        }
    });
}<commit_msg>removing js loggin<commit_after>$(window).scroll(function() {
    if ($('.navbar').offset().top > 50) {
        $('.navbar-fixed-top').addClass('top-nav-collapse');
    } else {
        $('.navbar-fixed-top').removeClass('top-nav-collapse');
    }
});

$(function() {
    if(navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(function(position) {
            var jqxhr = $.get('/school/find', {
                latitude: position.coords.latitude,
                longitude: position.coords.longitude
            });

            jqxhr.done(function(response) {
                loadMap(position, response);
                // update header with closest school and information about it.
            });

            jqxhr.fail(function(response) {
                console.log(response.responseText);
            });
        }, function() {
            console.log('Geolocation service failed.');
        });
    } else {
        console.log('Browser does not support Geolocation');
    }
});

function loadMap(position, school) {
    var mapOptions = {
        zoom: 13,
        center: new google.maps.LatLng(position.coords.latitude, position.coords.longitude),
        disableDefaultUI: true,
        scrollwheel: false,
        draggable: true,
        //styles: [{"featureType":"water","elementType":"geometry","stylers":[{"color":"#a2daf2"}]},{"featureType":"landscape.man_made","elementType":"geometry","stylers":[{"color":"#f7f1df"}]},{"featureType":"landscape.natural","elementType":"geometry","stylers":[{"color":"#d0e3b4"}]},{"featureType":"landscape.natural.terrain","elementType":"geometry","stylers":[{"visibility":"off"}]},{"featureType":"poi.park","elementType":"geometry","stylers":[{"color":"#bde6ab"}]},{"featureType":"poi","elementType":"labels","stylers":[{"visibility":"off"}]},{"featureType":"poi.medical","elementType":"geometry","stylers":[{"color":"#fbd3da"}]},{"featureType":"poi.business","stylers":[{"visibility":"off"}]},{"featureType":"road","elementType":"geometry.stroke","stylers":[{"visibility":"off"}]},{"featureType":"road","elementType":"labels","stylers":[{"visibility":"off"}]},{"featureType":"road.highway","elementType":"geometry.fill","stylers":[{"color":"#ffe15f"}]},{"featureType":"road.highway","elementType":"geometry.stroke","stylers":[{"color":"#efd151"}]},{"featureType":"road.arterial","elementType":"geometry.fill","stylers":[{"color":"#ffffff"}]},{"featureType":"road.local","elementType":"geometry.fill","stylers":[{"color":"black"}]},{"featureType":"transit.station.airport","elementType":"geometry.fill","stylers":[{"color":"#cfb2db"}]}]
    };

    var mapElement = document.getElementById('map');
    var map = new google.maps.Map(mapElement, mapOptions);
    var origin = position.coords.latitude + ',' + position.coords.longitude;
    var destination = school.latitude + ',' + school.longitude;
    var service = new google.maps.DirectionsService();

    var request = {
        origin: origin,
        destination: destination,
        travelMode: google.maps.DirectionsTravelMode.DRIVING
    };

    service.route(request, function(response, status) {
        if (status == google.maps.DirectionsStatus.OK) {
            new google.maps.DirectionsRenderer({
                map: map,
                directions: response,
                suppressMarkers: true
            });

            var route = response.routes[0].legs[0];
            var startMarker = new google.maps.Marker({
                position: route.start_location,
                map: map,
                animation: google.maps.Animation.DROP,
                title: 'Start Point'
            });

            var startingMarkerContent = '
You are Here
';
            var startInfoWindow = new google.maps.InfoWindow();
            startInfoWindow.setContent(startingMarkerContent);
            startInfoWindow.open(map, startMarker);

            google.maps.event.addListener(startMarker, 'click', function() {
                startInfoWindow.open(map, startMarker);
            });

            var endMarker = new google.maps.Marker({
                position: route.end_location,
                map: map,
                animation: google.maps.Animation.DROP,
                title: 'End Point'
            });

            var endingMarkerContent = '
'+
                    '
'+school.name+'
'+
                    '
'+school.address+'
'+
                    school.city+' '+school.state+' '+school.zip;

            if(school.url !== undefined) {
                endingMarkerContent += '
'+school.url+'';
            }

            endingMarkerContent += '

';

            var endInfowindow = new google.maps.InfoWindow();
            endInfowindow.setContent(endingMarkerContent);
            endInfowindow.open(map, endMarker);

            google.maps.event.addListener(endMarker, 'click', function() {
                endInfoWindow.open(map, endMarker);
            });
        } else {
            console.log("Unable to retrieve your route");
        }
    });
}<|endoftext|>


<commit_before><commit_msg>experimenting during migration<commit_after><|endoftext|>


<commit_before>var onHeaders = require('on-headers');
var proxy = require('proxy-middleware');
var url = require('url');

module.exports = function(gulp, plugins, config) {
  return function() {
    plugins.connect.server({
      root: [__dirname + '/../../' + config.path],
      port: config.port,
      livereload: false,
      open: false,
      middleware: function(connect, opt) {
        config.proxies = config.proxies || [];
        var middlewares =
            config.proxies.map(function(entry) { return makeProxy(entry.route, entry.url); });
        middlewares.push(connect.favicon());
        // pub serve can't give the right content-type header for jsonp requests
        // so we must turn off Chromium strict MIME type checking
        // see https://github.com/angular/angular/issues/3030#issuecomment-123453168
        middlewares.unshift(stripHeader('x-content-type-options'));
        return middlewares;
      }
    })();
  };
};

function makeProxy(route, urlParam) {
  var options = url.parse(urlParam);
  options.route = route;
  return proxy(options);
}

function stripHeader(toStrip) {
  return function(req, res, next) {
    onHeaders(res, function onHeaders() {
      res.removeHeader(toStrip);
    });
    next();
  };
}
<commit_msg>cleanup(build): fix jsserve not to throw<commit_after>var onHeaders = require('on-headers');
var proxy = require('proxy-middleware');
var url = require('url');

module.exports = function(gulp, plugins, config) {
  return function() {
    plugins.connect.server({
      root: [__dirname + '/../../' + config.path],
      port: config.port,
      livereload: false,
      open: false,
      middleware: function(connect, opt) {
        config.proxies = config.proxies || [];
        var middlewares =
            config.proxies.map(function(entry) { return makeProxy(entry.route, entry.url); });
        middlewares.push(connect.favicon());
        // pub serve can't give the right content-type header for jsonp requests
        // so we must turn off Chromium strict MIME type checking
        // see https://github.com/angular/angular/issues/3030#issuecomment-123453168
        middlewares.unshift(stripHeader('x-content-type-options'));
        return middlewares;
      }
    });
  };
};

function makeProxy(route, urlParam) {
  var options = url.parse(urlParam);
  options.route = route;
  return proxy(options);
}

function stripHeader(toStrip) {
  return function(req, res, next) {
    onHeaders(res, function onHeaders() {
      res.removeHeader(toStrip);
    });
    next();
  };
}
<|endoftext|>