<commit_before>import {
  h,
  render,
  props,
  BoltComponent,
  define,
  css,
  spacingSizes,
  hasNativeShadowDomSupport,
} from '@bolt/core';

const isActiveClass = 'is-active';

@define
class BoltNavLink extends BoltComponent() {
  static is = 'bolt-navlink';

  // The element reacts to changes to the `active` attribute.
  static get observedAttributes() {
    return ['active'];
  }

  constructor() {
    super();

    this._shadowLink = this.querySelector('a');
  }

  // Returns whether or not the current `<bolt-navlink>` element has been active.
  get active() {
    return this.hasAttribute('active');
  }

  // Sets the `active` state for the current custom element
  set active(value) {
    /* Properties can be set to all kinds of string values. This
     * makes sure it’s converted to a proper boolean value using
     * JavaScript’s truthiness & falsiness principles.
     */

    value = Boolean(value);
    if (value) {
      this.setAttribute('active', '');
    } else {
      this.removeAttribute('active');
    }
  }

  // `attributeChangedCallback` processes changes to the `active` attr
  attributeChangedCallback(name, oldVal, newVal) {
    switch (name) {
      case 'active':
        if (this.active) {
          this._shadowLink.classList.add(isActiveClass);

          // Dispatch an event that signals to the parent what element is being active
          this.dispatchEvent(
            new CustomEvent('activateLink', {
              detail: {
                isActiveNow: true,
              },
              bubbles: true,
            }),
          );
        }
        else {
          this._shadowLink.classList.remove(isActiveClass);
        }
    }
  }

  onClick() {
    if (!this.active) {
      this.active = true;
    }
  }

  render() {
    return this.html`
      ${this.slot('default')}
    `
  }

  connecting() {
    this.addEventListener('click', this.onClick);

    // Set an initially active link if appropriate.
    const isAlreadyActive = this._shadowLink.classList.contains(isActiveClass) || this._shadowLink.getAttribute('href') === window.location.hash;

    if (isAlreadyActive) {
      this.active = true;
    }

    this._upgradeProperty('active');
  }

  // See https://developers.google.com/web/fundamentals/web-components/best-practices#lazy-properties
  // for an explanation of lazy properties.
  _upgradeProperty(prop) {
    if (this.hasOwnProperty(prop)) {
      let value = this[prop];
      delete this[prop];
      this[prop] = value;
    }
  }

  disconnecting() {
    this.removeEventListener('click', this.onClick);
  }
}
<commit_msg>add reset for shadowlink (#657)<commit_after>import {
  h,
  render,
  props,
  BoltComponent,
  define,
  css,
  spacingSizes,
  hasNativeShadowDomSupport,
} from '@bolt/core';

const isActiveClass = 'is-active';

@define
class BoltNavLink extends BoltComponent() {
  static is = 'bolt-navlink';

  // The element reacts to changes to the `active` attribute.
  static get observedAttributes() {
    return ['active'];
  }

  constructor() {
    super();

    this._shadowLink = this.querySelector('a');
  }

  // Returns whether or not the current `<bolt-navlink>` element has been active.
  get active() {
    return this.hasAttribute('active');
  }

  // Sets the `active` state for the current custom element
  set active(value) {
    /* Properties can be set to all kinds of string values. This
     * makes sure it’s converted to a proper boolean value using
     * JavaScript’s truthiness & falsiness principles.
     */

    value = Boolean(value);
    if (value) {
      this.setAttribute('active', '');
    } else {
      this.removeAttribute('active');
    }
  }

  // Fix needed for Firefox and IE in which children are not available when constructor is called
  resetShadowLink() {
    this._shadowLink = this.querySelector('a');
  }

  // `attributeChangedCallback` processes changes to the `active` attr
  attributeChangedCallback(name, oldVal, newVal) {
    switch (name) {
      case 'active':
        if (!this._shadowLink) {
          this.resetShadowLink();
        }
        else if (this.active) {
          this._shadowLink.classList.add(isActiveClass);

          // Dispatch an event that signals to the parent what element is being active
          this.dispatchEvent(
            new CustomEvent('activateLink', {
              detail: {
                isActiveNow: true,
              },
              bubbles: true,
            }),
          );
        }
        else {
          this._shadowLink.classList.remove(isActiveClass);
        }
    }
  }

  onClick() {
    if (!this.active) {
      this.active = true;
    }
  }

  render() {
    return this.html`
      ${this.slot('default')}
    `
  }

  connecting() {
    this.addEventListener('click', this.onClick);

    // Set an initially active link if appropriate.
    if (!this._shadowLink) {
      this.resetShadowLink();
    }
    else {
      const isAlreadyActive = this._shadowLink.classList.contains(isActiveClass) || this._shadowLink.getAttribute('href') === window.location.hash;

      if (isAlreadyActive) {
        this.active = true;
      }
    }

    this._upgradeProperty('active');
  }

  // See https://developers.google.com/web/fundamentals/web-components/best-practices#lazy-properties
  // for an explanation of lazy properties.
  _upgradeProperty(prop) {
    if (this.hasOwnProperty(prop)) {
      let value = this[prop];
      delete this[prop];
      this[prop] = value;
    }
  }

  disconnecting() {
    this.removeEventListener('click', this.onClick);
  }
}
<|endoftext|>


<commit_before>/* global describe, it *//* jshint -W030 */

"use strict";

var expect = require('chai').expect;
var welsh = require('../index');

describe("Welsh", function () {
  it("should work", function (done) {
    welsh(function (resolve, reject) {
      resolve('bill');
    }).then(function (result) {
      expect(result).to.equal('bill');
      return 'hello ' + result;
    }).then(function (result) {
      expect(result).to.equal('hello bill');
      return '"---' + result + '---"';
    }).then(/* fall through */).then(function (result) {
      expect(result).to.equal('"---hello bill---"');
      var np = welsh();
      setTimeout(function () {
        var another = welsh();
        np.resolve(another);
        setTimeout(function () {
          another.resolve('***' + result + '***');
        }, 100);
      }, 100);
      return np;
    }).then(function (result) {
      expect(result).to.equal('***"---hello bill---"***');
      return '///' + result + '\\\\\\';
    }).then(function (result) {
      expect(result).to.equal('///***"---hello bill---"***\\\\\\');
      done();
    });
  });

  it("should handle exceptions", function (done) {
    var p = welsh().catch(function (err) {
      expect(err).to.equal("an error!");
      return 'totally ' + err;
    }).then(function (result) {
      expect(result).to.equal("totally an error!");
      var p = welsh();
      setTimeout(function () {
        p.reject(result + ' later');
      }, 100);
      return p;
    }).catch(function (err) {
      expect(err).to.equal("totally an error! later");
      done();
    });

    p.reject("an error!");
  });

  it("should accept values before 'then'", function (done) {
    var p = welsh();
    p.resolve('hello');
    p.then(function (result) {
      expect(result).to.equal('hello');
      done();
    });
  });

  it("should explode if you re-resolve", function (done) {
    var p = welsh();
    p.resolve('hello');
    expect(function () {
      p.resolve('uh-oh!');
    }).to.throw(Error);
    done();
  });

  it("should be able to continue", function (done) {
    var p = welsh().then(function (result) {
      expect(result).to.equal('Bob');
      return "Hello, " + result + "!";
    });

    p.resolve('Bob');
    p.then(function (result) {
      expect(result).to.equal('Hello, Bob!');
      return result + ' ***';
    });

    p.then(function (result) {
      expect(result).to.equal('Hello, Bob! ***');
      done();
    });
  });

  it("should allow re-entrant 'then'", function (done) {
    var p = welsh();
    p.then(function (result) {
      expect(result).to.equal('Bill');
      p.then(function (result) {
        expect(result).to.equal("Hello, Bill! How are you?");
        return result + " I'm fine!";
      });
      return 'Hello, ' + result + '!';
    }).then(function (result) {
      expect(result).to.equal("Hello, Bill!");
      return result + " How are you?";
    });

    p.resolve('Bill');
    p.then(function (result) {
      expect(result).to.equal("Hello, Bill! How are you? I'm fine!");
      done();
    });
  });
});
<commit_msg>allow for executor<commit_after>/* global describe, it *//* jshint -W030 */

"use strict";

var expect = require('chai').expect;
var welsh = require('../index');

describe("Welsh", function () {
  it("should work", function (done) {
    welsh(function (resolve, reject) {
      expect(resolve).to.be.a('function');
      expect(reject).to.be.a('function');
      resolve('bill');
    }).then(function (result) {
      expect(result).to.equal('bill');
      return 'hello ' + result;
    }).then(function (result) {
      expect(result).to.equal('hello bill');
      return '"---' + result + '---"';
    }).then(/* fall through */).then(function (result) {
      expect(result).to.equal('"---hello bill---"');
      var np = welsh();
      setTimeout(function () {
        var another = welsh();
        np.resolve(another);
        setTimeout(function () {
          another.resolve('***' + result + '***');
        }, 100);
      }, 100);
      return np;
    }).then(function (result) {
      expect(result).to.equal('***"---hello bill---"***');
      return '///' + result + '\\\\\\';
    }).then(function (result) {
      expect(result).to.equal('///***"---hello bill---"***\\\\\\');
      done();
    });
  });

  it("should handle exceptions", function (done) {
    var p = welsh().catch(function (err) {
      expect(err).to.equal("an error!");
      return 'totally ' + err;
    }).then(function (result) {
      expect(result).to.equal("totally an error!");
      var p = welsh();
      setTimeout(function () {
        p.reject(result + ' later');
      }, 100);
      return p;
    }).catch(function (err) {
      expect(err).to.equal("totally an error! later");
      done();
    });

    p.reject("an error!");
  });

  it("should accept values before 'then'", function (done) {
    var p = welsh();
    p.resolve('hello');
    p.then(function (result) {
      expect(result).to.equal('hello');
      done();
    });
  });

  it("should explode if you re-resolve", function (done) {
    var p = welsh();
    p.resolve('hello');
    expect(function () {
      p.resolve('uh-oh!');
    }).to.throw(Error);
    done();
  });

  it("should be able to continue", function (done) {
    var p = welsh().then(function (result) {
      expect(result).to.equal('Bob');
      return "Hello, " + result + "!";
    });

    p.resolve('Bob');
    p.then(function (result) {
      expect(result).to.equal('Hello, Bob!');
      return result + ' ***';
    });

    p.then(function (result) {
      expect(result).to.equal('Hello, Bob! ***');
      done();
    });
  });

  it("should allow re-entrant 'then'", function (done) {
    var p = welsh();
    p.then(function (result) {
      expect(result).to.equal('Bill');
      p.then(function (result) {
        expect(result).to.equal("Hello, Bill! How are you?");
        return result + " I'm fine!";
      });
      return 'Hello, ' + result + '!';
    }).then(function (result) {
      expect(result).to.equal("Hello, Bill!");
      return result + " How are you?";
    });

    p.resolve('Bill');
    p.then(function (result) {
      expect(result).to.equal("Hello, Bill! How are you? I'm fine!");
      done();
    });
  });
});
<|endoftext|>


<commit_before>// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

// Define search commands. Depends on dialog.js or another
// implementation of the openDialog method.

// Replace works a little oddly -- it will do the replace on the next
// Ctrl-G (or whatever is bound to findNext) press. You prevent a
// replace by making sure the match is no longer selected when hitting
// Ctrl-G.

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"), require("./searchcursor"), require("../dialog/dialog"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  "use strict";

  function searchOverlay(query, caseInsensitive) {
    if (typeof query == "string")
      query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g");
    else if (!query.global)
      query = new RegExp(query.source, query.ignoreCase ? "gi" : "g");

    return {token: function(stream) {
      query.lastIndex = stream.pos;
      var match = query.exec(stream.string);
      if (match && match.index == stream.pos) {
        stream.pos += match[0].length || 1;
        return "searching";
      } else if (match) {
        stream.pos = match.index;
      } else {
        stream.skipToEnd();
      }
    }};
  }

  function SearchState() {
    this.posFrom = this.posTo = this.lastQuery = this.query = null;
    this.overlay = null;
  }

  function getSearchState(cm) {
    return cm.state.search || (cm.state.search = new SearchState());
  }

  function queryCaseInsensitive(query) {
    return typeof query == "string" && query == query.toLowerCase();
  }

  function getSearchCursor(cm, query, pos) {
    // Heuristic: if the query string is all lowercase, do a case insensitive search.
    return cm.getSearchCursor(query, pos, queryCaseInsensitive(query));
  }

  function persistentDialog(cm, text, deflt, f) {
    cm.openDialog(text, f, {
      value: deflt,
      selectValueOnOpen: true,
      closeOnEnter: false,
      onClose: function() { clearSearch(cm); }
    });
  }

  function dialog(cm, text, shortText, deflt, f) {
    if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
    else f(prompt(shortText, deflt));
  }

  function confirmDialog(cm, text, shortText, fs) {
    if (cm.openConfirm) cm.openConfirm(text, fs);
    else if (confirm(shortText)) fs[0]();
  }

  function parseString(string) {
    return string.replace(/\\(.)/g, function(_, ch) {
      if (ch == "n") return "\n"
      if (ch == "r") return "\r"
      return ch
    })
  }

  function parseQuery(query) {
    var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
    if (isRE) {
      try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); }
      catch(e) {} // Not a regular expression after all, do a string search
    } else {
      query = parseString(query)
    }
    if (typeof query == "string" ? query == "" : query.test(""))
      query = /x^/;
    return query;
  }

  var queryDialog =
    'Search: <input type="text" style="width: 10em" class="CodeMirror-search-field"/> (Use /re/ syntax for regexp search)';

  function startSearch(cm, state, query) {
    state.queryText = query;
    state.query = parseQuery(query);
    cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
    state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
    cm.addOverlay(state.overlay);
    if (cm.showMatchesOnScrollbar) {
      if (state.annotate) { state.annotate.clear(); state.annotate = null; }
      state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
    }
  }

  function doSearch(cm, rev, persistent) {
    var state = getSearchState(cm);
    if (state.query) return findNext(cm, rev);
    var q = cm.getSelection() || state.lastQuery;
    if (persistent && cm.openDialog) {
      var hiding = null
      persistentDialog(cm, queryDialog, q, function(query, event) {
        CodeMirror.e_stop(event);
        if (!query) return;
        if (query != state.queryText) {
          startSearch(cm, state, query);
          state.posFrom = state.posTo = cm.getCursor();
        }
        if (hiding) hiding.style.opacity = 1
        findNext(cm, event.shiftKey, function(_, to) {
          var dialog
          if (to.line < 3 && document.querySelector &&
              (dialog = cm.display.wrapper.querySelector(".CodeMirror-dialog")) &&
              dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top)
            (hiding = dialog).style.opacity = .4
        })
        scrollToCursor(cm);
      });
    } else {
      dialog(cm, queryDialog, "Search for:", q, function(query) {
        if (query && !state.query) cm.operation(function() {
          startSearch(cm, state, query);
          state.posFrom = state.posTo = cm.getCursor();
          findNext(cm, rev);
          scrollToCursor(cm);
        });
      });
    }
  }

  function scrollToCursor(cm) {
    var cursor = cm.getCursor();
    var line = cursor.line;
    var char = cursor.ch;
    var range = getSelectedRange(cm, false);
    cm.setCursor({line:line,ch:char});
    var myHeight = cm.getScrollInfo().clientHeight;
    var coords = cm.charCoords({line: line, ch: char}, "global");
    window.scrollTo(0, (coords.top + coords.bottom - myHeight) / 2);
    cm.setSelection(range.from, range.to);
  }

  function getSelectedRange(cm, all) {
    var start = cm.getCursor(true),
        end = cm.getCursor(false);
    if(typeof all === "undefined") all = true;
    if(all && start == end) return { from: {line: 0, ch: 0}, to: {line: cm.lineCount()} }
    return { from: cm.getCursor(true), to: cm.getCursor(false) };
  }

  function findNext(cm, rev, callback) {cm.operation(function() {
    var state = getSearchState(cm);
    var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
    if (!cursor.find(rev)) {
      cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
      if (!cursor.find(rev)) return;
    }
    cm.setSelection(cursor.from(), cursor.to());
    cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);
    state.posFrom = cursor.from(); state.posTo = cursor.to();
    if (callback) callback(cursor.from(), cursor.to())
    scrollToCursor(cm);
  });}

  function clearSearch(cm) {cm.operation(function() {
    var state = getSearchState(cm);
    state.lastQuery = state.query;
    if (!state.query) return;
    state.query = state.queryText = null;
    cm.removeOverlay(state.overlay);
    if (state.annotate) { state.annotate.clear(); state.annotate = null; }
  });}

  var replaceQueryDialog =
    ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> (Use /re/ syntax for regexp search)';
  var replacementQueryDialog = 'With: <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
  var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>All</button> <button>Stop</button>";

  function replaceAll(cm, query, text) {
    cm.operation(function() {
      for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
        if (typeof query != "string") {
          var match = cm.getRange(cursor.from(), cursor.to()).match(query);
          cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
        } else cursor.replace(text);
      }
    });
  }

  function replace(cm, all) {
    if (cm.getOption("readOnly")) return;
    var query = cm.getSelection() || getSearchState(cm).lastQuery;
    var dialogText = all ? "Replace all:" : "Replace:"
    dialog(cm, dialogText + replaceQueryDialog, dialogText, query, function(query) {
      if (!query) return;
      query = parseQuery(query);
      dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) {
        text = parseString(text)
        if (all) {
          replaceAll(cm, query, text)
        } else {
          clearSearch(cm);
          var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
          var advance = function() {
            var start = cursor.from(), match;
            if (!(match = cursor.findNext())) {
              cursor = getSearchCursor(cm, query);
              if (!(match = cursor.findNext()) ||
                  (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
            }
            cm.setSelection(cursor.from(), cursor.to());
            cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
            confirmDialog(cm, doReplaceConfirm, "Replace?",
                          [function() {doReplace(match);}, advance,
                           function() {replaceAll(cm, query, text)}]);
          };
          var doReplace = function(match) {
            cursor.replace(typeof query == "string" ? text :
                           text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
            advance();
          };
          advance();
        }
      });
    });
  }

  CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
  CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};
  CodeMirror.commands.findNext = doSearch;
  CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
  CodeMirror.commands.clearSearch = clearSearch;
  CodeMirror.commands.replace = replace;
  CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
  CodeMirror.commands.scrollToCursor = function(cm) {scrollToCursor(cm);};
  CodeMirror.commands.getSelectedRange = function(cm, all) {getSelectedRange(cm, all);};
});
<commit_msg>search.js: find next /prev according to cursor, dialog from start<commit_after>// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

// Define search commands. Depends on dialog.js or another
// implementation of the openDialog method.

// Replace works a little oddly -- it will do the replace on the next
// Ctrl-G (or whatever is bound to findNext) press. You prevent a
// replace by making sure the match is no longer selected when hitting
// Ctrl-G.

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"), require("./searchcursor"), require("../dialog/dialog"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  "use strict";

  function searchOverlay(query, caseInsensitive) {
    if (typeof query == "string")
      query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g");
    else if (!query.global)
      query = new RegExp(query.source, query.ignoreCase ? "gi" : "g");

    return {token: function(stream) {
      query.lastIndex = stream.pos;
      var match = query.exec(stream.string);
      if (match && match.index == stream.pos) {
        stream.pos += match[0].length || 1;
        return "searching";
      } else if (match) {
        stream.pos = match.index;
      } else {
        stream.skipToEnd();
      }
    }};
  }

  function SearchState() {
    this.posFrom = this.posTo = this.lastQuery = this.query = null;
    this.overlay = null;
  }

  function getSearchState(cm) {
    return cm.state.search || (cm.state.search = new SearchState());
  }

  function queryCaseInsensitive(query) {
    return typeof query == "string" && query == query.toLowerCase();
  }

  function getSearchCursor(cm, query, pos) {
    // Heuristic: if the query string is all lowercase, do a case insensitive search.
    return cm.getSearchCursor(query, pos, queryCaseInsensitive(query));
  }

  function persistentDialog(cm, text, deflt, f) {
    cm.openDialog(text, f, {
      value: deflt,
      selectValueOnOpen: true,
      closeOnEnter: false,
      onClose: function() { clearSearch(cm); }
    });
  }

  function dialog(cm, text, shortText, deflt, f) {
    if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
    else f(prompt(shortText, deflt));
  }

  function confirmDialog(cm, text, shortText, fs) {
    if (cm.openConfirm) cm.openConfirm(text, fs);
    else if (confirm(shortText)) fs[0]();
  }

  function parseString(string) {
    return string.replace(/\\(.)/g, function(_, ch) {
      if (ch == "n") return "\n"
      if (ch == "r") return "\r"
      return ch
    })
  }

  function parseQuery(query) {
    var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
    if (isRE) {
      try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); }
      catch(e) {} // Not a regular expression after all, do a string search
    } else {
      query = parseString(query)
    }
    if (typeof query == "string" ? query == "" : query.test(""))
      query = /x^/;
    return query;
  }

  var queryDialog =
    'Search: <input type="text" style="width: 10em" class="CodeMirror-search-field"/> (Use /re/ syntax for regexp search)';

  function startSearch(cm, state, query) {
    state.queryText = query;
    state.query = parseQuery(query);
    cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
    state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
    cm.addOverlay(state.overlay);
    if (cm.showMatchesOnScrollbar) {
      if (state.annotate) { state.annotate.clear(); state.annotate = null; }
      state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
    }
  }

  function doSearch(cm, rev, persistent) {
    var state = getSearchState(cm);
    if (state.query) return findNext(cm, rev);
    var q = cm.getSelection() || state.lastQuery;
    if (persistent && cm.openDialog) {
      var hiding = null
      persistentDialog(cm, queryDialog, q, function(query, event) {
        CodeMirror.e_stop(event);
        if (!query) return;
        if (query != state.queryText) startSearch(cm, state, query);
        if (hiding) hiding.style.opacity = 1;
        findNext(cm, event.shiftKey, function(_, to) {
          var dialog
          if (to.line < 3 && document.querySelector &&
              (dialog = cm.display.wrapper.querySelector(".CodeMirror-dialog")) &&
              dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top)
            (hiding = dialog).style.opacity = .4
        })
        scrollToCursor(cm);
      });
    } else {
      dialog(cm, queryDialog, "Search for:", q, function(query) {
        if (query && !state.query) cm.operation(function() {
          startSearch(cm, state, query);
          findNext(cm, rev, null, true);
          scrollToCursor(cm);
        });
      });
    }
  }

  function scrollToCursor(cm) {
    var cursor = cm.getCursor();
    var line = cursor.line;
    var char = cursor.ch;
    var range = getSelectedRange(cm, false);
    cm.setCursor({line:line,ch:char});
    var myHeight = cm.getScrollInfo().clientHeight;
    var coords = cm.charCoords({line: line, ch: char}, "global");
    window.scrollTo(0, (coords.top + coords.bottom - myHeight) / 2);
    cm.setSelection(range.from, range.to);
  }

  function getSelectedRange(cm, all) {
    var start = cm.getCursor(true),
        end = cm.getCursor(false);
    if(typeof all === "undefined") all = true;
    if(all && start == end) return { from: {line: 0, ch: 0}, to: {line: cm.lineCount()} }
    return { from: cm.getCursor(true), to: cm.getCursor(false) };
  }

  function findNext(cm, rev, callback, start=false) {cm.operation(function() {
    var state = getSearchState(cm);
    var cursor = getSearchCursor(cm, state.query, start == true ? CodeMirror.Pos(cm.firstLine(), 0) : (rev ? cm.getCursor(true) : cm.getCursor(false)));
    if (!cursor.find(rev)) {
      cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
      if (!cursor.find(rev)) return;
    }
    cm.setSelection(cursor.from(), cursor.to());
    cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);
    state.posFrom = cursor.from(); state.posTo = cursor.to();
    if (callback) callback(cursor.from(), cursor.to())
    scrollToCursor(cm);
  });}

  function clearSearch(cm) {cm.operation(function() {
    var state = getSearchState(cm);
    state.lastQuery = state.query;
    if (!state.query) return;
    state.query = state.queryText = null;
    cm.removeOverlay(state.overlay);
    if (state.annotate) { state.annotate.clear(); state.annotate = null; }
  });}

  var replaceQueryDialog =
    ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> (Use /re/ syntax for regexp search)';
  var replacementQueryDialog = 'With: <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
  var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>All</button> <button>Stop</button>";

  function replaceAll(cm, query, text) {
    cm.operation(function() {
      for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
        if (typeof query != "string") {
          var match = cm.getRange(cursor.from(), cursor.to()).match(query);
          cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
        } else cursor.replace(text);
      }
    });
  }

  function replace(cm, all) {
    if (cm.getOption("readOnly")) return;
    var query = cm.getSelection() || getSearchState(cm).lastQuery;
    var dialogText = all ? "Replace all:" : "Replace:"
    dialog(cm, dialogText + replaceQueryDialog, dialogText, query, function(query) {
      if (!query) return;
      query = parseQuery(query);
      dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) {
        text = parseString(text)
        if (all) {
          replaceAll(cm, query, text)
        } else {
          clearSearch(cm);
          var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
          var advance = function() {
            var start = cursor.from(), match;
            if (!(match = cursor.findNext())) {
              cursor = getSearchCursor(cm, query);
              if (!(match = cursor.findNext()) ||
                  (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
            }
            cm.setSelection(cursor.from(), cursor.to());
            cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
            confirmDialog(cm, doReplaceConfirm, "Replace?",
                          [function() {doReplace(match);}, advance,
                           function() {replaceAll(cm, query, text)}]);
          };
          var doReplace = function(match) {
            cursor.replace(typeof query == "string" ? text :
                           text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
            advance();
          };
          advance();
        }
      });
    });
  }

  CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
  CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};
  CodeMirror.commands.findNext = doSearch;
  CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
  CodeMirror.commands.clearSearch = clearSearch;
  CodeMirror.commands.replace = replace;
  CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
  CodeMirror.commands.scrollToCursor = function(cm) {scrollToCursor(cm);};
  CodeMirror.commands.getSelectedRange = function(cm, all) {getSelectedRange(cm, all);};
});
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 - present Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*global Promise */
/*eslint-env node */
/*jslint node: true */
"use strict";

module.exports = function (grunt) {

    var _       = require("lodash"),
        build   = require("./build")(grunt),
        common  = require("./lib/common")(grunt),
        exec    = require("child_process").exec,
        fs      = require("fs-extra"),
        glob    = require("glob"),
        https   = require("https"),
        path    = require("path"),
        tar     = require("tar"),
        temp    = require("temp"),
        zlib    = require("zlib");

    temp.track();

    function runNpmInstall(where, callback) {
        grunt.log.writeln("running npm install --production in " + where);
        exec('npm install --production', { cwd: './' + where }, function (err, stdout, stderr) {
            if (err) {
                grunt.log.error(stderr);
            } else {
                grunt.log.writeln(stdout || "finished npm install in " + where);
            }
            return err ? callback(stderr) : callback(null, stdout);
        });
    }

    grunt.registerTask("npm-install", "Install node_modules to the dist folder so it gets bundled with release", function () {
        var npmShrinkwrapJSON = grunt.file.readJSON("npm-shrinkwrap.json");
        common.writeJSON(grunt, "dist/npm-shrinkwrap.json", npmShrinkwrapJSON);

        var packageJSON = grunt.file.readJSON("package.json");
        delete packageJSON.devDependencies;
        delete packageJSON.scripts; // we don't want to run post-install scripts in dist folder
        common.writeJSON(grunt, "dist/package.json", packageJSON);

        var done = this.async();
        runNpmInstall("dist", function (err) {
            return err ? done(false) : done();
        });
    });

    grunt.registerTask("npm-install-src", "Install node_modules to the src folder", function () {
        var done = this.async();
        runNpmInstall("src", function (err) {
            return err ? done(false) : done();
        });
    });

    grunt.registerTask("npm-install-extensions", "Install node_modules for default extensions which have package.json defined", function () {
        var _done = this.async();
        glob("src/extensions/**/package.json", function (err, files) {
            if (err) {
                grunt.log.error(err);
                return _done(false);
            }
            files = files.filter(function (path) {
                return path.indexOf("node_modules") === -1;
            });
            var done = _.after(files.length, _done);
            files.forEach(function (file) {
                runNpmInstall(path.dirname(file), function (err) {
                    return err ? _done(false) : done();
                });
            });
        });
    });

    grunt.registerTask(
        "npm-install-source",
        "Install node_modules for src folder and default extensions which have package.json defined",
        ["npm-install-src", "copy:thirdparty", "npm-install-extensions"]
    );

    function getNodeModulePackageUrl(extensionName) {
        return new Promise(function (resolve, reject) {
            exec("npm view " + extensionName + " dist.tarball", {}, function (err, stdout, stderr) {
                if (err) {
                    grunt.log.error(stderr);
                }
                return err ? reject(stderr) : resolve(stdout.toString("utf8").trim());
            });
        });
    }

    function getTempDirectory(tempName) {
        return new Promise(function (resolve, reject) {
            temp.mkdir(tempName, function(err, dirPath) {
                return err ? reject(err) : resolve(dirPath);
            });
        });
    }

    function downloadUrlToFolder(url, dirPath) {
        return new Promise(function (resolve, reject) {
            grunt.log.writeln(url + " downloading...");
            https.get(url, function (res) {

                if (res.statusCode !== 200) {
                    return reject(new Error("Request failed: " + res.statusCode));
                }

                var unzipStream = zlib.createGunzip();
                res.pipe(unzipStream);

                var extractStream = tar.Extract({ path: dirPath, strip: 0 });
                unzipStream.pipe(extractStream);

                extractStream.on('finish', function() {
                    grunt.log.writeln(url + " successfully downloaded");
                    resolve(path.resolve(dirPath, "package"));
                });

            }).on('error', function(err) {
                reject(err);
            });
        });
    }

    function move(from, to) {
        return new Promise(function (resolve, reject) {
            fs.remove(to, function (err) {
                if (err) {
                    return reject(err);
                }
                fs.move(from, to, function (err) {
                    if (err) {
                        return reject(err);
                    }
                    return resolve();
                });
            });
        });
    }

    function downloadAndInstallExtensionFromNpm(obj) {
        var extensionName = obj.name;
        var extensionVersion = obj.version ? "@" + obj.version : "";
        var data = {};
        return getNodeModulePackageUrl(extensionName + extensionVersion)
            .then(function (urlToDownload) {
                data.urlToDownload = urlToDownload;
                return getTempDirectory(extensionName);
            })
            .then(function (tempDirPath) {
                data.tempDirPath = tempDirPath;
                return downloadUrlToFolder(data.urlToDownload, data.tempDirPath);
            })
            .then(function (extensionPath) {
                var target = path.resolve(__dirname, '..', 'src', 'extensions', 'default', extensionName);
                return move(extensionPath, target);
            });
    }

    grunt.registerTask("npm-download-default-extensions",
                       "Downloads extensions from npm and puts them to the src/extensions/default folder",
                       function () {

        var packageJSON = grunt.file.readJSON("package.json");
        var extensionsToDownload = Object.keys(packageJSON.defaultExtensions).map(function (name) {
            return {
                name: name,
                version: packageJSON.defaultExtensions[name]
            };
        });

        var done = this.async();
        Promise.all(extensionsToDownload.map(function (extension) {
            return downloadAndInstallExtensionFromNpm(extension);
        })).then(function () {
            return done();
        }).catch(function (err) {
            grunt.log.error(err);
            return done(false);
        });

    });

};
<commit_msg>Missed running npm-install on JSUtils<commit_after>/*
 * Copyright (c) 2013 - present Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*global Promise */
/*eslint-env node */
/*jslint node: true */
"use strict";

module.exports = function (grunt) {

    var _       = require("lodash"),
        build   = require("./build")(grunt),
        common  = require("./lib/common")(grunt),
        exec    = require("child_process").exec,
        fs      = require("fs-extra"),
        glob    = require("glob"),
        https   = require("https"),
        path    = require("path"),
        tar     = require("tar"),
        temp    = require("temp"),
        zlib    = require("zlib");

    temp.track();

    function runNpmInstall(where, callback) {
        grunt.log.writeln("running npm install --production in " + where);
        exec('npm install --production', { cwd: './' + where }, function (err, stdout, stderr) {
            if (err) {
                grunt.log.error(stderr);
            } else {
                grunt.log.writeln(stdout || "finished npm install in " + where);
            }
            return err ? callback(stderr) : callback(null, stdout);
        });
    }

    grunt.registerTask("npm-install", "Install node_modules to the dist folder so it gets bundled with release", function () {
        var npmShrinkwrapJSON = grunt.file.readJSON("npm-shrinkwrap.json");
        common.writeJSON(grunt, "dist/npm-shrinkwrap.json", npmShrinkwrapJSON);

        var packageJSON = grunt.file.readJSON("package.json");
        delete packageJSON.devDependencies;
        delete packageJSON.scripts; // we don't want to run post-install scripts in dist folder
        common.writeJSON(grunt, "dist/package.json", packageJSON);

        var done = this.async();
        runNpmInstall("dist", function (err) {
            return err ? done(false) : done();
        });
    });

    grunt.registerTask("npm-install-src", "Install node_modules to the src folder", function () {
        var _done = this.async(),
            dirs = ["src", "src/JSUtils", "src/JSUtils/node"],
            done = _.after(dirs.length, _done);
        dirs.forEach(function (dir) {
            runNpmInstall(dir, function (err) {
                return err ? done(false) : done();
            });
        });
    });

    grunt.registerTask("npm-install-extensions", "Install node_modules for default extensions which have package.json defined", function () {
        var _done = this.async();
        glob("src/extensions/**/package.json", function (err, files) {
            if (err) {
                grunt.log.error(err);
                return _done(false);
            }
            files = files.filter(function (path) {
                return path.indexOf("node_modules") === -1;
            });
            var done = _.after(files.length, _done);
            files.forEach(function (file) {
                runNpmInstall(path.dirname(file), function (err) {
                    return err ? _done(false) : done();
                });
            });
        });
    });

    grunt.registerTask(
        "npm-install-source",
        "Install node_modules for src folder and default extensions which have package.json defined",
        ["npm-install-src", "copy:thirdparty", "npm-install-extensions"]
    );

    function getNodeModulePackageUrl(extensionName) {
        return new Promise(function (resolve, reject) {
            exec("npm view " + extensionName + " dist.tarball", {}, function (err, stdout, stderr) {
                if (err) {
                    grunt.log.error(stderr);
                }
                return err ? reject(stderr) : resolve(stdout.toString("utf8").trim());
            });
        });
    }

    function getTempDirectory(tempName) {
        return new Promise(function (resolve, reject) {
            temp.mkdir(tempName, function(err, dirPath) {
                return err ? reject(err) : resolve(dirPath);
            });
        });
    }

    function downloadUrlToFolder(url, dirPath) {
        return new Promise(function (resolve, reject) {
            grunt.log.writeln(url + " downloading...");
            https.get(url, function (res) {

                if (res.statusCode !== 200) {
                    return reject(new Error("Request failed: " + res.statusCode));
                }

                var unzipStream = zlib.createGunzip();
                res.pipe(unzipStream);

                var extractStream = tar.Extract({ path: dirPath, strip: 0 });
                unzipStream.pipe(extractStream);

                extractStream.on('finish', function() {
                    grunt.log.writeln(url + " successfully downloaded");
                    resolve(path.resolve(dirPath, "package"));
                });

            }).on('error', function(err) {
                reject(err);
            });
        });
    }

    function move(from, to) {
        return new Promise(function (resolve, reject) {
            fs.remove(to, function (err) {
                if (err) {
                    return reject(err);
                }
                fs.move(from, to, function (err) {
                    if (err) {
                        return reject(err);
                    }
                    return resolve();
                });
            });
        });
    }

    function downloadAndInstallExtensionFromNpm(obj) {
        var extensionName = obj.name;
        var extensionVersion = obj.version ? "@" + obj.version : "";
        var data = {};
        return getNodeModulePackageUrl(extensionName + extensionVersion)
            .then(function (urlToDownload) {
                data.urlToDownload = urlToDownload;
                return getTempDirectory(extensionName);
            })
            .then(function (tempDirPath) {
                data.tempDirPath = tempDirPath;
                return downloadUrlToFolder(data.urlToDownload, data.tempDirPath);
            })
            .then(function (extensionPath) {
                var target = path.resolve(__dirname, '..', 'src', 'extensions', 'default', extensionName);
                return move(extensionPath, target);
            });
    }

    grunt.registerTask("npm-download-default-extensions",
                       "Downloads extensions from npm and puts them to the src/extensions/default folder",
                       function () {

        var packageJSON = grunt.file.readJSON("package.json");
        var extensionsToDownload = Object.keys(packageJSON.defaultExtensions).map(function (name) {
            return {
                name: name,
                version: packageJSON.defaultExtensions[name]
            };
        });

        var done = this.async();
        Promise.all(extensionsToDownload.map(function (extension) {
            return downloadAndInstallExtensionFromNpm(extension);
        })).then(function () {
            return done();
        }).catch(function (err) {
            grunt.log.error(err);
            return done(false);
        });

    });

};
<|endoftext|>


<commit_before>'use strict';

if (!process.env.DEBUG) {
	process.env.DEBUG = '*,-' + [
		'body-parser:urlencoded',
		'compression',
		'connect:dispatcher',
		'node-soap',
		'time'
	].join(',-');
}
<commit_msg>Ignore logs from soap<commit_after>'use strict';

if (!process.env.DEBUG) {
	process.env.DEBUG = '*,-' + [
		'body-parser:urlencoded',
		'compression',
		'connect:dispatcher',
		'node-soap',
		'soap',
		'time'
	].join(',-');
}
<|endoftext|>


<commit_before>'use strict';

(function (root, factory) {
    // Backbone-style settings
    if (typeof define === 'function' && define.amd) {
        define(['exports'], function (exports) {
            root.Morphine = factory(root/*, exports*/);
        });
    } else if (typeof module !== 'undefined') {
        module.exports = factory(root/*, exports*/);
    } else {
        root.Morphine = factory(root/*, {}*/);
    }
}(this, function (root) {

    /**
     * Конфигурационный объект для Morphine.
     * Настраивается через Morphine.config
     */
    var CONFIG = {

        /**
         * Разделительный символ, который будет использоваться в path.
         * По умолчанию это точка.
         */
        separator: '.'

    };

    function Morphine() {
        return MorphineBuilder.apply(this, arguments);
    }

    function MorphineArray() {}
    MorphineArray.prototype = new Array();
    MorphineArray.prototype = new MorphineArray();
    MorphineArray.prototype.constructor = MorphineArray;

    Morphine.prototype.version = '0.0.9';

    function MorphineBuilder () {
        var ln = arguments.length;

        switch (ln) {
            case 1:
                // м.б. объект или path-строка
                if (checkType(arguments[0], String)) {
                    setter.apply(this, arguments);
                } else
                // TODO: Возникают проблемы с преобразованием в Morphine, если arguments[0] массив
                if (checkType(arguments[0], Object) || checkType(arguments[0], Array)) {
                    converter.call(this, arguments[0], true);
                }

            break;

            case 2:
                // м.б. path+значение
                setter.apply(this, arguments);
            break;
        }
        return this;
    }

    var CommonPrototypeMixin = {
        isObject: function () {
            return this.constructor === Morphine;
        },
        isArray: function () {
            return this.constructor === MorphineArray;
        },
        /**
         * @public
         * Проверит свойство объекта на null
         * @param {String} key ключ элемента
         * @return {Boolean} Результат проверки. true - свойство === null, false - свойство !== null.
         **/
        isNull: function (key) {
            return this[key] === null;
        },
        /**
         * @public
         * Проверит свойство объекта на undefined
         * @param {String} key ключ элемента
         * @return {Boolean} Результат проверки. true - свойство === undefined, false - свойство !== undefined.
         **/
        isUndefined: function (key) {
            return typeof this[key] === 'undefined';
        },
        /**
         * @public
         * Проверит наличие свойства key в текущем объекте
         * @param {String} key ключ элемента
         * @return {Boolean} Результат проверки присутствия поля. true - присутствует, false - отсутствует.
         **/
        has: function (key) {
            return this.hasOwnProperty(key);
        },
        set: function (path, value) {
            setter.call(this, path, value);
            return this;
        },
        get: function (path) {
            var pathArray = path.split(CONFIG.separator);
            return getter(pathArray, this);
        },
        config: function () {
            Configure.apply(this, arguments);
            return this;
        }
    };

    var MorphinePrototypeMixin = {};
    var MorphineArrayPrototypeMixin = {};

    /**
     * Скопирует в прототип this все свойства объекта source
     * @param {Object} source объект с набором свойств для копирования
     **/
    Morphine.mixin = MorphineArray.mixin = function (source) {
        if (source) {
            for (var prop in source) {
                if (!source.hasOwnProperty(prop)) continue;
                this.prototype[prop] = source[prop];
            }
        }
    };

    Morphine.mixin(CommonPrototypeMixin);
    MorphineArray.mixin(CommonPrototypeMixin);

    /**
     * @private
     * Метод конфигурирует дальнейшую работу с объектом
     * @param {Object} options Набор свойств для CONFIG-объекта
     */
    function Configure (options) {
        if (checkType(options, Object)) {
            CONFIG.separator = options.separator ? options.separator : CONFIG.separator;
        }
    }

    /**
     * @private
     * Проверяет соответствие фактического типа real примитивным типам.
     * Если задан ожидаемый тип expect, то проверяется соответствие только с ним.
     */
    function checkType (real, expect) {
        expect = expect ? [expect] : [Boolean, String, Number];
        var ln = expect.length;
        for (var i = 0; i < ln; i++) {
            if (expect[i] === real || expect[i] === real.constructor) {
                return true;
            }
        }
        return false;
    }

    /**
     * @private
     * Метод выстроит объект по структуре указанной в path. Если указано
     * значение value - присвоит его последнему элементу.
     * @param {String} path Задает структуру объекта для построения
     * @param {*} value Значение последнего элемента в path
     **/
    function builder (path, value) {
        var pathArray = path.split(CONFIG.separator),
            intRegexp = /^[0-9]$/;

        innerBuilder.call(this, pathArray, value);

        function innerBuilder (pathArray, value) {
            var index = pathArray.shift();
            var testInt = intRegexp.test(pathArray[0]);
            var testCollection = pathArray[0] === '$';

            if (pathArray.length === 0) {
                if (index === '$') {
                    this.push(value);
                } else if (intRegexp.test(index)) {
                    if (typeof this[index] === 'undefined') {
                        // TODO: Заменить все console-выводы на исключения
                        console.error("Элемент %o не существует", index);
                    }
                } else {
                    this[index] = value;
                }
                return;
            } else {
                if (testInt || testCollection) {
                    if (testCollection) {
                        if (index === "$") {
                            this.push(new MorphineArray());
                        } else {
                            this[index] = new MorphineArray();
                        }
                    } else if (testInt) {
                        if (typeof this[index] === 'undefined') {
                            console.error("Элемент %o не существует", index);
                        }
                    } else {
                        this[index] = (typeof this[index] !== 'undefined') ? this[index] : new MorphineArray();
                    }
                } else {
                    if (intRegexp.test(index)) {
                        this[index] = (typeof this[index] !== 'undefined') ? this[index] : new Morphine();
                    } else if (index === '$') {
                        this.push(new Morphine());
                    } else {
                        this[index] = (typeof this[index] !== 'undefined') ? this[index] : new Morphine();
                    }
                }

                // Если в коллекцию $ был добавлен очередной элемент - получаем его индекс, для дальнейшего построения
                if (index === '$') {
                    index = this.length-1;
                }
            }

            innerBuilder.call(this[index], pathArray, value);
        }
    }

    /**
     * @private
     * Возвращает объект из источника
     * @param {Array} pathArray  массив содержащий путь до целевого элемента
     * @param {Object} source Объект-источник
     **/
    function getter(pathArray, source) {
        var index = pathArray.shift();

        if (!pathArray.length) {
            return source[index];
        }
        return getter(pathArray, source[index]);
    }

    /**
     * @private
     * Метод установит значение по указанному path. Если path или его часть
     * не существует, то недостающие элементы будут выстроены в соответствии
     * со структурой path.
     * @param {String} path Задает элемент для установки значения
     * @param {*} value Значение последнего элемента в path. По умолчанию - пустой объект
     * @return {Morphine} Получившийся объект
     **/
    function setter(path, value) {
        var valToSet = (typeof value !== 'undefined') ? value : {},
            checkResult = checkType(valToSet),
            mObject = null;

        if (!checkResult) {
            if (checkType(valToSet, Object)) {
                mObject = new Morphine();
            } else
            if (checkType(valToSet, Array)) {
                mObject = new MorphineArray();
            } else
            if (checkType(valToSet, Morphine) || checkType(valToSet, MorphineArray)) {
                checkResult = true;
            }
        }

        builder.bind(this)(path, checkResult ? valToSet : converter.bind(mObject)(valToSet));
        return this;
    }

    /**
     * Преобразует plain объект/массив в Morphine-сущность
     * @param {*} obj объект для преобразования
     * @param {boolean} self true - преобразовать plain-объект в себя (в this), false - преобразовать в новый объект
     */
    function converter (obj, self) {
        if (checkType(obj, Array)) {
            return toMorphine.call(this, obj, self ? null : MorphineArray);
        } else if (checkType(obj, Object)) {
            return toMorphine.call(this, obj, self ? null : Morphine);
        } else {
            // obj является примитивным или кастомным типом, поэтому вернем его без изменений
            return obj;
        }

        function toMorphine (obj, construct) {
            var morph = construct ? new construct() : this;

            for (var key in obj) {
                if (!obj.hasOwnProperty(key)) continue;

                if (typeof obj[key] === 'undefined' || obj[key] === null) {
                    continue;
                } else if (checkType(obj[key], Object)) {
                    valueSetter.call(morph, key, toMorphine.call(morph, obj[key], Morphine));
                } else if (checkType(obj[key], Array)) {
                    valueSetter.call(morph, key, toMorphine.call(morph, obj[key], MorphineArray));
                } else if (checkType(obj[key])) {
                    valueSetter.call(morph, key, obj[key]);
                } else {
                    console.error("Конструктор не определен %o %o %o", obj, key, obj[key]);
                }
            }

            return morph;
        }

        function valueSetter (key, value) {
            if (this.isObject()) {
                this.set(key, value);
            } else if (this.isArray()) {
                this.push(value);
            }
        }
        return morph;
    }

    return Morphine;
}));<commit_msg>Добавлен merger. Реализована операция объединения объектов/массивов через метод merge<commit_after>'use strict';

(function (root, factory) {
    // Backbone-style settings
    if (typeof define === 'function' && define.amd) {
        define(['exports'], function (exports) {
            root.Morphine = factory(root/*, exports*/);
        });
    } else if (typeof module !== 'undefined') {
        module.exports = factory(root/*, exports*/);
    } else {
        root.Morphine = factory(root/*, {}*/);
    }
}(this, function (root) {

    /**
     * Конфигурационный объект для Morphine.
     * Настраивается через Morphine.config
     */
    var CONFIG = {

        /**
         * Разделительный символ, который будет использоваться в path.
         * По умолчанию это точка.
         */
        separator: '.'

    };

    function Morphine() {
        return MorphineBuilder.apply(this, arguments);
    }

    function MorphineArray() {}
    MorphineArray.prototype = new Array();
    MorphineArray.prototype = new MorphineArray();
    MorphineArray.prototype.constructor = MorphineArray;

    Morphine.prototype.version = '0.0.9';

    function MorphineBuilder () {
        var ln = arguments.length;

        switch (ln) {
            case 1:
                // м.б. объект или path-строка
                if (checkType(arguments[0], String)) {
                    setter.apply(this, arguments);
                } else
                // TODO: Возникают проблемы с преобразованием в Morphine, если arguments[0] массив
                if (checkType(arguments[0], Object) || checkType(arguments[0], Array)) {
                    converter.call(this, arguments[0], true);
                }

            break;

            case 2:
                // м.б. path+значение
                setter.apply(this, arguments);
            break;
        }
        return this;
    }

    var CommonPrototypeMixin = {
        isObject: function () {
            return this.constructor === Morphine;
        },
        isArray: function () {
            return this.constructor === MorphineArray;
        },
        /**
         * @public
         * Проверит свойство объекта на null
         * @param {String} key ключ элемента
         * @return {Boolean} Результат проверки. true - свойство === null, false - свойство !== null.
         **/
        isNull: function (key) {
            return this[key] === null;
        },
        /**
         * @public
         * Проверит свойство объекта на undefined
         * @param {String} key ключ элемента
         * @return {Boolean} Результат проверки. true - свойство === undefined, false - свойство !== undefined.
         **/
        isUndefined: function (key) {
            return typeof this[key] === 'undefined';
        },
        /**
         * @public
         * Проверит наличие свойства key в текущем объекте
         * @param {String} key ключ элемента
         * @return {Boolean} Результат проверки присутствия поля. true - присутствует, false - отсутствует.
         **/
        has: function (key) {
            return this.hasOwnProperty(key);
        },
        /**
         * Выполнит merge src c текущим объектом
         * @param {Any Object} src
         */
        merge: function (src) {
            if (src.isObject || src.isArray) {
                merger.call(this, src);
            } else {
                merger.call(this, converter.call(this, src));
            }
            return this;
        },
        /**
         * Установит свойство по указанному path
         * @param {String} path Путь по которому нужно установить значение
         * @param {*} value значение для установки в объекте
         * @return {Morphine} Текущий экземпляр объекта
         */
        set: function (path, value) {
            setter.call(this, path, value);
            return this;
        },
        /**
         * Вернет значение по указанному path
         * @param {String} path путь по которому нужно получить значение
         * @return {*} Значение расположенное по заданному пути
         */
        get: function (path) {
            var pathArray = path.split(CONFIG.separator);
            return getter(pathArray, this);
        },
        /**
         * Сконфигурирует текущий экземпляр объекта
         * @param {Object} options объект опций для конфигурирования текущего экземпляра Morphine
         * @return {Morphine} Текущий экземпляр объекта
         */
        config: function () {
            Configure.apply(this, arguments);
            return this;
        }
    };

    var MorphinePrototypeMixin = {};
    var MorphineArrayPrototypeMixin = {};

    /**
     * Скопирует в прототип this все свойства объекта source
     * @param {Object} source объект с набором свойств для копирования
     **/
    Morphine.mixin = MorphineArray.mixin = function (source) {
        if (source) {
            for (var prop in source) {
                if (!source.hasOwnProperty(prop)) continue;
                this.prototype[prop] = source[prop];
            }
        }
    };

    Morphine.mixin(CommonPrototypeMixin);
    MorphineArray.mixin(CommonPrototypeMixin);

    /**
     * @private
     * Метод конфигурирует дальнейшую работу с объектом
     * @param {Object} options Набор свойств для CONFIG-объекта
     */
    function Configure (options) {
        if (checkType(options, Object)) {
            CONFIG.separator = options.separator ? options.separator : CONFIG.separator;
        }
    }

    /**
     * @private
     * Проверяет соответствие фактического типа real примитивным типам.
     * Если задан ожидаемый тип expect, то проверяется соответствие только с ним.
     */
    function checkType (real, expect) {
        expect = expect ? [expect] : [Boolean, String, Number];
        var ln = expect.length;
        for (var i = 0; i < ln; i++) {
            if (expect[i] === real || expect[i] === real.constructor) {
                return true;
            }
        }
        return false;
    }

    /**
     * @private
     * Метод выстроит объект по структуре указанной в path. Если указано
     * значение value - присвоит его последнему элементу.
     * @param {String} path Задает структуру объекта для построения
     * @param {*} value Значение последнего элемента в path
     **/
    function builder (path, value) {
        var pathArray = path.split(CONFIG.separator),
            intRegexp = /^[0-9]$/;

        innerBuilder.call(this, pathArray, value);

        function innerBuilder (pathArray, value) {
            var index = pathArray.shift();
            var testInt = intRegexp.test(pathArray[0]);
            var testCollection = pathArray[0] === '$';

            if (pathArray.length === 0) {
                if (index === '$') {
                    this.push(value);
                } else if (intRegexp.test(index)) {
                    if (typeof this[index] === 'undefined') {
                        // TODO: Заменить все console-выводы на исключения
                        console.error("Элемент %o не существует", index);
                    }
                } else {
                    this[index] = value;
                }
                return;
            } else {
                if (testInt || testCollection) {
                    if (testCollection) {
                        if (index === "$") {
                            this.push(new MorphineArray());
                        } else {
                            this[index] = new MorphineArray();
                        }
                    } else if (testInt) {
                        if (typeof this[index] === 'undefined') {
                            console.error("Элемент %o не существует", index);
                        }
                    } else {
                        this[index] = (typeof this[index] !== 'undefined') ? this[index] : new MorphineArray();
                    }
                } else {
                    if (intRegexp.test(index)) {
                        this[index] = (typeof this[index] !== 'undefined') ? this[index] : new Morphine();
                    } else if (index === '$') {
                        this.push(new Morphine());
                    } else {
                        this[index] = (typeof this[index] !== 'undefined') ? this[index] : new Morphine();
                    }
                }

                // Если в коллекцию $ был добавлен очередной элемент - получаем его индекс, для дальнейшего построения
                if (index === '$') {
                    index = this.length-1;
                }
            }

            innerBuilder.call(this[index], pathArray, value);
        }
    }

    /**
     * @private
     * Возвращает объект из источника
     * @param {Array} pathArray  массив содержащий путь до целевого элемента
     * @param {Object} source Объект-источник
     **/
    function getter(pathArray, source) {
        var index = pathArray.shift();

        if (!pathArray.length) {
            return source[index];
        }
        return getter(pathArray, source[index]);
    }

    /**
     * @private
     * Метод установит значение по указанному path. Если path или его часть
     * не существует, то недостающие элементы будут выстроены в соответствии
     * со структурой path.
     * @param {String} path Задает элемент для установки значения
     * @param {*} value Значение последнего элемента в path. По умолчанию - пустой объект
     * @return {Morphine} Получившийся объект
     **/
    function setter(path, value) {
        var valToSet = (typeof value !== 'undefined') ? value : {},
            checkResult = checkType(valToSet),
            mObject = null;

        if (!checkResult) {
            if (checkType(valToSet, Object)) {
                mObject = new Morphine();
            } else
            if (checkType(valToSet, Array)) {
                mObject = new MorphineArray();
            } else
            if (checkType(valToSet, Morphine) || checkType(valToSet, MorphineArray)) {
                checkResult = true;
            }
        }

        builder.bind(this)(path, checkResult ? valToSet : converter.bind(mObject)(valToSet));
        return this;
    }

    /**
     * Преобразует plain объект/массив в Morphine-сущность
     * @param {*} obj объект для преобразования
     * @param {boolean} self true - преобразовать plain-объект в себя (в this), false - преобразовать в новый объект
     * @return {Morphine} Результат преобразования
     */
    function converter (obj, self) {
        if (checkType(obj, Array)) {
            return toMorphine.call(this, obj, self ? null : MorphineArray);
        } else if (checkType(obj, Object)) {
            return toMorphine.call(this, obj, self ? null : Morphine);
        } else {
            // obj является примитивным или кастомным типом, поэтому вернем его без изменений
            return obj;
        }

        function toMorphine (obj, construct) {
            var morph = construct ? new construct() : this;

            for (var key in obj) {
                if (!obj.hasOwnProperty(key)) continue;

                if (typeof obj[key] === 'undefined' || obj[key] === null) {
                    continue;
                } else if (checkType(obj[key], Object)) {
                    valueSetter.call(morph, key, toMorphine.call(morph, obj[key], Morphine));
                } else if (checkType(obj[key], Array)) {
                    valueSetter.call(morph, key, toMorphine.call(morph, obj[key], MorphineArray));
                } else if (checkType(obj[key])) {
                    valueSetter.call(morph, key, obj[key]);
                } else {
                    console.error("Конструктор не определен %o %o %o", obj, key, obj[key]);
                }
            }

            return morph;
        }

        function valueSetter (key, value) {
            if (this.isObject()) {
                this.set(key, value);
            } else if (this.isArray()) {
                this.push(value);
            }
        }
        return morph;
    }

    /**
     * @private
     * Мерджит поля объекта src в this, если эти поля являются
     * примитивными типами. Если встречает объект или массив - переходит
     * в него, создавая аналогичную вложенность в this и продолжает merge.
     * Объект должен быть Morphine-сущностью.
     * @param {Morphine} src Исходный объект для мерджа
     */
    // TODO: Как выполнится merge для массивов с разными размерностями
    function merger (src) {
        var dst = this;
        for (var key in src) {
            if (!src.has(key) || src.isUndefined(key) || src.isNull(key)) continue;

            if (checkType(src[key].constructor)) {
                dst[key] = src[key];
            } else {
                if (!dst.has(key)) {
                    if (src[key].isArray()) {
                        dst[key] = new MorphineArray();
                    } else if (src[key].isObject()) {
                        dst[key] = new Morphine();
                    }
                }
                merger.call(dst[key], src[key]);
            }
        }
        return dst;
    }

    return Morphine;
}));<|endoftext|>


<commit_before>jsio('from common.javascript import Class, bind')
jsio('import browser.UIComponent')
// jsio('import browser.ItemView') see note below on jsio circular imports...
jsio('import browser.events as events')

exports = Class(browser.UIComponent, function(supr){

	this.init = function(itemReference, itemType) {
		supr(this, 'init');
		this._itemReference = itemReference;
		this._itemType = itemType;
	}

	this.createContent = function() {
		this._itemReference.subscribe('ReferenceChanged', bind(this, '_renderView'));
		this._renderView();
	}

	this._renderView = function() {
		this._element.innerHTML = '';
		// jsio doesn't support circular imports...
		var itemView = new window.__gItemViewClass(this._itemReference, this._itemType, 'reference');
		itemView.appendTo(this._element);
		events.add(itemView.getElement(), 'click', bind(this, '_publish', 'Click'));
	}

	this.getReferencedItem = function() {
		return this._itemReference.getReferencedItem();
	}
})

<commit_msg>When clicking a reference view, the panel for the referenced item should be focused. Cancel the click event in order to prevent the click to focus the item view housing the reference view after the referenced item panel has been focused.<commit_after>jsio('from common.javascript import Class, bind')
jsio('import browser.UIComponent')
// jsio('import browser.ItemView') see note below on jsio circular imports...
jsio('import browser.events as events')

exports = Class(browser.UIComponent, function(supr){

	this.init = function(itemReference, itemType) {
		supr(this, 'init');
		this._itemReference = itemReference;
		this._itemType = itemType;
	}

	this.createContent = function() {
		this._itemReference.subscribe('ReferenceChanged', bind(this, '_renderView'));
		this._renderView();
	}

	this._renderView = function() {
		this._element.innerHTML = '';
		// jsio doesn't support circular imports...
		var itemView = new window.__gItemViewClass(this._itemReference, this._itemType, 'reference');
		itemView.appendTo(this._element);
		events.add(itemView.getElement(), 'click', bind(this, 'onItemViewClick'));
	}

	this._onItemViewClick = function(e) {
		events.cancel(e);
		this._publish('Click');
	}

	this.getReferencedItem = function() {
		return this._itemReference.getReferencedItem();
	}
})

<|endoftext|>