• Jump To … +
    can.construct.proxy.js can.construct.super.js can.control.plugin.js can.dojo.js can.fixture.js can.jquery-all.js can.jquery.js can.model.queue.js can.mootools.js can.observe.attributes.js can.observe.backup.js can.observe.delegate.js can.observe.setter.js can.observe.validations.js can.view.modifiers.js can.view.mustache.js can.yui.js can.zepto.js
  • can.fixture.js

  • ¶
    /*!
    * CanJS - 1.1.5 (2013-03-27)
    * http://canjs.us/
    * Copyright (c) 2013 Bitovi
    * Licensed MIT
    */
    (function (can, window, undefined) {
  • ¶

    can/util/object/object.js

        var isArray = can.isArray,
  • ¶

    essentially returns an object that has all the must have comparisons ... must haves, do not return true when provided undefined

            cleanSet = function (obj, compares) {
                var copy = can.extend({}, obj);
                for (var prop in copy) {
                    var compare = compares[prop] === undefined ? compares["*"] : compares[prop];
                    if (same(copy[prop], undefined, compare)) {
                        delete copy[prop]
                    }
                }
                return copy;
            },
            propCount = function (obj) {
                var count = 0;
                for (var prop in obj) count++;
                return count;
            };
    
        can.Object = {};
    
        var same = can.Object.same = function (a, b, compares, aParent, bParent, deep) {
            var aType = typeof a,
                aArray = isArray(a),
                comparesType = typeof compares,
                compare;
    
            if (comparesType == 'string' || compares === null) {
                compares = compareMethods[compares];
                comparesType = 'function'
            }
            if (comparesType == 'function') {
                return compares(a, b, aParent, bParent)
            }
            compares = compares || {};
    
            if (a instanceof Date) {
                return a === b;
            }
            if (deep === -1) {
                return aType === 'object' || a === b;
            }
            if (aType !== typeof b || aArray !== isArray(b)) {
                return false;
            }
            if (a === b) {
                return true;
            }
            if (aArray) {
                if (a.length !== b.length) {
                    return false;
                }
                for (var i = 0; i < a.length; i++) {
                    compare = compares[i] === undefined ? compares["*"] : compares[i]
                    if (!same(a[i], b[i], a, b, compare)) {
                        return false;
                    }
                };
                return true;
            } else if (aType === "object" || aType === 'function') {
                var bCopy = can.extend({}, b);
                for (var prop in a) {
                    compare = compares[prop] === undefined ? compares["*"] : compares[prop];
                    if (!same(a[prop], b[prop], compare, a, b, deep === false ? -1 : undefined)) {
                        return false;
                    }
                    delete bCopy[prop];
                }
  • ¶

    go through bCopy props ... if there is no compare .. return false

                for (prop in bCopy) {
                    if (compares[prop] === undefined || !same(undefined, b[prop], compares[prop], a, b, deep === false ? -1 : undefined)) {
                        return false;
                    }
                }
                return true;
            }
            return false;
        };
    
        can.Object.subsets = function (checkSet, sets, compares) {
            var len = sets.length,
                subsets = [],
                checkPropCount = propCount(checkSet),
                setLength;
    
            for (var i = 0; i < len; i++) {
  • ¶

    check this subset

                var set = sets[i];
                if (can.Object.subset(checkSet, set, compares)) {
                    subsets.push(set)
                }
            }
            return subsets;
        };
    
        can.Object.subset = function (subset, set, compares) {
  • ¶

    go through set {type: 'folder'} and make sure every property is in subset {type: 'folder', parentId :5} then make sure that set has fewer properties make sure we are only checking 'important' properties in subset (ones that have to have a value)

            var setPropCount = 0,
                compares = compares || {};
    
            for (var prop in set) {
    
                if (!same(subset[prop], set[prop], compares[prop], subset, set)) {
                    return false;
                }
            }
            return true;
        }
    
        var compareMethods = {
            "null": function () {
                return true;
            },
            i: function (a, b) {
                return ("" + a).toLowerCase() == ("" + b).toLowerCase()
            }
        }
  • ¶

    can/util/fixture/fixture.js

    Get the URL from old Steal root, new Steal config or can.fixture.rootUrl

        var getUrl = function (url) {
            if (typeof steal !== 'undefined') {
                if (can.isFunction(steal.config)) {
                    return steal.config().root.mapJoin(url).toString();
                }
                return steal.root.join(url).toString();
            }
            return (can.fixture.rootUrl || '') + url;
        }
    
        var updateSettings = function (settings, originalOptions) {
            if (!can.fixture.on) {
                return;
            }
  • ¶

    simple wrapper for logging

            var _logger = function (type, arr) {
                if (console.log.apply) {
                    Function.prototype.call.apply(console[type], [console].concat(arr));
  • ¶

    console[type].apply(console, arr)

                } else {
                    console[type](arr)
                }
            },
                log = function () {
                    if (window.console && console.log) {
                        Array.prototype.unshift.call(arguments, 'fixture INFO:');
                        _logger("log", Array.prototype.slice.call(arguments));
                    }
                    else if (window.opera && window.opera.postError) {
                        opera.postError("fixture INFO: " + Array.prototype.join.call(arguments, ','));
                    }
                }
  • ¶

    We always need the type which can also be called method, default to GET

                settings.type = settings.type || settings.method || 'GET';
  • ¶

    add the fixture option if programmed in

            var data = overwrite(settings);
  • ¶

    if we don't have a fixture, do nothing

            if (!settings.fixture) {
                if (window.location.protocol === "file:") {
                    log("ajax request to " + settings.url + ", no fixture found");
                }
                return;
            }
  • ¶

    if referencing something else, update the fixture option

            if (typeof settings.fixture === "string" && can.fixture[settings.fixture]) {
                settings.fixture = can.fixture[settings.fixture];
            }
  • ¶

    if a string, we just point to the right url

            if (typeof settings.fixture == "string") {
                var url = settings.fixture;
    
                if (/^\/\//.test(url)) {
  • ¶

    this lets us use rootUrl w/o having steal...

                    url = getUrl(settings.fixture.substr(2));
                }
    
                if (data) {
  • ¶

    Template static fixture URLs

                    url = can.sub(url, data);
                }
    
                delete settings.fixture;
    
    
    
                settings.url = url;
                settings.data = null;
                settings.type = "GET";
                if (!settings.error) {
                    settings.error = function (xhr, error, message) {
                        throw "fixtures.js Error " + error + " " + message;
                    };
                }
            }
            else {
  • ¶

    it's a function ... add the fixture datatype so our fixture transport handles it TODO: make everything go here for timing and other fun stuff add to settings data from fixture ...

                settings.dataTypes && settings.dataTypes.splice(0, 0, "fixture");
    
                if (data && originalOptions) {
                    can.extend(originalOptions.data, data)
                }
            }
        },
  • ¶

    A helper function that takes what's called with response and moves some common args around to make it easier to call

            extractResponse = function (status, statusText, responses, headers) {
  • ¶

    if we get response(RESPONSES, HEADERS)

                if (typeof status != "number") {
                    headers = statusText;
                    responses = status;
                    statusText = "success"
                    status = 200;
                }
  • ¶

    if we get response(200, RESPONSES, HEADERS)

                if (typeof statusText != "string") {
                    headers = responses;
                    responses = statusText;
                    statusText = "success";
                }
                if (status >= 400 && status <= 599) {
                    this.dataType = "text"
                }
                return [status, statusText, extractResponses(this, responses), headers];
            },
  • ¶

    If we get data instead of responses, make sure we provide a response type that matches the first datatype (typically json)

            extractResponses = function (settings, responses) {
                var next = settings.dataTypes ? settings.dataTypes[0] : (settings.dataType || 'json');
                if (!responses || !responses[next]) {
                    var tmp = {}
                    tmp[next] = responses;
                    responses = tmp;
                }
                return responses;
            };
  • ¶

    used to check urls check if jQuery

        if (can.ajaxPrefilter && can.ajaxTransport) {
  • ¶

    the pre-filter needs to re-route the url

            can.ajaxPrefilter(updateSettings);
    
            can.ajaxTransport("fixture", function (s, original) {
  • ¶

    remove the fixture from the datatype

                s.dataTypes.shift();
  • ¶

    we'll return the result of the next data type

                var timeout, stopped = false;
    
                return {
                    send: function (headers, callback) {
  • ¶

    we'll immediately wait the delay time for all fixtures

                        timeout = setTimeout(function () {
  • ¶

    if the user wants to call success on their own, we allow it ...

                            var success = function () {
                                if (stopped === false) {
                                    callback.apply(null, extractResponse.apply(s, arguments));
                                }
                            },
  • ¶

    get the result form the fixture

                                result = s.fixture(original, success, headers, s);
                            if (result !== undefined) {
  • ¶

    make sure the result has the right dataType

                                callback(200, "success", extractResponses(s, result), {});
                            }
                        }, can.fixture.delay);
                    },
                    abort: function () {
                        stopped = true;
                        clearTimeout(timeout)
                    }
                };
            });
        } else {
            var AJAX = can.ajax;
            can.ajax = function (settings) {
                updateSettings(settings, settings);
                if (settings.fixture) {
                    var timeout, d = new can.Deferred(),
                        stopped = false;
  • ¶

    TODO this should work with response

                    d.getResponseHeader = function () {}
  • ¶

    call success and fail

                    d.then(settings.success, settings.fail);
  • ¶

    abort should stop the timeout and calling success

                    d.abort = function () {
                        clearTimeout(timeout);
                        stopped = true;
                        d.reject(d)
                    }
  • ¶

    set a timeout that simulates making a request ....

                    timeout = setTimeout(function () {
  • ¶

    if the user wants to call success on their own, we allow it ...

                        var success = function () {
                            var response = extractResponse.apply(settings, arguments),
                                status = response[0];
    
                            if ((status >= 200 && status < 300 || status === 304) && stopped === false) {
                                d.resolve(response[2][settings.dataType])
                            } else {
  • ¶

    TODO probably resolve better

                                d.reject(d, 'error', response[1]);
                            }
                        },
  • ¶

    get the result form the fixture

                            result = settings.fixture(settings, success, settings.headers, settings);
                        if (result !== undefined) {
                            d.resolve(result)
                        }
                    }, can.fixture.delay);
    
                    return d;
                } else {
                    return AJAX(settings);
                }
            }
        }
    
        var typeTest = /^(script|json|text|jsonp)$/,
  • ¶

    a list of 'overwrite' settings object

            overwrites = [],
  • ¶

    returns the index of an overwrite function

            find = function (settings, exact) {
                for (var i = 0; i < overwrites.length; i++) {
                    if ($fixture._similar(settings, overwrites[i], exact)) {
                        return i;
                    }
                }
                return -1;
            },
  • ¶

    overwrites the settings fixture if an overwrite matches

            overwrite = function (settings) {
                var index = find(settings);
                if (index > -1) {
                    settings.fixture = overwrites[index].fixture;
                    return $fixture._getData(overwrites[index].url, settings.url)
                }
    
            },
  • ¶

    Makes an attempt to guess where the id is at in the url and returns it.

            getId = function (settings) {
                var id = settings.data.id;
    
                if (id === undefined && typeof settings.data === "number") {
                    id = settings.data;
                }
    
    
    
                if (id === undefined) {
                    settings.url.replace(/\/(\d+)(\/|$|\.)/g, function (all, num) {
                        id = num;
                    });
                }
    
                if (id === undefined) {
                    id = settings.url.replace(/\/(\w+)(\/|$|\.)/g, function (all, num) {
                        if (num != 'update') {
                            id = num;
                        }
                    })
                }
    
                if (id === undefined) { // if still not set, guess a random number
                    id = Math.round(Math.random() * 1000)
                }
    
                return id;
            };
    
        var $fixture = can.fixture = function (settings, fixture) {
  • ¶

    if we provide a fixture ...

            if (fixture !== undefined) {
                if (typeof settings == 'string') {
  • ¶

    handle url strings

                    var matches = settings.match(/(GET|POST|PUT|DELETE) (.+)/i);
                    if (!matches) {
                        settings = {
                            url: settings
                        };
                    } else {
                        settings = {
                            url: matches[2],
                            type: matches[1]
                        };
                    }
    
                }
  • ¶

    handle removing. An exact match if fixture was provided, otherwise, anything similar

                var index = find(settings, !! fixture);
                if (index > -1) {
                    overwrites.splice(index, 1)
                }
                if (fixture == null) {
                    return
                }
                settings.fixture = fixture;
                overwrites.push(settings)
            } else {
                can.each(settings, function (fixture, url) {
                    $fixture(url, fixture);
                })
            }
        };
        var replacer = can.replacer;
    
        can.extend(can.fixture, {
  • ¶

    given ajax settings, find an overwrite

            _similar: function (settings, overwrite, exact) {
                if (exact) {
                    return can.Object.same(settings, overwrite, {
                        fixture: null
                    })
                } else {
                    return can.Object.subset(settings, overwrite, can.fixture._compare)
                }
            },
            _compare: {
                url: function (a, b) {
                    return !!$fixture._getData(b, a)
                },
                fixture: null,
                type: "i"
            },
  • ¶

    gets data from a url like "/todo/{id}" given "todo/5"

            _getData: function (fixtureUrl, url) {
                var order = [],
                    fixtureUrlAdjusted = fixtureUrl.replace('.', '\\.').replace('?', '\\?'),
                    res = new RegExp(fixtureUrlAdjusted.replace(replacer, function (whole, part) {
                        order.push(part)
                        return "([^\/]+)"
                    }) + "$").exec(url),
                    data = {};
    
                if (!res) {
                    return null;
                }
                res.shift();
                can.each(order, function (name) {
                    data[name] = res.shift()
                })
                return data;
            },
    
            store: function (types, count, make, filter) {
    
                var items = [],
  • ¶

    TODO: change this to a hash

                    findOne = function (id) {
                        for (var i = 0; i < items.length; i++) {
                            if (id == items[i].id) {
                                return items[i];
                            }
                        }
                    },
                    methods = {};
    
                if (typeof types === "string") {
                    types = [types + "s", types]
                } else if (!can.isArray(types)) {
                    filter = make;
                    make = count;
                    count = types;
                }
  • ¶

    make all items

                can.extend(methods, {
    
                    findAll: function (request) {
  • ¶

    copy array of items

                        var retArr = items.slice(0);
                        request.data = request.data || {};
  • ¶

    sort using order order looks like ["age ASC","gender DESC"]

                        can.each((request.data.order || []).slice(0).reverse(), function (name) {
                            var split = name.split(" ");
                            retArr = retArr.sort(function (a, b) {
                                if (split[1].toUpperCase() !== "ASC") {
                                    if (a[split[0]] < b[split[0]]) {
                                        return 1;
                                    } else if (a[split[0]] == b[split[0]]) {
                                        return 0
                                    } else {
                                        return -1;
                                    }
                                }
                                else {
                                    if (a[split[0]] < b[split[0]]) {
                                        return -1;
                                    } else if (a[split[0]] == b[split[0]]) {
                                        return 0
                                    } else {
                                        return 1;
                                    }
                                }
                            });
                        });
  • ¶

    group is just like a sort

                        can.each((request.data.group || []).slice(0).reverse(), function (name) {
                            var split = name.split(" ");
                            retArr = retArr.sort(function (a, b) {
                                return a[split[0]] > b[split[0]];
                            });
                        });
    
                        var offset = parseInt(request.data.offset, 10) || 0,
                            limit = parseInt(request.data.limit, 10) || (items.length - offset),
                            i = 0;
  • ¶

    filter results if someone added an attr like parentId

                        for (var param in request.data) {
                            i = 0;
                            if (request.data[param] !== undefined && // don't do this if the value of the param is null (ignore it)
                            (param.indexOf("Id") != -1 || param.indexOf("_id") != -1)) {
                                while (i < retArr.length) {
                                    if (request.data[param] != retArr[i][param]) {
                                        retArr.splice(i, 1);
                                    } else {
                                        i++;
                                    }
                                }
                            }
                        }
    
                        if (filter) {
                            i = 0;
                            while (i < retArr.length) {
                                if (!filter(retArr[i], request)) {
                                    retArr.splice(i, 1);
                                } else {
                                    i++;
                                }
                            }
                        }
  • ¶

    return data spliced with limit and offset

                        return {
                            "count": retArr.length,
                            "limit": request.data.limit,
                            "offset": request.data.offset,
                            "data": retArr.slice(offset, offset + limit)
                        };
                    },
    
                    findOne: function (request, response) {
                        var item = findOne(getId(request));
                        response(item ? item : undefined);
                    },
    
                    update: function (request, response) {
                        var id = getId(request);
  • ¶

    TODO: make it work with non-linear ids ..

                        can.extend(findOne(id), request.data);
                        response({
                            id: getId(request)
                        }, {
                            location: request.url || "/" + getId(request)
                        });
                    },
    
                    destroy: function (request) {
                        var id = getId(request);
                        for (var i = 0; i < items.length; i++) {
                            if (items[i].id == id) {
                                items.splice(i, 1);
                                break;
                            }
                        }
  • ¶

    TODO: make it work with non-linear ids ..

                        can.extend(findOne(id) || {}, request.data);
                        return {};
                    },
    
                    create: function (settings, response) {
                        var item = make(items.length, items);
    
                        can.extend(item, settings.data);
    
                        if (!item.id) {
                            item.id = items.length;
                        }
    
                        items.push(item);
                        var id = item.id || parseInt(Math.random() * 100000, 10);
                        response({
                            id: id
                        }, {
                            location: settings.url + "/" + id
                        })
                    }
                });
    
                var reset = function () {
                    items = [];
                    for (var i = 0; i < (count); i++) {
  • ¶

    call back provided make

                        var item = make(i, items);
    
                        if (!item.id) {
                            item.id = i;
                        }
                        items.push(item);
                    }
                    if (can.isArray(types)) {
                        can.fixture["~" + types[0]] = items;
                        can.fixture["-" + types[0]] = methods.findAll;
                        can.fixture["-" + types[1]] = methods.findOne;
                        can.fixture["-" + types[1] + "Update"] = methods.update;
                        can.fixture["-" + types[1] + "Destroy"] = methods.destroy;
                        can.fixture["-" + types[1] + "Create"] = methods.create;
                    }
                }
                reset()
  • ¶

    if we have types given add them to can.fixture

                return can.extend({
                    getId: getId,
    
                    find: function (settings) {
                        return findOne(getId(settings));
                    },
    
                    reset: reset
                }, methods);
            },
    
            rand: function (arr, min, max) {
                if (typeof arr == 'number') {
                    if (typeof min == 'number') {
                        return arr + Math.floor(Math.random() * (min - arr));
                    } else {
                        return Math.floor(Math.random() * arr);
                    }
    
                }
                var rand = arguments.callee;
  • ¶

    get a random set

                if (min === undefined) {
                    return rand(arr, rand(arr.length + 1))
                }
  • ¶

    get a random selection of arr

                var res = [];
                arr = arr.slice(0);
  • ¶

    set max

                if (!max) {
                    max = min;
                }
  • ¶

    random max

                max = min + Math.round(rand(max - min))
                for (var i = 0; i < max; i++) {
                    res.push(arr.splice(rand(arr.length), 1)[0])
                }
                return res;
            },
    
            xhr: function (xhr) {
                return can.extend({}, {
                    abort: can.noop,
                    getAllResponseHeaders: function () {
                        return "";
                    },
                    getResponseHeader: function () {
                        return "";
                    },
                    open: can.noop,
                    overrideMimeType: can.noop,
                    readyState: 4,
                    responseText: "",
                    responseXML: null,
                    send: can.noop,
                    setRequestHeader: can.noop,
                    status: 200,
                    statusText: "OK"
                }, xhr);
            },
    
            on: true
        });
    
        can.fixture.delay = 200;
    
    
        can.fixture.rootUrl = getUrl('');
    
        can.fixture["-handleFunction"] = function (settings) {
            if (typeof settings.fixture === "string" && can.fixture[settings.fixture]) {
                settings.fixture = can.fixture[settings.fixture];
            }
            if (typeof settings.fixture == "function") {
                setTimeout(function () {
                    if (settings.success) {
                        settings.success.apply(null, settings.fixture(settings, "success"));
                    }
                    if (settings.complete) {
                        settings.complete.apply(null, settings.fixture(settings, "complete"));
                    }
                }, can.fixture.delay);
                return true;
            }
            return false;
        };
  • ¶

    Expose this for fixture debugging

        can.fixture.overwrites = overwrites;
        can.fixture.make = can.fixture.store;
    
    })(can, this);