|
237 | 237 | if (!(this instanceof Binding)) return new Binding(bindObj, registerIndex);
|
238 | 238 | var thisBinding = this;
|
239 | 239 | this.registerIndex = registerIndex;
|
| 240 | + |
| 241 | + this.preferDom = !!bindObj.preferDom; |
240 | 242 |
|
241 | 243 | if (typeof bindObj.canBind === 'string' || Array.isArray(bindObj.canBind)) {
|
242 | 244 | bindObj.canBind = {schema: bindObj.canBind};
|
|
395 | 397 | }
|
396 | 398 | return this._concatOptions;
|
397 | 399 | },
|
398 |
| - addHtml: function (canBind, htmlFunc) { |
399 |
| - return this.add({ |
400 |
| - canBind: canBind, |
401 |
| - html: htmlFunc |
402 |
| - }); |
| 400 | + addHtml: function (canBind, htmlFunc, extra) { |
| 401 | + var bindObj = Object.create(extra || {}); |
| 402 | + bindObj.canBind = canBind; |
| 403 | + bindObj.html = htmlFunc; |
| 404 | + return this.add(bindObj); |
403 | 405 | },
|
404 | 406 | add: function (bindObj) {
|
405 | 407 | this._state++;
|
|
520 | 522 | api.bindings = new Bindings();
|
521 | 523 |
|
522 | 524 | api.navigateTo = function (href) {
|
523 |
| - href = resolveUrl(window.location.href, href); |
524 |
| - var loc = window.location.href; |
525 |
| - if (href === loc) return; |
526 |
| - var locParsed = parseUrl(loc); |
527 |
| - var domain = locParsed.protocol + locParsed.authority; |
528 |
| - var path = window.location.href.replace(/[#?].*/g, ''); |
529 |
| - if (href.substring(0, path.length) === path) { |
530 |
| - href = href.substring(path.length); |
531 |
| - } else if (href.substring(0, domain.length) === domain) { |
532 |
| - href = href.substring(domain.length); |
533 |
| - } else { |
| 525 | + var relative = api.util.url.relative(window.location.href, href); |
| 526 | + if (relative === href) { |
534 | 527 | window.location.href = href;
|
535 | 528 | return;
|
536 |
| - } |
537 |
| - if (typeof history === 'object' && typeof history.pushState === 'function') { |
538 |
| - history.pushState(null, null, href); |
| 529 | + } else if (typeof history === 'object' && typeof history.pushState === 'function') { |
| 530 | + history.pushState(null, null, relative); |
539 | 531 | } else {
|
540 |
| - window.location.href = '#' + encodeURI(href).replace(/#/g, '%23'); |
| 532 | + window.location.href = '#' + encodeURI(relative).replace(/#/g, '%23'); |
541 | 533 | }
|
542 | 534 | };
|
543 | 535 |
|
|
561 | 553 | this.includeDataProperties = false;
|
562 | 554 |
|
563 | 555 | this.urlForState = function (resourceUrl, newUiState) {
|
564 |
| - return '?json=' + encodeURIComponent(resourceUrl); |
| 556 | + if (typeof window === 'object' && window.location && typeof window.location.href === 'string') { |
| 557 | + resourceUrl = api.util.url.relative(window.location.href, resourceUrl); |
| 558 | + } |
| 559 | + return api.util.url.encodeQuery({json: resourceUrl}) || '?'; |
565 | 560 | };
|
566 | 561 | this.stateForUrl = function (url) {
|
567 | 562 | var parsed = api.util.url.parse(url);
|
|
588 | 583 | monitorLocation: function (element) {
|
589 | 584 | var thisContext = this;
|
590 | 585 |
|
| 586 | + var emitter = new api.EventEmitter(); |
| 587 | + |
591 | 588 | var getHref = function () {
|
592 | 589 | return window.location.href;
|
593 | 590 | };
|
594 | 591 |
|
595 | 592 | var oldHref = null;
|
| 593 | + var pending = false; |
| 594 | + var isFirst = true; |
596 | 595 | var updateFromLocation = function () {
|
| 596 | + if (pending) return; |
597 | 597 | var href = getHref();
|
598 | 598 | if (href === oldHref) return;
|
599 | 599 | oldHref = href;
|
|
603 | 603 | console.log('New URL:', newHref);
|
604 | 604 |
|
605 | 605 | var state = thisContext.stateForUrl(newHref);
|
606 |
| - var resourceUrl = state[0], uiState = state[1]; |
| 606 | + var resourceUrl = state[0], uiState = state[1] || {}; |
607 | 607 | console.log('New state:', resourceUrl, uiState);
|
608 | 608 | if (resourceUrl) {
|
| 609 | + pending = true; |
| 610 | + emitter.emit('change', resourceUrl, uiState, isFirst); |
609 | 611 | // Reset the UI state, don't bother trying to change it
|
610 |
| - thisContext.ui = thisContext._dataStore.create(state[1] || {}); |
611 |
| - thisContext.bind(state[0], element); |
| 612 | + thisContext.ui = thisContext._dataStore.create(uiState); |
| 613 | + thisContext.bind(resourceUrl, element, function () { |
| 614 | + emitter.emit('change-done', resourceUrl, uiState); |
| 615 | + pending = false; |
| 616 | + }); |
612 | 617 | } else {
|
| 618 | + emitter.emit('ui', uiState); |
613 | 619 | // Update the UI state, leaving the URL the same
|
614 | 620 | thisContext.ui.set('', uiState);
|
615 | 621 | }
|
| 622 | + isFirst = false; |
616 | 623 | }
|
617 | 624 |
|
618 | 625 | if (typeof history === 'object' && typeof history.pushState === 'function') {
|
619 | 626 | window.onpopstate = updateFromLocation;
|
620 | 627 | }
|
621 | 628 |
|
622 | 629 | var interval = setInterval(updateFromLocation, 100);
|
623 |
| - return function () { |
624 |
| - clearInterval(interval); |
625 |
| - }; |
| 630 | + return emitter; |
626 | 631 | },
|
627 | 632 | navigateTo: function (href) {
|
628 | 633 | api.navigateTo(href);
|
|
804 | 809 | element.boundBinding = binding;
|
805 | 810 | element.boundContext = context;
|
806 | 811 |
|
| 812 | + if (binding.preferDom) { |
| 813 | + model.whenReady(function () { |
| 814 | + thisContext._updateDom(element, tag, attrs, function (error) { |
| 815 | + binding.bindDom(context, model, element); |
| 816 | + if (oldState !== model._root.state) { |
| 817 | + console.log('Model changed during initial render:', model.url()); |
| 818 | + return thisContext._updateDom(element, tag, attrs, callback); |
| 819 | + } |
| 820 | + callback(error); |
| 821 | + }); |
| 822 | + }); |
| 823 | + return; |
| 824 | + } |
| 825 | + |
807 | 826 | function htmlReady(error) {
|
808 | 827 | scanForChildBindings(element, context, function (err) {
|
809 | 828 | binding.bindDom(context, model, element);
|
|
0 commit comments