<commit_before>// ==UserScript==
// @name           amzRememberOptions
// @namespace      http://d.hatena.ne.jp/furyu-tei
// @include        http://www.amazon.co.jp/*
// @include        https://www.amazon.co.jp/*
// @description    remember search options for Amazon.co.jp (ver.0.1.2.0)
// ==/UserScript==
/*
The MIT License (MIT)

Copyright (c) 2014 furyu <furyutei@gmail.com>

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.
*/

(function(w, d){

//{ user parameters
var REPLACE_AUTHOR_URL = true;

var DEFAULT_SEARCH_ALIAS = 'search-alias=stripbooks';   //  select#searchDropdownBox
var DEFAULT_SORT = 'date-desc-rank';    //  select#sort
//}

w.init_amz_options = function(){
    //localStorage.clear();
    localStorage.setItem('search_alias', '');
    localStorage.setItem('sort', '');
}   // end of init_amz_options()

var main = function(){
    // === クエリ文字列分解
    var split_query_string = function(query_string) {
        var query_list = query_string.split('&');
        var param_dict = {};
        for (var ci=0,len=query_list.length; ci<len; ci++) {
            var param_parts = query_list[ci].split('=');
            param_dict[param_parts[0]] = (2 <= param_parts.length) ? decodeURIComponent(param_parts[1]) : '';
            // TODO: 同一キーが複数ある場合を考慮していない
        }
        return param_dict;
    };  // end of split_query_string()

    // === クエリ文字列作成
    var join_query_params = function(param_dict) {
        var query_list = [];
        for (var name in param_dict) {
            if (!param_dict.hasOwnProperty(name)) continue;
            query_list.push(name + '=' + encodeURIComponent(param_dict[name]));
        }
        return query_list.join('&');
    };  //  end of join_query_params()

    // === 正規化URL(link[name="canonical"]のhref属性)より情報取得
    var analyze_canonical_url = function(canonical_url) {
        var result = {};
        if (!canonical_url) {
            var link_canonical = d.querySelector('link[rel="canonical"]');
            if (link_canonical) canonical_url = d.querySelector('link[rel="canonical"]').href;
        }
        result['canonical_url'] = canonical_url;
        while (canonical_url) {
            if (!canonical_url.match(/^(https?:\/\/[^\/]+)\/([^\/]+)\/([^\/]+)([\/\?])(.+)$/)) break;
            result['domain_url'] = RegExp.$1;
            result['keywords'] = decodeURIComponent(RegExp.$2);
            result['kind'] = RegExp.$3;
            var method = result['method'] = (RegExp.$4 == '?') ? 'query' : 'path';
            var parameters = RegExp.$5;
            var param_dict = {};
            switch (method) {
                case    'query':
                    param_dict = split_query_string(parameters);
                    break;
                case    'path':
                    param_dict = {asin: parameters};
                    break;
            }
            result['param_dict'] = param_dict;
            break;
        }
        return result;
    };  // end of analyze_canonical_url()

    var ready = false;
    for (;;) {
        // === 検索フォームの各要素取得
        var elm_searchbar = d.querySelector('form#nav-searchbar');
        if (!elm_searchbar) break;

        var elm_textbox = elm_searchbar.querySelector('input#twotabsearchtextbox') || elm_searchbar.querySelector('*[name="field-keywords"]');
        if (!elm_textbox) break;

        var elm_search_dropdown_box = d.querySelector('select#searchDropdownBox') || elm_searchbar.querySelector('*[name="url"]');
        if (!elm_search_dropdown_box) break;

        ready = true;

        // === 保存済みオプションパラメータ取得
        var option_search_alias = localStorage.getItem('search_alias');
        var option_sort = localStorage.getItem('sort');
        if (!option_search_alias) option_search_alias = DEFAULT_SEARCH_ALIAS;
        if (!option_sort) option_sort = DEFAULT_SORT;

        // === 現ページの正規化URL情報取得
        canonical_url_info = analyze_canonical_url();
        //console.log(canonical_url_info);

        // === 著者ページURL → 著者検索ページ URL 変換
        var change_author_url = (function(){
            var action = elm_searchbar.action;
            var elm_inputs = elm_searchbar.querySelectorAll('*[name]');
            var source_form_param_dict = {};
            for (var ci=0, len=elm_inputs.length; ci < len; ci++) {
                var elm_input = elm_inputs[ci], input_name = elm_input.name, input_value = elm_input.value;
                switch (input_name) {
                    case    'field-keywords':
                        continue;
                    default:
                        break;
                }
                source_form_param_dict[input_name] = input_value;
            }
            if (!source_form_param_dict['url']) source_form_param_dict['url'] = option_search_alias;
            if (!source_form_param_dict['sort']) source_form_param_dict['sort'] = option_sort;

            var links = d.querySelectorAll('a.a-link-normal, div.buying a'), source_link_param_dict = null, field_name = null;
            for (var ci=0,len=links.length; ci<len; ci++) {
                if (!links[ci].href.match(/[?&]search-alias=/)) continue;
                var url_parts = links[ci].href.split('?');
                if (url_parts.length < 2) continue;
                source_link_param_dict = split_query_string(url_parts[1]);
                for (var name in source_link_param_dict) {
                    if (!source_link_param_dict.hasOwnProperty(name)) continue;
                    if (name.match(/^field-/)) {
                        field_name = name;
                        continue;
                    }
                }
                source_link_param_dict['sort'] = option_sort;
                break;
            }
            if (!field_name) field_name = 'field-author';

            //var is_valid = !!(elm_search_dropdown_box.value.match(/(?:books|digital-text)$/)) && REPLACE_AUTHOR_URL;
            var is_valid = REPLACE_AUTHOR_URL;

            return function(author_url){
                var result_url = null;
                for (;;) {
                    if (!is_valid) break;

                    var source_param_dict = (author_url) ? source_link_param_dict : source_form_param_dict;
                    if (!source_param_dict) break;

                    var param_dict = {};
                    for (var name in source_param_dict) {
                        if (!source_param_dict.hasOwnProperty(name)) continue;
                        param_dict[name] = source_param_dict[name];
                    }
                    if (author_url) {
                        if (!author_url.match(/\/([^\/]+)\/e\/[^\/]+\/ref=/)) break;
                        var author = RegExp.$1;
                        if (author == '-') break;
                        //param_dict['field-author'] = decodeURIComponent(author);
                        param_dict[field_name] = param_dict['text'] = decodeURIComponent(author);
                    }
                    else {
                        if (canonical_url_info.kind != 'e') break;
                        param_dict['field-author'] = canonical_url_info.keywords;
                        // TODO: 'field-author' 以外を考慮する必要はないか？
                    }
                    result_url = action + '?' + join_query_params(param_dict);
                    break;
                }
                return result_url;
            };
        })();   // end of change_author_url()

        // === 著者ページのチェック
        var new_url = change_author_url();
        if (new_url && (new_url != w.location.href)) {
            w.location.replace(new_url);    // 著者ページ→著者検索ページへリダイレクト
            break;
        }

        // === 著者ページへのリンク→著者検索ページへのリンクに変換
        //var links = d.querySelectorAll('div#byline span.author a.a-link-normal, div.buying a');
        var links = d.querySelectorAll('a.a-link-normal, div.buying a');
        for (var ci=0, len=links.length; ci<len; ci++) {
            var link = links[ci];
            var new_url = change_author_url(link.href);
            if (new_url && (new_url != link.href)) {
                link.href = new_url;
            }
            else {
                // 'sort' オプションの付加
                //link.href = link.href.replace(/([\?&]sort=)[^\?&]+/g, '$1'+encodeURIComponent(option_sort))
                var url_parts = link.href.split('?');
                if (2 <= url_parts.length) {
                    var url_base = url_parts[0], param_dict = split_query_string(url_parts[1]);
                    param_dict['sort'] = option_sort;
                    link.href = url_base + '?' + join_query_params(param_dict);
                }
            }
        }

        // === 検索フォーム内に 'sort' オプション埋め込み
        var elm_sort = elm_searchbar.querySelector('*[name="sort"]');
        if (!elm_sort) {
            var elm_sort = d.createElement('input');
            elm_sort.type = 'hidden';
            elm_sort.name = 'sort';
            elm_searchbar.appendChild(elm_sort);
        }
        elm_sort.value = option_sort;

        // === 並べ替えフォームの各要素取得
        var elm_search_sort_form = d.querySelector('form#searchSortForm');
        var elm_search_sort = (elm_search_sort_form) ? elm_search_sort_form.querySelector('select#sort') || elm_search_sort_form.querySelector('*[name="sort"]') : null;

        if (canonical_url_info.kind) {
            // === 検索結果画面・個別ページ等→パラメータを保存
            localStorage.setItem('search_alias', elm_search_dropdown_box.value);
            if (elm_search_sort) {
                localStorage.setItem('sort', elm_search_sort.value);
                elm_sort.value = elm_search_sort.value;
            }
        }
        else {
            // === 検索結果画面・個別ページ等以外→保存パラメータを設定
            elm_search_dropdown_box.value = option_search_alias;
            d.querySelector('span#nav-search-in-content').textContent = elm_search_dropdown_box.querySelector('option[value="'+option_search_alias+'"]').textContent;
            if (elm_search_sort) {
                elm_search_sort.value = option_sort;
            }
        }
        break;
    }
    return ready;
};  //  end of main()

var ready = main();
if (!ready) return;

// === ページ遷移を検知→メイン処理コール
var pushState = w.history.pushState;
if (pushState && w.addEventListener) {
    w.history.pushState = function(){
        pushState.apply(this, arguments);
        setTimeout(function(){main();}, 1);
    };
    w.addEventListener('popstate', function(evt){
        setTimeout(function(){main();}, 1);
    }, false);
}
else {
    var check = function(url){
        setTimeout(function(){
            var current_url = w.location.href;
            if (current_url == url) {
                check(url);
                return;
            }
            setTimeout(function(){main();}, 1);
            check(current_url);
        }, 300);
    };
    check(w.location.href);
}

})(window, document);

// ■ end of file
<commit_msg>0.1.3.0: 検索結果で並び順未指定の場合、保存された並び順へ遷移するよう修正<commit_after>// ==UserScript==
// @name           amzRememberOptions
// @namespace      http://d.hatena.ne.jp/furyu-tei
// @include        http://www.amazon.co.jp/*
// @include        https://www.amazon.co.jp/*
// @description    remember search options for Amazon.co.jp (ver.0.1.3.0)
// ==/UserScript==
/*
The MIT License (MIT)

Copyright (c) 2014 furyu <furyutei@gmail.com>

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.
*/

(function(w, d){

//{ user parameters
var REPLACE_AUTHOR_URL = true;

var DEFAULT_SEARCH_ALIAS = 'search-alias=stripbooks';   //  select#searchDropdownBox
var DEFAULT_SORT = 'date-desc-rank';    //  select#sort
//}

w.init_amz_options = function(){
    //localStorage.clear();
    localStorage.setItem('search_alias', '');
    localStorage.setItem('sort', '');
}   // end of init_amz_options()

var main = function(){
    // === クエリ文字列分解
    var split_query_string = function(query_string) {
        var query_list = query_string.split('&');
        var param_dict = {};
        for (var ci=0,len=query_list.length; ci<len; ci++) {
            var param_parts = query_list[ci].split('=');
            param_dict[param_parts[0]] = (2 <= param_parts.length) ? decodeURIComponent(param_parts[1]) : '';
            // TODO: 同一キーが複数ある場合を考慮していない
        }
        return param_dict;
    };  // end of split_query_string()

    // === クエリ文字列作成
    var join_query_params = function(param_dict) {
        var query_list = [];
        for (var name in param_dict) {
            if (!param_dict.hasOwnProperty(name)) continue;
            query_list.push(name + '=' + encodeURIComponent(param_dict[name]));
        }
        return query_list.join('&');
    };  //  end of join_query_params()

    // === 正規化URL(link[name="canonical"]のhref属性)より情報取得
    var analyze_canonical_url = function(canonical_url) {
        var result = {};
        if (!canonical_url) {
            var link_canonical = d.querySelector('link[rel="canonical"]');
            if (link_canonical) canonical_url = d.querySelector('link[rel="canonical"]').href;
        }
        result['canonical_url'] = canonical_url;
        while (canonical_url) {
            if (!canonical_url.match(/^(https?:\/\/[^\/]+)\/([^\/]+)\/([^\/]+)([\/\?])(.+)$/)) break;
            result['domain_url'] = RegExp.$1;
            result['keywords'] = decodeURIComponent(RegExp.$2);
            result['kind'] = RegExp.$3;
            var method = result['method'] = (RegExp.$4 == '?') ? 'query' : 'path';
            var parameters = RegExp.$5;
            var param_dict = {};
            switch (method) {
                case    'query':
                    param_dict = split_query_string(parameters);
                    break;
                case    'path':
                    param_dict = {asin: parameters};
                    break;
            }
            result['param_dict'] = param_dict;
            break;
        }
        return result;
    };  // end of analyze_canonical_url()

    var ready = false;
    for (;;) {
        // === 検索フォームの各要素取得
        var elm_searchbar = d.querySelector('form#nav-searchbar');
        if (!elm_searchbar) break;

        var elm_textbox = elm_searchbar.querySelector('input#twotabsearchtextbox') || elm_searchbar.querySelector('*[name="field-keywords"]');
        if (!elm_textbox) break;

        var elm_search_dropdown_box = d.querySelector('select#searchDropdownBox') || elm_searchbar.querySelector('*[name="url"]');
        if (!elm_search_dropdown_box) break;

        ready = true;

        // === 保存済みオプションパラメータ取得
        var option_search_alias = localStorage.getItem('search_alias');
        var option_sort = localStorage.getItem('sort');
        if (!option_search_alias) option_search_alias = DEFAULT_SEARCH_ALIAS;
        if (!option_sort) option_sort = DEFAULT_SORT;

        // === 現ページの正規化URL情報取得
        canonical_url_info = analyze_canonical_url();
        //console.log(canonical_url_info);

        // === 著者ページURL → 著者検索ページ URL 変換
        var change_author_url = (function(){
            var action = elm_searchbar.action;
            var elm_params = elm_searchbar.querySelectorAll('*[name]');
            var source_form_param_dict = {};
            for (var ci=0, len=elm_params.length; ci < len; ci++) {
                var elm_input = elm_params[ci], input_name = elm_input.name, input_value = elm_input.value;
                switch (input_name) {
                    case    'field-keywords':
                        continue;
                    default:
                        break;
                }
                source_form_param_dict[input_name] = input_value;
            }
            if (!source_form_param_dict['url']) source_form_param_dict['url'] = option_search_alias;
            if (!source_form_param_dict['sort']) source_form_param_dict['sort'] = option_sort;

            var links = d.querySelectorAll('a.a-link-normal, div.buying a'), source_link_param_dict = null, field_name = null;
            for (var ci=0,len=links.length; ci<len; ci++) {
                if (!links[ci].href.match(/[?&]search-alias=/)) continue;
                var url_parts = links[ci].href.split('?');
                if (url_parts.length < 2) continue;
                source_link_param_dict = split_query_string(url_parts[1]);
                for (var name in source_link_param_dict) {
                    if (!source_link_param_dict.hasOwnProperty(name)) continue;
                    if (name.match(/^field-/)) {
                        field_name = name;
                        continue;
                    }
                }
                source_link_param_dict['sort'] = option_sort;
                break;
            }
            if (!field_name) field_name = 'field-author';

            //var is_valid = !!(elm_search_dropdown_box.value.match(/(?:books|digital-text)$/)) && REPLACE_AUTHOR_URL;
            var is_valid = REPLACE_AUTHOR_URL;

            return function(author_url){
                var result_url = null;
                for (;;) {
                    if (!is_valid) break;

                    var source_param_dict = (author_url) ? source_link_param_dict : source_form_param_dict;
                    if (!source_param_dict) break;

                    var param_dict = {};
                    for (var name in source_param_dict) {
                        if (!source_param_dict.hasOwnProperty(name)) continue;
                        param_dict[name] = source_param_dict[name];
                    }
                    if (author_url) {
                        if (!author_url.match(/\/([^\/]+)\/e\/[^\/]+\/ref=/)) break;
                        var author = RegExp.$1;
                        if (author == '-') break;
                        //param_dict['field-author'] = decodeURIComponent(author);
                        param_dict[field_name] = param_dict['text'] = decodeURIComponent(author);
                    }
                    else {
                        if (canonical_url_info.kind != 'e') break;
                        param_dict['field-author'] = canonical_url_info.keywords;
                        // TODO: 'field-author' 以外を考慮する必要はないか？
                    }
                    result_url = action + '?' + join_query_params(param_dict);
                    break;
                }
                return result_url;
            };
        })();   // end of change_author_url()

        // === 著者ページのチェック
        var new_url = change_author_url();
        if (new_url && (new_url != w.location.href)) {
            // console.log('New URL: ' + new_url);
            w.location.replace(new_url);    // 著者ページ→著者検索ページへ遷移
            break;
        }

        // === 著者ページへのリンク→著者検索ページへのリンクに変換
        //var links = d.querySelectorAll('div#byline span.author a.a-link-normal, div.buying a');
        var links = d.querySelectorAll('a.a-link-normal, div.buying a');
        for (var ci=0, len=links.length; ci<len; ci++) {
            var link = links[ci];
            var new_url = change_author_url(link.href);
            if (new_url && (new_url != link.href)) {
                link.href = new_url;
            }
            else {
                // 'sort' オプションの付加
                //link.href = link.href.replace(/([\?&]sort=)[^\?&]+/g, '$1'+encodeURIComponent(option_sort))
                var url_parts = link.href.split('?');
                if (2 <= url_parts.length) {
                    var url_base = url_parts[0], param_dict = split_query_string(url_parts[1]);
                    param_dict['sort'] = option_sort;
                    link.href = url_base + '?' + join_query_params(param_dict);
                }
            }
        }

        // === 検索フォーム内に 'sort' オプション埋め込み
        var elm_sort = elm_searchbar.querySelector('*[name="sort"]');
        if (!elm_sort) {
            var elm_sort = d.createElement('input');
            elm_sort.type = 'hidden';
            elm_sort.name = 'sort';
            elm_searchbar.appendChild(elm_sort);
        }
        elm_sort.value = option_sort;

        // === 並べ替えフォームの各要素取得
        var elm_search_sort_form = d.querySelector('form#searchSortForm');
        var elm_search_sort = (elm_search_sort_form) ? elm_search_sort_form.querySelector('select#sort') || elm_search_sort_form.querySelector('*[name="sort"]') : null;

        if (canonical_url_info.kind) {
            // === 検索結果画面・個別ページ等→パラメータを保存
            localStorage.setItem('search_alias', elm_search_dropdown_box.value);
            if (elm_search_sort) {
                if (w.location.href.match(/[\?&]sort=/)) {
                    // 並び順指定あり→パラメータ保存
                    localStorage.setItem('sort', elm_search_sort.value);
                    elm_sort.value = elm_search_sort.value;
                }
                else {
                    // 並び順指定なし
                    if (elm_search_sort.value != option_sort && elm_search_sort.querySelector('option[value="' + option_sort +'"]')) {
                        // 保存された並び順と異なる→並び順を指定してページ遷移
                        elm_search_sort.value = option_sort;
                        //elm_search_sort_form.submit();
                        var elm_params = elm_search_sort_form.querySelectorAll('*[name]');
                        var query_list = [];
                        for (var ci=0,len=elm_params.length; ci<len; ci++) {
                            query_list.push(elm_params[ci]['name'] + '=' + encodeURIComponent(elm_params[ci]['value']));
                        }
                        var new_url = elm_search_sort_form.action + '?' + query_list.join('&');
                        // console.log('New URL: ' + new_url);
                        w.location.replace(new_url);
                    }
                }
            }
        }
        else {
            // === 検索結果画面・個別ページ等以外→保存パラメータを設定
            elm_search_dropdown_box.value = option_search_alias;
            d.querySelector('span#nav-search-in-content').textContent = elm_search_dropdown_box.querySelector('option[value="'+option_search_alias+'"]').textContent;
            if (elm_search_sort) {
                elm_search_sort.value = option_sort;
            }
        }
        break;
    }
    return ready;
};  //  end of main()

var ready = main();
if (!ready) return;

// === ページ遷移を検知→メイン処理コール
var pushState = w.history.pushState;
if (pushState && w.addEventListener) {
    w.history.pushState = function(){
        pushState.apply(this, arguments);
        setTimeout(function(){main();}, 1);
    };
    w.addEventListener('popstate', function(evt){
        setTimeout(function(){main();}, 1);
    }, false);
}
else {
    var check = function(url){
        setTimeout(function(){
            var current_url = w.location.href;
            if (current_url == url) {
                check(url);
                return;
            }
            setTimeout(function(){main();}, 1);
            check(current_url);
        }, 300);
    };
    check(w.location.href);
}

})(window, document);

// ■ end of file
<|endoftext|>


<commit_before>pageflow.transientReferences = {
  initialize: function() {
    this.transientReferences = {};
    this.pendingReferences = {};
  },

  getReference: function(attribute, collection) {
    if (typeof collection === 'string') {
      var fileType = pageflow.editor.fileTypes.findByCollectionName(collection);
      collection = pageflow.entry.getFileCollection(fileType);
    }

    return this.transientReferences[attribute] ||
      collection.get(this.get(attribute));
  },

  setReference: function(attribute, record) {
    this._cleanUpReference(attribute);
    this._setReference(attribute, record);
    this._listenForReady(attribute, record);
  },

  unsetReference: function(attribute) {
    this._cleanUpReference(attribute);
    this.unset(attribute);
  },

  _setReference: function(attribute, record) {
    if (record.isNew()) {
      this.transientReferences[attribute] = record;
      this.set(attribute, null);
      this._setIdOnceSynced(attribute, record);
    }
    else {
      this.set(attribute, record.id);
    }
  },

  _setIdOnceSynced: function(attribute, record) {
    record.once('change:id', function() {
      delete this.transientReferences[attribute];
      this.set(attribute, record.id);
    }, this);
  },

  _listenForReady: function(attribute, record) {
    if (!record.isReady()) {
      this.pendingReferences[attribute] = record;

      this.listenTo(record, 'change:state', function() {
        if (record.isReady()) {
          this._cleanUpReadyListener(attribute);
          this.trigger('change');
        }
      });
    }
  },

  _cleanUpReference: function(attribute) {
    this._cleanUpSaveListener(attribute);
    this._cleanUpReadyListener(attribute);
  },

  _cleanUpSaveListener: function(attribute) {
    if (this.transientReferences[attribute]) {
      this.stopListening(this.transientReferences[attribute], 'change:id');
      delete this.transientReferences[attribute];
    }
  },

  _cleanUpReadyListener: function(attribute) {
    if (this.pendingReferences[attribute]) {
      this.stopListening(this.pendingReferences[attribute], 'change:state');
      delete this.pendingReferences[attribute];
    }
  }
};<commit_msg>Make FileReferenceInputView Set Attribute to null<commit_after>pageflow.transientReferences = {
  initialize: function() {
    this.transientReferences = {};
    this.pendingReferences = {};
  },

  getReference: function(attribute, collection) {
    if (typeof collection === 'string') {
      var fileType = pageflow.editor.fileTypes.findByCollectionName(collection);
      collection = pageflow.entry.getFileCollection(fileType);
    }

    return this.transientReferences[attribute] ||
      collection.get(this.get(attribute));
  },

  setReference: function(attribute, record) {
    this._cleanUpReference(attribute);
    this._setReference(attribute, record);
    this._listenForReady(attribute, record);
  },

  unsetReference: function(attribute) {
    this._cleanUpReference(attribute);
    this.set(attribute, null);
  },

  _setReference: function(attribute, record) {
    if (record.isNew()) {
      this.transientReferences[attribute] = record;
      this.set(attribute, null);
      this._setIdOnceSynced(attribute, record);
    }
    else {
      this.set(attribute, record.id);
    }
  },

  _setIdOnceSynced: function(attribute, record) {
    record.once('change:id', function() {
      delete this.transientReferences[attribute];
      this.set(attribute, record.id);
    }, this);
  },

  _listenForReady: function(attribute, record) {
    if (!record.isReady()) {
      this.pendingReferences[attribute] = record;

      this.listenTo(record, 'change:state', function() {
        if (record.isReady()) {
          this._cleanUpReadyListener(attribute);
          this.trigger('change');
        }
      });
    }
  },

  _cleanUpReference: function(attribute) {
    this._cleanUpSaveListener(attribute);
    this._cleanUpReadyListener(attribute);
  },

  _cleanUpSaveListener: function(attribute) {
    if (this.transientReferences[attribute]) {
      this.stopListening(this.transientReferences[attribute], 'change:id');
      delete this.transientReferences[attribute];
    }
  },

  _cleanUpReadyListener: function(attribute) {
    if (this.pendingReferences[attribute]) {
      this.stopListening(this.pendingReferences[attribute], 'change:state');
      delete this.pendingReferences[attribute];
    }
  }
};<|endoftext|>


<commit_before>/*!
 * Amplify Store - Persistent Client-Side Storage @VERSION
 *
 * Copyright 2011 appendTo LLC. (http://appendto.com/team)
 * Dual licensed under the MIT or GPL licenses.
 * http://appendto.com/open-source-licenses
 *
 * http://amplifyjs.com
 */
(function( amplify, undefined ) {

var store = amplify.store = function( key, value, options ) {
	var type = store.type;
	if ( options && options.type && options.type in store.types ) {
		type = options.type;
	}
	return store.types[ type ]( key, value, options || {} );
};

store.types = {};
store.type = null;
store.addType = function( type, storage ) {
	if ( !store.type ) {
		store.type = type;
	}

	store.types[ type ] = storage;
	store[ type ] = function( key, value, options ) {
		options = options || {};
		options.type = type;
		return store( key, value, options );
	};
};
store.error = function() {
	return "amplify.store quota exceeded";
};

var rprefix = /^__amplify__/;
function createFromStorageInterface( storageType, storage ) {
	store.addType( storageType, function( key, value, options ) {
		var storedValue, parsed, i, remove,
			ret = value,
			now = (new Date()).getTime();

		if ( !key ) {
			ret = {};
			remove = [];
			i = 0;
			try {
				// accessing the length property works around a localStorage bug
				// in Firefox 4.0 where the keys don't update cross-page
				// we assign to key just to avoid Closure Compiler from removing
				// the access as "useless code"
				// https://bugzilla.mozilla.org/show_bug.cgi?id=662511
				key = storage.length;

				while ( key = storage.key( i++ ) ) {
					if ( rprefix.test( key ) ) {
						parsed = JSON.parse( storage.getItem( key ) );
						if ( parsed.expires && parsed.expires <= now ) {
							remove.push( key );
						} else {
							ret[ key.replace( rprefix, "" ) ] = parsed.data;
						}
					}
				}
				while ( key = remove.pop() ) {
					storage.removeItem( key );
				}
			} catch ( error ) {}
			return ret;
		}

		// protect against name collisions with direct storage
		key = "__amplify__" + key;

		if ( value === undefined ) {
			storedValue = storage.getItem( key );
			parsed = storedValue ? JSON.parse( storedValue ) : { expires: -1 };
			if ( parsed.expires && parsed.expires <= now ) {
				storage.removeItem( key );
			} else {
				return parsed.data;
			}
		} else {
			if ( value === null ) {
				storage.removeItem( key );
			} else {
				parsed = JSON.stringify({
					data: value,
					expires: options.expires ? now + options.expires : null
				});
				try {
					storage.setItem( key, parsed );
				// quota exceeded
				} catch( error ) {
					// expire old data and try again
					store[ storageType ]();
					try {
						storage.setItem( key, parsed );
					} catch( error ) {
						throw store.error();
					}
				}
			}
		}

		return ret;
	});
}

// localStorage + sessionStorage
// IE 8+, Firefox 3.5+, Safari 4+, Chrome 4+, Opera 10.5+, iPhone 2+, Android 2+
for ( var webStorageType in { localStorage: 1, sessionStorage: 1 } ) {
	// try/catch for file protocol in Firefox
	try {
		if ( window[ webStorageType ].getItem ) {
			createFromStorageInterface( webStorageType, window[ webStorageType ] );
		}
	} catch( e ) {}
}

// globalStorage
// non-standard: Firefox 2+
// https://developer.mozilla.org/en/dom/storage#globalStorage
if ( !store.types.localStorage && window.globalStorage ) {
	// try/catch for file protocol in Firefox
	try {
		createFromStorageInterface( "globalStorage",
			window.globalStorage[ window.location.hostname ] );
		// Firefox 2.0 and 3.0 have sessionStorage and globalStorage
		// make sure we default to globalStorage
		// but don't default to globalStorage in 3.5+ which also has localStorage
		if ( store.type === "sessionStorage" ) {
			store.type = "globalStorage";
		}
	} catch( e ) {}
}

// userData
// non-standard: IE 5+
// http://msdn.microsoft.com/en-us/library/ms531424(v=vs.85).aspx
(function() {
	// IE 9 has quirks in userData that are a huge pain
	// rather than finding a way to detect these quirks
	// we just don't register userData if we have localStorage
	if ( store.types.localStorage ) {
		return;
	}

	// append to html instead of body so we can do this from the head
	var div = document.createElement( "div" ),
		attrKey = "amplify";
	div.style.display = "none";
	document.getElementsByTagName( "head" )[ 0 ].appendChild( div );

	// we can't feature detect userData support
	// so just try and see if it fails
	// surprisingly, even just adding the behavior isn't enough for a failure
	// so we need to load the data as well
	try {
		div.addBehavior( "#default#userdata" );
		div.load( attrKey );
	} catch( e ) {
		div.parentNode.removeChild( div );
		return;
	}

	store.addType( "userData", function( key, value, options ) {
		div.load( attrKey );
		var attr, parsed, prevValue, i, remove,
			ret = value,
			now = (new Date()).getTime();

		if ( !key ) {
			ret = {};
			remove = [];
			i = 0;
			while ( attr = div.XMLDocument.documentElement.attributes[ i++ ] ) {
				parsed = JSON.parse( attr.value );
				if ( parsed.expires && parsed.expires <= now ) {
					remove.push( attr.name );
				} else {
					ret[ attr.name ] = parsed.data;
				}
			}
			while ( key = remove.pop() ) {
				div.removeAttribute( key );
			}
			div.save( attrKey );
			return ret;
		}

		// convert invalid characters to dashes
		// http://www.w3.org/TR/REC-xml/#NT-Name
		// simplified to assume the starting character is valid
		// also removed colon as it is invalid in HTML attribute names
		key = key.replace( /[^-._0-9A-Za-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u37f-\u1fff\u200c-\u200d\u203f\u2040\u2070-\u218f]/g, "-" );
		// adjust invalid starting character to deal with our simplified sanitization
		key = key.replace( /^-/, "_-" );

		if ( value === undefined ) {
			attr = div.getAttribute( key );
			parsed = attr ? JSON.parse( attr ) : { expires: -1 };
			if ( parsed.expires && parsed.expires <= now ) {
				div.removeAttribute( key );
			} else {
				return parsed.data;
			}
		} else {
			if ( value === null ) {
				div.removeAttribute( key );
			} else {
				// we need to get the previous value in case we need to rollback
				prevValue = div.getAttribute( key );
				parsed = JSON.stringify({
					data: value,
					expires: (options.expires ? (now + options.expires) : null)
				});
				div.setAttribute( key, parsed );
			}
		}

		try {
			div.save( attrKey );
		// quota exceeded
		} catch ( error ) {
			// roll the value back to the previous value
			if ( prevValue === null ) {
				div.removeAttribute( key );
			} else {
				div.setAttribute( key, prevValue );
			}

			// expire old data and try again
			store.userData();
			try {
				div.setAttribute( key, parsed );
				div.save( attrKey );
			} catch ( error ) {
				// roll the value back to the previous value
				if ( prevValue === null ) {
					div.removeAttribute( key );
				} else {
					div.setAttribute( key, prevValue );
				}
				throw store.error();
			}
		}
		return ret;
	});
}() );

// in-memory storage
// fallback for all browsers to enable the API even if we can't persist data
(function() {
	var memory = {},
		timeout = {};

	function copy( obj ) {
		return obj === undefined ? undefined : JSON.parse( JSON.stringify( obj ) );
	}

	store.addType( "memory", function( key, value, options ) {
		if ( !key ) {
			return copy( memory );
		}

		if ( value === undefined ) {
			return copy( memory[ key ] );
		}

		if ( timeout[ key ] ) {
			clearTimeout( timeout[ key ] );
			delete timeout[ key ];
		}

		if ( value === null ) {
			delete memory[ key ];
			return null;
		}

		memory[ key ] = value;
		if ( options.expires ) {
			timeout[ key ] = setTimeout(function() {
				delete memory[ key ];
				delete timeout[ key ];
			}, options.expires );
		}

		return value;
	});
}() );

}( this.amplify = this.amplify || {} ) );
<commit_msg>Store: Don't register web storage in Safari Private Browsing mode. Fixes #59 - amplify.store quota exceeded safari 5.1.5 private browsing.<commit_after>/*!
 * Amplify Store - Persistent Client-Side Storage @VERSION
 *
 * Copyright 2011 appendTo LLC. (http://appendto.com/team)
 * Dual licensed under the MIT or GPL licenses.
 * http://appendto.com/open-source-licenses
 *
 * http://amplifyjs.com
 */
(function( amplify, undefined ) {

var store = amplify.store = function( key, value, options ) {
	var type = store.type;
	if ( options && options.type && options.type in store.types ) {
		type = options.type;
	}
	return store.types[ type ]( key, value, options || {} );
};

store.types = {};
store.type = null;
store.addType = function( type, storage ) {
	if ( !store.type ) {
		store.type = type;
	}

	store.types[ type ] = storage;
	store[ type ] = function( key, value, options ) {
		options = options || {};
		options.type = type;
		return store( key, value, options );
	};
};
store.error = function() {
	return "amplify.store quota exceeded";
};

var rprefix = /^__amplify__/;
function createFromStorageInterface( storageType, storage ) {
	store.addType( storageType, function( key, value, options ) {
		var storedValue, parsed, i, remove,
			ret = value,
			now = (new Date()).getTime();

		if ( !key ) {
			ret = {};
			remove = [];
			i = 0;
			try {
				// accessing the length property works around a localStorage bug
				// in Firefox 4.0 where the keys don't update cross-page
				// we assign to key just to avoid Closure Compiler from removing
				// the access as "useless code"
				// https://bugzilla.mozilla.org/show_bug.cgi?id=662511
				key = storage.length;

				while ( key = storage.key( i++ ) ) {
					if ( rprefix.test( key ) ) {
						parsed = JSON.parse( storage.getItem( key ) );
						if ( parsed.expires && parsed.expires <= now ) {
							remove.push( key );
						} else {
							ret[ key.replace( rprefix, "" ) ] = parsed.data;
						}
					}
				}
				while ( key = remove.pop() ) {
					storage.removeItem( key );
				}
			} catch ( error ) {}
			return ret;
		}

		// protect against name collisions with direct storage
		key = "__amplify__" + key;

		if ( value === undefined ) {
			storedValue = storage.getItem( key );
			parsed = storedValue ? JSON.parse( storedValue ) : { expires: -1 };
			if ( parsed.expires && parsed.expires <= now ) {
				storage.removeItem( key );
			} else {
				return parsed.data;
			}
		} else {
			if ( value === null ) {
				storage.removeItem( key );
			} else {
				parsed = JSON.stringify({
					data: value,
					expires: options.expires ? now + options.expires : null
				});
				try {
					storage.setItem( key, parsed );
				// quota exceeded
				} catch( error ) {
					// expire old data and try again
					store[ storageType ]();
					try {
						storage.setItem( key, parsed );
					} catch( error ) {
						throw store.error();
					}
				}
			}
		}

		return ret;
	});
}

// localStorage + sessionStorage
// IE 8+, Firefox 3.5+, Safari 4+, Chrome 4+, Opera 10.5+, iPhone 2+, Android 2+
for ( var webStorageType in { localStorage: 1, sessionStorage: 1 } ) {
	// try/catch for file protocol in Firefox and Private Browsing in Safari 5
	try {
		// Safari 5 in Private Browsing mode exposes localStorage
		// but doesn't allow storing data, so we attempt to store and remove an item.
		// This will unfortunately give us a false negative if we're at the limit.
		window[ webStorageType ].setItem( "__amplify__", "x" );
		window[ webStorageType ].removeItem( "__amplify__" );
		createFromStorageInterface( webStorageType, window[ webStorageType ] );
	} catch( e ) {}
}

// globalStorage
// non-standard: Firefox 2+
// https://developer.mozilla.org/en/dom/storage#globalStorage
if ( !store.types.localStorage && window.globalStorage ) {
	// try/catch for file protocol in Firefox
	try {
		createFromStorageInterface( "globalStorage",
			window.globalStorage[ window.location.hostname ] );
		// Firefox 2.0 and 3.0 have sessionStorage and globalStorage
		// make sure we default to globalStorage
		// but don't default to globalStorage in 3.5+ which also has localStorage
		if ( store.type === "sessionStorage" ) {
			store.type = "globalStorage";
		}
	} catch( e ) {}
}

// userData
// non-standard: IE 5+
// http://msdn.microsoft.com/en-us/library/ms531424(v=vs.85).aspx
(function() {
	// IE 9 has quirks in userData that are a huge pain
	// rather than finding a way to detect these quirks
	// we just don't register userData if we have localStorage
	if ( store.types.localStorage ) {
		return;
	}

	// append to html instead of body so we can do this from the head
	var div = document.createElement( "div" ),
		attrKey = "amplify";
	div.style.display = "none";
	document.getElementsByTagName( "head" )[ 0 ].appendChild( div );

	// we can't feature detect userData support
	// so just try and see if it fails
	// surprisingly, even just adding the behavior isn't enough for a failure
	// so we need to load the data as well
	try {
		div.addBehavior( "#default#userdata" );
		div.load( attrKey );
	} catch( e ) {
		div.parentNode.removeChild( div );
		return;
	}

	store.addType( "userData", function( key, value, options ) {
		div.load( attrKey );
		var attr, parsed, prevValue, i, remove,
			ret = value,
			now = (new Date()).getTime();

		if ( !key ) {
			ret = {};
			remove = [];
			i = 0;
			while ( attr = div.XMLDocument.documentElement.attributes[ i++ ] ) {
				parsed = JSON.parse( attr.value );
				if ( parsed.expires && parsed.expires <= now ) {
					remove.push( attr.name );
				} else {
					ret[ attr.name ] = parsed.data;
				}
			}
			while ( key = remove.pop() ) {
				div.removeAttribute( key );
			}
			div.save( attrKey );
			return ret;
		}

		// convert invalid characters to dashes
		// http://www.w3.org/TR/REC-xml/#NT-Name
		// simplified to assume the starting character is valid
		// also removed colon as it is invalid in HTML attribute names
		key = key.replace( /[^-._0-9A-Za-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u37f-\u1fff\u200c-\u200d\u203f\u2040\u2070-\u218f]/g, "-" );
		// adjust invalid starting character to deal with our simplified sanitization
		key = key.replace( /^-/, "_-" );

		if ( value === undefined ) {
			attr = div.getAttribute( key );
			parsed = attr ? JSON.parse( attr ) : { expires: -1 };
			if ( parsed.expires && parsed.expires <= now ) {
				div.removeAttribute( key );
			} else {
				return parsed.data;
			}
		} else {
			if ( value === null ) {
				div.removeAttribute( key );
			} else {
				// we need to get the previous value in case we need to rollback
				prevValue = div.getAttribute( key );
				parsed = JSON.stringify({
					data: value,
					expires: (options.expires ? (now + options.expires) : null)
				});
				div.setAttribute( key, parsed );
			}
		}

		try {
			div.save( attrKey );
		// quota exceeded
		} catch ( error ) {
			// roll the value back to the previous value
			if ( prevValue === null ) {
				div.removeAttribute( key );
			} else {
				div.setAttribute( key, prevValue );
			}

			// expire old data and try again
			store.userData();
			try {
				div.setAttribute( key, parsed );
				div.save( attrKey );
			} catch ( error ) {
				// roll the value back to the previous value
				if ( prevValue === null ) {
					div.removeAttribute( key );
				} else {
					div.setAttribute( key, prevValue );
				}
				throw store.error();
			}
		}
		return ret;
	});
}() );

// in-memory storage
// fallback for all browsers to enable the API even if we can't persist data
(function() {
	var memory = {},
		timeout = {};

	function copy( obj ) {
		return obj === undefined ? undefined : JSON.parse( JSON.stringify( obj ) );
	}

	store.addType( "memory", function( key, value, options ) {
		if ( !key ) {
			return copy( memory );
		}

		if ( value === undefined ) {
			return copy( memory[ key ] );
		}

		if ( timeout[ key ] ) {
			clearTimeout( timeout[ key ] );
			delete timeout[ key ];
		}

		if ( value === null ) {
			delete memory[ key ];
			return null;
		}

		memory[ key ] = value;
		if ( options.expires ) {
			timeout[ key ] = setTimeout(function() {
				delete memory[ key ];
				delete timeout[ key ];
			}, options.expires );
		}

		return value;
	});
}() );

}( this.amplify = this.amplify || {} ) );
<|endoftext|>


<commit_before>/**
 * Created with RubyMine.
 * User: i061485
 * Date: 2/23/14
 * Time: 11:03 AM
 */

define([
    'plugins/plugin',
    'plugins/widgets/widget.content.controller'
], function defineImageController(PluginBase, WidgetContentController) {

    /**
     * Define image controller
     * @class ImageController
     * @extends PluginController
     * @extends WidgetContentController
     * @constructor
     */
    var ImageController = function ImageController() {
    };

    return ImageController.extend('ImageController', {

        /**
         * Check rendering content
         * @memberOf ImageController
         */
        checkEmbeddedContent: function checkEmbeddedContent() {

            /**
             * Define event
             * @type {splitEmbeddedContent|setEmbeddedContent|string}
             */
            var event = this.model.getPrefs('imageSplitContent') ?
                this.eventmanager.eventList.splitEmbeddedContent :
                this.eventmanager.eventList.setEmbeddedContent;

            this.observer.publish(event);
        },

        /**
         * Set embedded content
         * @memberOf ImageController
         */
        setEmbeddedContent: function setEmbeddedContent() {

            this.view.elements.$image.renderEmbeddedContent({
                url: this.model.getPrefs('imageUrl'),
                text: this.model.getPrefs('imageText'),
                repeatX: this.model.getPrefs('imageRepeatX'),
                repeatY: this.model.getPrefs('imageRepeatY'),
                stretch: this.model.getPrefs('imageStretch')
            });
        },

        /**
         * Split embedded content
         * @memberOf ImageController
         * @param subscribers
         * @param {boolean|*} simulate
         */
        splitEmbeddedContent: function splitEmbeddedContent(subscribers, simulate) {

            subscribers = this.base.define(
                subscribers,
                this.controller.getSubscribers(
                    this.eventmanager.eventList.splitEmbeddedContent
                )
            );

            this.view.elements.$image.renderSplitEmbeddedContent({
                url: this.model.getPrefs('imageUrl'),
                text: this.model.getPrefs('imageText'),
                repeatX: this.model.getPrefs('imageRepeatX'),
                repeatY: this.model.getPrefs('imageRepeatY'),
                stretch: this.model.getPrefs('imageStretch'),
                splitTo: subscribers.length,
                simulate: this.base.defineBoolean(simulate, false, true)
            });

        },

        /**
         * Update preview
         * @memberOf ImageController
         * @param event
         */
        updatePreview: function updatePreview(event) {

            /**
             * Get scope
             * @type {Image}
             */
            var scope = this.scope,
                $referrer = scope.referrer,
                $modal = $referrer.view.get$modal(),
                $preferences = scope.view.elements.$preferences;

            if ($preferences) {
                $preferences.updatePreviewImage(
                    $modal,
                    event
                );
            }
        },

        /**
         * Add Image rule
         * @memberOf ImageController
         * @param e
         */
        addImageRule: function addImageRule(e) {

            /**
             * Define $button
             * @type {*|jQuery|HTMLElement}
             */
            var $button = $(e.target),
                scope = this.scope;

            scope.observer.publish(
                scope.eventmanager.eventList.publishRule,
                [$button.attr('value'), this.scope.constructor.prototype.name]
            );
        }

    }, PluginBase.prototype, WidgetContentController.prototype);
});<commit_msg>cont image range<commit_after>/**
 * Created with RubyMine.
 * User: i061485
 * Date: 2/23/14
 * Time: 11:03 AM
 */

define([
    'plugins/plugin',
    'plugins/widgets/widget.content.controller'
], function defineImageController(PluginBase, WidgetContentController) {

    /**
     * Define image controller
     * @class ImageController
     * @extends PluginController
     * @extends WidgetContentController
     * @constructor
     */
    var ImageController = function ImageController() {
    };

    return ImageController.extend('ImageController', {

        /**
         * Check rendering content
         * @memberOf ImageController
         */
        checkEmbeddedContent: function checkEmbeddedContent() {

            /**
             * Define event
             * @type {splitEmbeddedContent|setEmbeddedContent|string}
             */
            var event = this.model.getPrefs('imageSplitContent') ?
                this.eventmanager.eventList.splitEmbeddedContent :
                this.eventmanager.eventList.setEmbeddedContent;

            this.observer.publish(event);
        },

        /**
         * Set embedded content
         * @memberOf ImageController
         */
        setEmbeddedContent: function setEmbeddedContent() {

            this.view.elements.$image.renderEmbeddedContent({
                url: this.model.getPrefs('imageUrl'),
                text: this.model.getPrefs('imageText'),
                repeatX: this.model.getPrefs('imageRepeatX'),
                repeatY: this.model.getPrefs('imageRepeatY'),
                stretch: this.model.getPrefs('imageStretch')
            });
        },

        /**
         * Split embedded content
         * @memberOf ImageController
         * @param subscribers
         * @param {boolean|*} simulate
         */
        splitEmbeddedContent: function splitEmbeddedContent(subscribers, simulate) {

            subscribers = this.base.define(
                subscribers,
                this.controller.getSubscribers(
                    this.eventmanager.eventList.splitEmbeddedContent
                )
            );

            this.view.elements.$image.renderSplitEmbeddedContent({
                url: this.model.getPrefs('imageUrl'),
                text: this.model.getPrefs('imageText'),
                repeatX: this.model.getPrefs('imageRepeatX'),
                repeatY: this.model.getPrefs('imageRepeatY'),
                stretch: this.model.getPrefs('imageStretch'),
                splitTo: subscribers.length,
                simulate: this.base.defineBoolean(simulate, false, true)
            });
        },

        /**
         * Update preview
         * @memberOf ImageController
         * @param event
         */
        updatePreview: function updatePreview(event) {

            /**
             * Get scope
             * @type {Image}
             */
            var scope = this.scope,
                $referrer = scope.referrer,
                $modal = $referrer.view.get$modal(),
                $preferences = scope.view.elements.$preferences;

            if ($preferences) {
                $preferences.updatePreviewImage(
                    $modal,
                    event
                );
            }
        },

        /**
         * Add Image rule
         * @memberOf ImageController
         * @param e
         */
        addImageRule: function addImageRule(e) {

            /**
             * Define $button
             * @type {*|jQuery|HTMLElement}
             */
            var $button = $(e.target),
                scope = this.scope;

            scope.observer.publish(
                scope.eventmanager.eventList.publishRule,
                [$button.attr('value'), this.scope.constructor.prototype.name]
            );
        }

    }, PluginBase.prototype, WidgetContentController.prototype);
});<|endoftext|>


<commit_before>/*shimThis.js - by Danieltamkin*/
/*shim an element ontop of another element*/
(function( $ ) {
	$.fn.shimThis = function($data) {
		$this = $(this);
		data($data);
		function data(data){
			if(data == undefined){
				$data = dataTemplate();
			}
			else{
				if(data.ontop == undefined && data.offset == undefined){
					$data = dataTemplate();
				}
				else{
					$data = data;
				}
			}
		}
		function dataTemplate(){
			$data = {
				ontop: 'null',
				offset: '0'
			}
			return $data;
		}
		function calculate(){
			if($data.ontop != "null"){
				var elementHeight 		= $this.height(),
						ontopHeight 			= $($data.ontop).height(),
						elementShim 			= 0;
			  if($data.offset == "0"){
					$data.offset = "25%";
					$data.offset = $data.offset+"px";
				}
				$ontop = $($data.ontop);
				$this.css("z-index","999");
				$this.css("margin-top","-"+$data.offset);
				console.log("shimThis: Calculation Complete");
			}
			else{
				console.log("shimThis: Error no ontop element to calculate.");
			}
		}
		return this.each(function() {
			calculate();
			$(window).resize(function() {
				calculate();
			});
		});
  };
}( jQuery ));<commit_msg>fixed doubling of offset during calc.<commit_after>/*shimThis.js - by Danieltamkin*/
/*shim an element ontop of another element*/
(function( $ ) {
	$.fn.shimThis = function($data) {
		$this = $(this);
		data($data);
		function data(data){
			if(data == undefined){
				$data = dataTemplate();
			}
			else{
				if(data.ontop == undefined && data.offset == undefined){
					$data = dataTemplate();
				}
				else{
					$data = data;
				}
			}
		}
		function dataTemplate(){
			$data = {
				ontop: 'null',
				offset: '0'
			}
			return $data;
		}
		function calculate(){
			if($data.ontop != "null"){
				var elementHeight 		= $this.height(),
						ontopHeight 			= $($data.ontop).height(),
						elementShim 			= 0;
			  if($data.offset == "0"){
					$data.offset = "25%";
				}
				$ontop = $($data.ontop);
				$this.css("z-index","999");
				$this.css("margin-top","-"+$data.offset);
				console.log("shimThis: Calculation Complete");
			}
			else{
				console.log("shimThis: Error no ontop element to calculate.");
			}
		}
		return this.each(function() {
			calculate();
			$(window).resize(function() {
				calculate();
			});
		});
  };
}( jQuery ));
<|endoftext|>


<commit_before>$(function() {
    var toggleVisibility = $("article input");

    // toggleVisibility is hidden by default for users who don't have
    // JavaScript enabled; this is to not confuse them by showing them
    // a nonfunctional button. Make it appear now.
    toggleVisibility.css("display", "inline");

    var hideArticle = function(contentToHide) {
        contentToHide.fadeOut("fast");
    };

    var showArticle = function(contentToShow) {
        contentToShow.fadeIn("fast");
    }

    toggleVisibility.click(function() {
        // find the content whose visibility will be toggled
        var parentArticle = $(this).parents("article");
        var contentToChange = parentArticle.children().not("h3");
        // hide if shown, show if hidden
        if ($(this).val() === "Hide") {
            hideArticle(contentToChange);
            $(this).val("Show");
        }
        else {
            showArticle(contentToChange);
            $(this).val("Hide");
        }
    });
});
<commit_msg>Changed the hide and show functions to use slide up and down<commit_after>$(function() {
    var toggleVisibility = $("article input");

    // toggleVisibility is hidden by default for users who don't have
    // JavaScript enabled; this is to not confuse them by showing them
    // a nonfunctional button. Make it appear now.
    toggleVisibility.css("display", "inline");

    var hideArticle = function(contentToHide) {
        contentToHide.slideUp();
    };

    var showArticle = function(contentToShow) {
        contentToShow.slideDown();
    }

    toggleVisibility.click(function() {
        // find the content whose visibility will be toggled
        var parentArticle = $(this).parents("article");
        var contentToChange = parentArticle.children().not("h3");
        // hide if shown, show if hidden
        if ($(this).val() === "Hide") {
            hideArticle(contentToChange);
            $(this).val("Show");
        }
        else {
            showArticle(contentToChange);
            $(this).val("Hide");
        }
    });
});
<|endoftext|>


<commit_before>(function() {
"use strict";

var extend = angular.extend;

var STRIPE_CHECKOUT_URL = "https://checkout.stripe.com/checkout.js";

var OPTION_ATTRIBUTE_MAP = {
  key: "data-key",
  image: "data-image",
  name: "data-name",
  description: "data-description",
  amount: "data-amount",
  currency: "data-currency",
  panelLabel: "data-panel-label",
  zipCode: "data-zip-code",
  email: "data-email",
  label: "data-label",
  allowRememberMe: "data-allow-remember-me"
};

angular.module("stripe.checkout",[])
  .directive("stripeCheckout",StripeCheckoutDirective)
  .provider("StripeCheckout",StripeCheckoutProvider);

StripeCheckoutDirective.$inject = ["$parse", "StripeCheckout"];

function StripeCheckoutDirective($parse, StripeCheckout) {
  return { link: link };

  function link(scope, el, attrs) {
    var handler,
        callback = $parse(attrs.stripeCheckout)(scope);

    StripeCheckout.load()
      .then(function() {
        handler = StripeCheckout.configure(getOptions(el));
      });

    el.on("click",function() {
      if (handler)
        handler.open().then(function(result) {
          callback.apply(null,result);
        });
    });
  }

  function getOptions(el) {
    var opt, val,
        options = {};

    for (opt in OPTION_ATTRIBUTE_MAP) {
      val = el.attr(OPTION_ATTRIBUTE_MAP[opt]);
      if (typeof val !== "undefined")
        options[opt] = val;
    }

    return options;
  }
}

function StripeCheckoutProvider() {
  var defaults = {};

  this.defaults = function(options) {
    extend(defaults,options);
  };

  this.load = function(StripeCheckout) {
    return StripeCheckout.load();
  };

  this.load.$inject = ["StripeCheckout"];

  this.$get = function($document, $q) {
    return new StripeCheckoutService($document,$q,defaults);
  };

  this.$get.$inject = ["$document", "$q"];
}

function StripeCheckoutService($document, $q, providerDefaults) {
  var defaults = {},
      promise;

  this.configure = function(options) {
    return new StripeHandlerWrapper($q,extend({},
      providerDefaults,
      defaults,
      options
    ));
  };

  this.load = function() {
    if (!promise)
      promise = loadLibrary($document,$q);

    return promise;
  };

  this.defaults = function(options) {
    extend(defaults,options);
  };
}

function StripeHandlerWrapper($q, options) {
  var deferred, success;

  var handler = StripeCheckout.configure(extend({},options,{
    token: function(token, args) {
      if (options.token) options.token(token,args);

      success = true;
      deferred.resolve([token, args]);
    },

    closed: function() {
      if (options.closed) options.closed();
      if (!success) deferred.reject();
    }
  }));

  this.open = function(openOptions) {
    deferred = $q.defer();
    success = false;

    handler.open(openOptions);

    return deferred.promise;
  };
}

function loadLibrary($document, $q) {
  return $q(function(resolve, reject) {
    var doc = $document[0],
        script = doc.createElement("script");
    script.src = STRIPE_CHECKOUT_URL;

    script.onload = function() {
      resolve();
    };

    script.onreadystatechange = function() {
      var rs = this.readyState;
      if (rs === "loaded" || rs === "complete")
        resolve();
    };

    script.onerror = function() {
      reject(new Error("Unable to load checkout.js"));
    };

    var container = doc.getElementsByTagName("head")[0];
    container.appendChild(script);
  });
}

})();
<commit_msg>use angular<commit_after>(function() {
"use strict";

var extend = angular.extend;

var STRIPE_CHECKOUT_URL = "https://checkout.stripe.com/checkout.js";

var OPTION_ATTRIBUTE_MAP = {
  key: "data-key",
  image: "data-image",
  name: "data-name",
  description: "data-description",
  amount: "data-amount",
  currency: "data-currency",
  panelLabel: "data-panel-label",
  zipCode: "data-zip-code",
  email: "data-email",
  label: "data-label",
  allowRememberMe: "data-allow-remember-me"
};

angular.module("stripe.checkout",[])
  .directive("stripeCheckout",StripeCheckoutDirective)
  .provider("StripeCheckout",StripeCheckoutProvider);

StripeCheckoutDirective.$inject = ["$parse", "StripeCheckout"];

function StripeCheckoutDirective($parse, StripeCheckout) {
  return { link: link };

  function link(scope, el, attrs) {
    var handler,
        callback = $parse(attrs.stripeCheckout)(scope);

    StripeCheckout.load()
      .then(function() {
        handler = StripeCheckout.configure(getOptions(el));
      });

    el.on("click",function() {
      if (handler)
        handler.open().then(function(result) {
          callback.apply(null,result);
        });
    });
  }

  function getOptions(el) {
    var opt, val,
        options = {};

    for (opt in OPTION_ATTRIBUTE_MAP) {
      val = el.attr(OPTION_ATTRIBUTE_MAP[opt]);
      if (typeof val !== "undefined")
        options[opt] = val;
    }

    return options;
  }
}

function StripeCheckoutProvider() {
  var defaults = {};

  this.defaults = function(options) {
    extend(defaults,options);
  };

  this.load = function(StripeCheckout) {
    return StripeCheckout.load();
  };

  this.load.$inject = ["StripeCheckout"];

  this.$get = function($document, $q) {
    return new StripeCheckoutService($document,$q,defaults);
  };

  this.$get.$inject = ["$document", "$q"];
}

function StripeCheckoutService($document, $q, providerDefaults) {
  var defaults = {},
      promise;

  this.configure = function(options) {
    return new StripeHandlerWrapper($q,extend({},
      providerDefaults,
      defaults,
      options
    ));
  };

  this.load = function() {
    if (!promise)
      promise = loadLibrary($document,$q);

    return promise;
  };

  this.defaults = function(options) {
    extend(defaults,options);
  };
}

function StripeHandlerWrapper($q, options) {
  var deferred, success;

  var handler = StripeCheckout.configure(extend({},options,{
    token: function(token, args) {
      if (options.token) options.token(token,args);

      success = true;
      deferred.resolve([token, args]);
    },

    closed: function() {
      if (options.closed) options.closed();
      if (!success) deferred.reject();
    }
  }));

  this.open = function(openOptions) {
    deferred = $q.defer();
    success = false;

    handler.open(openOptions);

    return deferred.promise;
  };
}

function loadLibrary($document, $q) {
  var deferred = $q.defer();

  var doc = $document[0],
      script = doc.createElement("script");
  script.src = STRIPE_CHECKOUT_URL;

  script.onload = function () {
    deferred.resolve();
  };

  script.onreadystatechange = function () {
    var rs = this.readyState;
    if (rs === "loaded" || rs === "complete")
      deferred.resolve();
  };

  script.onerror = function () {
    deferred.reject(new Error("Unable to load checkout.js"));
  };

  var container = doc.getElementsByTagName("head")[0];
  container.appendChild(script);

  return deferred.promise;
}

})();
<|endoftext|>


<commit_before>

// This code is kind of horrible, isn't there a better way?
function dohtmlreplace(element) {
	var nodes = element.childNodes;
	for (var n=0; n<nodes.length; n++) {
		if (nodes[n].nodeType == Node.TEXT_NODE) {
			var r = new RegExp('\\$\\s*([0-9,]+\\.?\\d*)', 'gi');
			l = 0
			nc = ''
			do {
				m = r.exec(nodes[n].textContent);
				if (r.lastIndex != 0) {
					z = parseFloat(m[1].replace(',',''));
					x = z;
					o = Math.pow(10, Math.floor(Math.log(z) / Math.LN10) - 1);
					x = Math.ceil(z / o);
					/*if (Math.floor(z / o) % 10 == 9) {
						x = x + 1
					}*/
					x = x * o;
					//diff = z - x
					nu = '$ ' + x.toFixed(2); // + '(' + diff.toFixed(2) + ')';
					app = nodes[n].textContent.substring(l, r.lastIndex-m[0].length) + nu
					nc = nc + app;
					l = r.lastIndex
				}
			} while (r.lastIndex != 0);
			nodes[n].textContent = nc + nodes[n].textContent.substr(l)
		} else {
			dohtmlreplace(nodes[n]);
		}
	}
}
dohtmlreplace(document.body);<commit_msg>Improved formatting slightly - preserves the space between the $ and the number, and the number of decimal places<commit_after>

// This code is kind of horrible, isn't there a better way?
function dohtmlreplace(element) {
	var nodes = element.childNodes;
	for (var n=0; n<nodes.length; n++) {
		if (nodes[n].nodeType == Node.TEXT_NODE) {
			var r = new RegExp('\\$(\\s*)([0-9,]+\\.?(\\d*))', 'gi');
			l = 0
			nc = ''
			do {
				m = r.exec(nodes[n].textContent);
				if (r.lastIndex != 0) {
					z = parseFloat(m[2].replace(',',''));
					x = z;
					o = Math.pow(10, Math.floor(Math.log(z) / Math.LN10) - 1);
					x = Math.ceil(z / o);
					/*if (Math.floor(z / o) % 10 == 9) {
						x = x + 1
					}*/
					x = x * o;
					//diff = z - x
					nu = '$' + m[1] + x.toFixed(m[3].length); // + '(' + diff.toFixed(2) + ')';
					app = nodes[n].textContent.substring(l, r.lastIndex-m[0].length) + nu
					nc = nc + app;
					l = r.lastIndex
				}
			} while (r.lastIndex != 0);
			nodes[n].textContent = nc + nodes[n].textContent.substr(l)
		} else {
			dohtmlreplace(nodes[n]);
		}
	}
}
dohtmlreplace(document.body);<|endoftext|>


<commit_before>'use strict';

const { set, merge, mergeWith } = require('lodash');

const { validate } = require('../../utilities');

/**
 * Check API input, for client-side errors (e.g. 400)
 * Only checks basic input according to current operation
 * In a nutshell, checks that:
 *  - required attributes are defined
 *  - disabled attributes are not defined
 *  - `id` is not an array, `ids` is an array, `filter` is an object, `data` is an array or object (depending on operation)
 *  - `order_by` syntax looks valid (does not check whether it is semantically correct)
 **/
const validateClientInputSyntax = function ({ modelName, operation, args }) {
  const type = 'clientInputSyntax';
  const schema = getValidateClientSchema({ operation });
  validate({ schema, data: args, reportInfo: { type, operation, modelName } });
};

// Builds JSON schema to validate against
const getValidateClientSchema = function({ operation }) {
  const rule = rules[operation];
  const properties = getProperties({ rule });
  const requiredProperties = getRequiredProps(rule.required);
  const forbiddenProperties = getForbiddenProperties({ rule });

  const schema = merge({ type: 'object', additionalProperties: false }, properties, requiredProperties, forbiddenProperties);
  return schema;
};

// Get properties to check against, as JSON schema, for a given operation
const getProperties = function ({ rule }) {
  return validateClientSchema
    // Whitelists input according to `allowed` or `required`
    .filter(({ name }) => rule.allowed.includes(name) || rule.required.includes(name))
    .map(({ name, value }) => {
      // Fire value functions
      value = typeof value === 'function' ? value(rule) : value;
      // Use `properties` JSON schema syntax
      name = 'properties.' + name.replace(/\./g, '.properties.');
      return { name, value };
    })
    // Handles 'VAR.VAR2' dot-delimited syntax
    .reduce((props, { name, value }) => set(props, name, value), {});
};

// JSON schemas applied to input
const validateClientSchema = [
  { name: 'data', value: ({ dataMultiple }) => !dataMultiple ? { type: 'object' } : { type: 'array', items: { type: 'object' } } },
  { name: 'filter', value: { type: 'object' } },
  { name: 'filter.id', value: ({ idMultiple }) => !idMultiple ? { not: { type: 'array' } } : { type: 'array', } },
  // Matches order_by value, i.e. 'ATTR[+|-],...'
  { name: 'order_by', value: { type: 'string', pattern: '^([a-z0-9_]+[+-]?)(,[a-z0-9_]+[+-]?)*$' } },
];

// Transform required properties into JSON schema
// E.g. ['filter', 'filter.var', 'filter.var.vat']
// to { required: ['filter'], properties: { filter: { required: ['var'], properties: { var: { required: ['vat'] } } } } }
const getRequiredProps = function (props) {
  // E.g. can be ['filter', 'filter.var', 'filter.var.vat']
  return props
    // E.g. this could transform to ['filter', 'properties.filter.var', 'properties.filter.properties.var.vat']
    .map(requiredProp => {
      const parts = requiredProp.split('.');
      return parts
        .reduce((memo, part, index) => memo
          .concat(index === parts.length - 1 ? [] : 'properties')
          .concat(part),
        [])
        .join('.')
        // Allow using syntax like `data.*.id` when `data` is an array
        .replace('properties.*', 'items');
    })
    // E.g. this could transform to [{ required: ['filter'] }, { properties: { filter: { required: ['var'] } } },
    // { properties: { filter: { properties: { var: { required: ['vat'] } } } } }]
    .map(requiredProp => {
      const value = [requiredProp.replace(/.*\./, '')];
      const path = requiredProp.replace(/[^.]+$/, 'required');
      return set({}, path, value);
    })
    // Merge array of objects into single object
    .reduce((memo, value) => mergeWith(memo, value, (obj, src) => obj instanceof Array ? obj.concat(src) : undefined), {});
};

// Add JSON schema forbidding properties
// E.g. `data.id` becomes { properties: { data: { properties: { id: false } } } }
// Only required for nested properties, since `allowed` is whitelisting
const getForbiddenProperties = function ({ rule: { forbidden = [] } }) {
  return forbidden
    .map(forbiddenProp => 'properties.' + forbiddenProp.replace(/\./g, '.properties.'))
    .map(path => set({}, path, false))
    .reduce(merge, {});
};

/**
 * List of rules for allowed|required attributes, according to the current operation
 * `required` implies `allowed`
 * `dataSingle` is `data` as object, `dataMultiple` is `data` as array. Same for `idMultiple`
 **/
/* eslint-disable key-spacing, no-multi-spaces */
const rules = {
  findOne:      { allowed: [],                                      required: ['filter', 'filter.id'],                    },
  findMany:     { allowed: ['filter', 'filter.id', 'order_by'],     required: [],
                  idMultiple: true                                                                                },
  deleteOne:    { allowed: [],                                      required: ['filter', 'filter.id']                     },
  deleteMany:   { allowed: ['filter', 'filter.id', 'order_by'],     required: [],
                  idMultiple: true                                                                                },
  updateOne:    { allowed: [],                                      required: ['data', 'filter', 'filter.id'],
                                                                    forbidden: ['data.id']                                },
  updateMany:   { allowed: ['filter', 'filter.id', 'order_by'],     required: ['data'],
                  idMultiple: true,                                 forbidden: ['data.id']                                },
  upsertOne:    { allowed: [],                                      required: ['data', 'data.id']                         },
  upsertMany:   { allowed: ['order_by'],                            required: ['data', 'data.*.id'],
                  dataMultiple: true                                                                                },
  replaceOne:   { allowed: [],                                      required: ['data', 'data.id']                         },
  replaceMany:  { allowed: ['order_by'],                            required: ['data', 'data.*.id'],
                  dataMultiple: true                                                                                },
  createOne:    { allowed: ['data'],                                required: ['data']                                    },
  createMany:   { allowed: ['data', 'order_by'],                    required: ['data'],
                  dataMultiple: true                                                                                },
};
/* eslint-enable key-spacing, no-multi-spaces */

module.exports = {
  validateClientInputSyntax,
};
<commit_msg>Remove redundancy<commit_after>'use strict';

const { set, merge, mergeWith } = require('lodash');

const { validate } = require('../../utilities');

/**
 * Check API input, for client-side errors (e.g. 400)
 * Only checks basic input according to current operation
 * In a nutshell, checks that:
 *  - required attributes are defined
 *  - disabled attributes are not defined
 *  - `id` is not an array, `ids` is an array, `filter` is an object, `data` is an array or object (depending on operation)
 *  - `order_by` syntax looks valid (does not check whether it is semantically correct)
 **/
const validateClientInputSyntax = function ({ modelName, operation, args }) {
  const type = 'clientInputSyntax';
  const schema = getValidateClientSchema({ operation });
  validate({ schema, data: args, reportInfo: { type, operation, modelName } });
};

// Builds JSON schema to validate against
const getValidateClientSchema = function({ operation }) {
  const rule = rules[operation];
  const properties = getProperties({ rule });
  const requiredProperties = getRequiredProps(rule.required);
  const forbiddenProperties = getForbiddenProperties({ rule });

  const schema = merge({ type: 'object', additionalProperties: false }, properties, requiredProperties, forbiddenProperties);
  return schema;
};

// Get properties to check against, as JSON schema, for a given operation
const getProperties = function ({ rule }) {
  return validateClientSchema
    // Whitelists input according to `allowed` or `required`
    .filter(({ name }) => rule.allowed.includes(name) || rule.required.includes(name))
    .map(({ name, value }) => {
      // Fire value functions
      value = typeof value === 'function' ? value(rule) : value;
      // Use `properties` JSON schema syntax
      name = 'properties.' + name.replace(/\./g, '.properties.');
      return { name, value };
    })
    // Handles 'VAR.VAR2' dot-delimited syntax
    .reduce((props, { name, value }) => set(props, name, value), {});
};

// JSON schemas applied to input
const validateClientSchema = [
  { name: 'data', value: ({ dataMultiple }) => !dataMultiple ? { type: 'object' } : { type: 'array', items: { type: 'object' } } },
  { name: 'filter', value: { type: 'object' } },
  { name: 'filter.id', value: ({ idMultiple }) => !idMultiple ? { not: { type: 'array' } } : { type: 'array', } },
  // Matches order_by value, i.e. 'ATTR[+|-],...'
  { name: 'order_by', value: { type: 'string', pattern: '^([a-z0-9_]+[+-]?)(,[a-z0-9_]+[+-]?)*$' } },
];

// Transform required properties into JSON schema
// E.g. ['filter', 'filter.var', 'filter.var.vat']
// to { required: ['filter'], properties: { filter: { required: ['var'], properties: { var: { required: ['vat'] } } } } }
const getRequiredProps = function (props) {
  // E.g. can be ['filter', 'filter.var', 'filter.var.vat']
  return props
    // E.g. this could transform to ['filter', 'properties.filter.var', 'properties.filter.properties.var.vat']
    .map(requiredProp => {
      const parts = requiredProp.split('.');
      return parts
        .reduce((memo, part, index) => memo
          .concat(index === parts.length - 1 ? [] : 'properties')
          .concat(part),
        [])
        .join('.')
        // Allow using syntax like `data.*.id` when `data` is an array
        .replace('properties.*', 'items');
    })
    // E.g. this could transform to [{ required: ['filter'] }, { properties: { filter: { required: ['var'] } } },
    // { properties: { filter: { properties: { var: { required: ['vat'] } } } } }]
    .map(requiredProp => {
      const value = [requiredProp.replace(/.*\./, '')];
      const path = requiredProp.replace(/[^.]+$/, 'required');
      return set({}, path, value);
    })
    // Merge array of objects into single object
    .reduce((memo, value) => mergeWith(memo, value, (obj, src) => obj instanceof Array ? obj.concat(src) : undefined), {});
};

// Add JSON schema forbidding properties
// E.g. `data.id` becomes { properties: { data: { properties: { id: false } } } }
// Only required for nested properties, since `allowed` is whitelisting
const getForbiddenProperties = function ({ rule: { forbidden = [] } }) {
  return forbidden
    .map(forbiddenProp => 'properties.' + forbiddenProp.replace(/\./g, '.properties.'))
    .map(path => set({}, path, false))
    .reduce(merge, {});
};

/**
 * List of rules for allowed|required attributes, according to the current operation
 * `required` implies `allowed`
 * `dataSingle` is `data` as object, `dataMultiple` is `data` as array. Same for `idMultiple`
 **/
/* eslint-disable key-spacing, no-multi-spaces */
const rules = {
  findOne:      { allowed: [],                                      required: ['filter', 'filter.id'],                    },
  findMany:     { allowed: ['filter', 'filter.id', 'order_by'],     required: [],
                  idMultiple: true                                                                                },
  deleteOne:    { allowed: [],                                      required: ['filter', 'filter.id']                     },
  deleteMany:   { allowed: ['filter', 'filter.id', 'order_by'],     required: [],
                  idMultiple: true                                                                                },
  updateOne:    { allowed: [],                                      required: ['data', 'filter', 'filter.id'],
                                                                    forbidden: ['data.id']                                },
  updateMany:   { allowed: ['filter', 'filter.id', 'order_by'],     required: ['data'],
                  idMultiple: true,                                 forbidden: ['data.id']                                },
  upsertOne:    { allowed: [],                                      required: ['data', 'data.id']                         },
  upsertMany:   { allowed: ['order_by'],                            required: ['data', 'data.*.id'],
                  dataMultiple: true                                                                                },
  replaceOne:   { allowed: [],                                      required: ['data', 'data.id']                         },
  replaceMany:  { allowed: ['order_by'],                            required: ['data', 'data.*.id'],
                  dataMultiple: true                                                                                },
  createOne:    { allowed: [],                                      required: ['data']                                    },
  createMany:   { allowed: ['order_by'],                            required: ['data'],
                  dataMultiple: true                                                                                },
};
/* eslint-enable key-spacing, no-multi-spaces */

module.exports = {
  validateClientInputSyntax,
};
<|endoftext|>


<commit_before>import React from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import injectSheet from "react-jss";
import { Row, Col } from "react-bootstrap/lib";
import { toRoman } from "roman-numerals";

import SubscriptionForm from "../../accounts/components/forms/SubscriptionForm";
import { subscribe } from "../../accounts/actions";
import { GoogleAdsense } from "../../advertisements/components";

const styles = {
  ArticleFooter: {
    fontFamily: "Minion Pro",
  },
  inPrint: {
    borderBottom: "1px solid #ddd",
    color: "#000",
    fontSize: "19px",
    fontStyle: "italic",
    lineHeight: 1.4,
    marginBottom: "14px",
    paddingBottom: "13px",
    "& a": {
      color: "#000",
      textDecoration: "underline",
      "&:hover, &:active, &:focus": {
        color: "#000",
      },
    },
  },
  subscriptionCTA: {
    color: "#000",
    fontSize: "19px",
    marginBottom: "10px",
  },
  subscriptionFormContainer: {
    marginBottom: "21px",
    "& > div > form": {
      display: "flex",
    },
    "& > div > form > div": {
      display: "inline-block",
    },
    "& > div > form > div:first-child": {
      width: "80%",
      "& > div > input": {
        // email input
        borderRadius: "3px 0 0 3px",
        width: "100%",
      },
    },
    "& > div > form > div:last-child": {
      width: "20%",
      "& > button": {
        // "Subscribe" button
        borderRadius: "0 3px 3px 0",
        margin: 0,
        width: "100%",
      },
    },
  },
  "@media (max-width: 991px)": {
    ArticleFooter: {
      padding: "0 10%",
    },
  },
  "@media (max-width: 767px)": {
    ArticleFooter: {
      padding: "0 2%",
    },
  },
};

const ArticleFooter = ({ classes, article, subscribe }) => {
  const { volume, issue, section } = article;
  return (
    <Row className={classes.ArticleFooter}>
      <Col xs={12} sm={12} md={8} lg={8}>

          This article appears in print in
          {section.permalink.includes('special-issues') ? (
            <Link to={section.permalink}>{section.name}</Link>, {section.description}
          ) : (

              {`Volume ${toRoman(volume)}, Issue ${issue}`}
              {/* TODO: Lookup table for individual volume/issue links */}

          )}.

          Never miss a Spectator issue again. Sign up for our newsletter
          to receive a weekly e-mail with staff-picked stories.

          <SubscriptionForm
            onSubmit={values => subscribe(values)}
            callToAction="Go"
          />

      </Col>

        <GoogleAdsense
          adClient='ca-pub-6227330768557696'
          adSlot='1527604127'
          style=
            {{
              'display':'display:inline-block',
            }}
          adLayout='in-article'
          adFormat='auto'
          fullWidthResponsive='true'
        />

    </Row>
  );
};

//ownProps so ArticleFooter gets semi typed props
const mapDispatchToProps = (dispatch, ownProps) => {
  return bindActionCreators({ subscribe }, dispatch);
};

export default connect(null, mapDispatchToProps)(
  injectSheet(styles)(ArticleFooter),
);
<commit_msg>block<commit_after>import React from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import injectSheet from "react-jss";
import { Row, Col } from "react-bootstrap/lib";
import { toRoman } from "roman-numerals";

import SubscriptionForm from "../../accounts/components/forms/SubscriptionForm";
import { subscribe } from "../../accounts/actions";
import { GoogleAdsense } from "../../advertisements/components";

const styles = {
  ArticleFooter: {
    fontFamily: "Minion Pro",
  },
  inPrint: {
    borderBottom: "1px solid #ddd",
    color: "#000",
    fontSize: "19px",
    fontStyle: "italic",
    lineHeight: 1.4,
    marginBottom: "14px",
    paddingBottom: "13px",
    "& a": {
      color: "#000",
      textDecoration: "underline",
      "&:hover, &:active, &:focus": {
        color: "#000",
      },
    },
  },
  subscriptionCTA: {
    color: "#000",
    fontSize: "19px",
    marginBottom: "10px",
  },
  subscriptionFormContainer: {
    marginBottom: "21px",
    "& > div > form": {
      display: "flex",
    },
    "& > div > form > div": {
      display: "inline-block",
    },
    "& > div > form > div:first-child": {
      width: "80%",
      "& > div > input": {
        // email input
        borderRadius: "3px 0 0 3px",
        width: "100%",
      },
    },
    "& > div > form > div:last-child": {
      width: "20%",
      "& > button": {
        // "Subscribe" button
        borderRadius: "0 3px 3px 0",
        margin: 0,
        width: "100%",
      },
    },
  },
  "@media (max-width: 991px)": {
    ArticleFooter: {
      padding: "0 10%",
    },
  },
  "@media (max-width: 767px)": {
    ArticleFooter: {
      padding: "0 2%",
    },
  },
};

const ArticleFooter = ({ classes, article, subscribe }) => {
  const { volume, issue, section } = article;
  return (
    <Row className={classes.ArticleFooter}>
      <Col xs={12} sm={12} md={8} lg={8}>

          This article appears in print in
          {section.permalink.includes('special-issues') ? (
            <Link to={section.permalink}>{section.name}</Link>, {section.description}
          ) : (

              {`Volume ${toRoman(volume)}, Issue ${issue}`}
              {/* TODO: Lookup table for individual volume/issue links */}

          )}.

          Never miss a Spectator issue again. Sign up for our newsletter
          to receive a weekly e-mail with staff-picked stories.

          <SubscriptionForm
            onSubmit={values => subscribe(values)}
            callToAction="Go"
          />

      </Col>

        <GoogleAdsense
          adClient='ca-pub-6227330768557696'
          adSlot='1527604127'
          style=
            {{
              'display':'display:block',
            }}
          adLayout='in-article'
          adFormat='auto'
          fullWidthResponsive='true'
        />

    </Row>
  );
};

//ownProps so ArticleFooter gets semi typed props
const mapDispatchToProps = (dispatch, ownProps) => {
  return bindActionCreators({ subscribe }, dispatch);
};

export default connect(null, mapDispatchToProps)(
  injectSheet(styles)(ArticleFooter),
);
<|endoftext|>


<commit_before>'use strict'
var Hub = require('../../lib')
var hub = global.hub = new Hub({ //eslint-disable-line
  adapter: {
    inject: require('../../lib/protocol/websocket'),
    // websocket: {}
    textfield: {}
  }
})

hub.adapter.set({ websocket: 'ws://localhost:3031' })

var Element = require('vigour-element')
var app = require('vigour-element/lib/app')

app.set({
  yuzi: new Element({
    text: { $prepend: 'scope: ', val: hub.adapter.scope }
  }),
  holder: {
    $: true,
    textfield: {
      type: 'input',
      value: { $: 'textfield' }
    }
  }
})

app.holder.val = hub

// for the multi-upstreams multi-scope we do need to apply subscriptions to the original -- pretty annoying
// other fix just add extension capability for this all subs on scopes also go to original: true
<commit_msg>update<commit_after>'use strict'
var Hub = require('../../lib')
var hub = global.hub = new Hub({ //eslint-disable-line
  adapter: {
    inject: require('../../lib/protocol/websocket'),
    // websocket: {}
    // textfield: {}
  }
})

hub.adapter.set({ websocket: 'ws://localhost:3031' })

var Element = require('vigour-element')
var app = require('vigour-element/lib/app')

app.set({
  yuzi: new Element({
    text: { $prepend: 'scope: ', val: hub.adapter.scope }
  }),
  holder: {
    $: true,
    textfield: {
      // type: 'input',
      text: { $: 'textfield' }
    }
  }
})

app.holder.val = hub

// for the multi-upstreams multi-scope we do need to apply subscriptions to the original -- pretty annoying
// other fix just add extension capability for this all subs on scopes also go to original: true
<|endoftext|>


<commit_before>// Copyright 2020, University of Colorado Boulder
/**
 * @fileoverview Rule to enforce that variable names for imported images and sounds have the conventional suffix.
 * "Sound" for sound imports and "Image" for images.
 *
 * The PhET team decided this was not required for strings.
 *
 * @author Jesse Greenberg (PhET Interactive Simulations)
 * @copyright 2021 University of Colorado Boulder
 */

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function( context ) {

  const soundSourceFileSuffixes = [ 'mp3.js', 'wav.js' ];
  // const imageSourceFileSuffixes = [ 'png.js', 'jpg.js', 'cur.js' ];

  return {
    ImportDeclaration: node => {
      if ( node.source && node.source.value ) {

        // determine if the import source has an image or sound file suffix
        const containsSoundSuffix = soundSourceFileSuffixes.some( suffix => {
          return node.source.value.endsWith( suffix );
        } );

        // const containsImageSuffix = imageSourceFileSuffixes.some( suffix => {
        //   return node.source.value.endsWith( suffix );
        // } );

        // if ( containsSoundSuffix || containsImageSuffix ) {
        if ( containsSoundSuffix ) {
          // const requiredVariableSuffix = containsSoundSuffix ? 'Sound' : 'Image';
          const requiredVariableSuffix = 'Sound';

          // the Specifier (AST node containing the variable name) must include
          // the right suffix
          node.specifiers.forEach( specifier => {
            if ( !specifier.local.name.endsWith( requiredVariableSuffix ) ) {
              context.report( {
                node: node,

                // report the loc of the variable so it gets highlighed by the IDE
                loc: specifier.local.loc,
                message: `Import variable missing ${requiredVariableSuffix} suffix.`
              } );
            }
          } );
        }
      }
    }
  };
};

module.exports.schema = [
  // JSON Schema for rule options goes here
];<commit_msg>Add a TODO and note about the portions of commented out code related to checking image imports, see https://github.com/phetsims/circuit-construction-kit-common/issues/702 and https://github.com/phetsims/chipper/issues/1060<commit_after>// Copyright 2020, University of Colorado Boulder
/**
 * @fileoverview Rule to enforce that variable names for imported images and sounds have the conventional suffix.
 * "Sound" for sound imports and "Image" for images.
 *
 * The PhET team decided this was not required for strings.
 *
 * TODO: See https://github.com/phetsims/chipper/issues/1060 - We aren't sure if images should have this convention
 * yet, that is to be discussed at an upcoming developer meeting.
 * See https://github.com/phetsims/chipper/issues/1060#issuecomment-877475545 in particular. Code related to
 * checking images import variable names has been commented out for now until we decide.
 *
 * @author Jesse Greenberg (PhET Interactive Simulations)
 * @copyright 2021 University of Colorado Boulder
 */

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function( context ) {

  const soundSourceFileSuffixes = [ 'mp3.js', 'wav.js' ];
  // const imageSourceFileSuffixes = [ 'png.js', 'jpg.js', 'cur.js' ];

  return {
    ImportDeclaration: node => {
      if ( node.source && node.source.value ) {

        // determine if the import source has an image or sound file suffix
        const containsSoundSuffix = soundSourceFileSuffixes.some( suffix => {
          return node.source.value.endsWith( suffix );
        } );

        // const containsImageSuffix = imageSourceFileSuffixes.some( suffix => {
        //   return node.source.value.endsWith( suffix );
        // } );

        // if ( containsSoundSuffix || containsImageSuffix ) {
        if ( containsSoundSuffix ) {
          // const requiredVariableSuffix = containsSoundSuffix ? 'Sound' : 'Image';
          const requiredVariableSuffix = 'Sound';

          // the Specifier (AST node containing the variable name) must include
          // the right suffix
          node.specifiers.forEach( specifier => {
            if ( !specifier.local.name.endsWith( requiredVariableSuffix ) ) {
              context.report( {
                node: node,

                // report the loc of the variable so it gets highlighed by the IDE
                loc: specifier.local.loc,
                message: `Import variable missing ${requiredVariableSuffix} suffix.`
              } );
            }
          } );
        }
      }
    }
  };
};

module.exports.schema = [
  // JSON Schema for rule options goes here
];<|endoftext|>