<commit_before>import {
    call,
    put,
    select,
    fork,
    takeLatest,
    takeEvery,
} from 'redux-saga/effects';
import { api } from '@steemit/steem-js';
import { loadFollows, fetchFollowCount } from 'app/redux/FollowSaga';
import { getContent } from 'app/redux/SagaShared';
import * as globalActions from './GlobalReducer';
import * as appActions from './AppReducer';
import constants from './constants';
import { fromJS, Map, Set } from 'immutable';
import { getStateAsync, callBridge } from 'app/utils/steemApi';

const REQUEST_DATA = 'fetchDataSaga/REQUEST_DATA';
const GET_CONTENT = 'fetchDataSaga/GET_CONTENT';
const FETCH_STATE = 'fetchDataSaga/FETCH_STATE';

export const fetchDataWatches = [
    takeLatest(REQUEST_DATA, fetchData),
    takeEvery(GET_CONTENT, getContentCaller),
    takeLatest('@@router/LOCATION_CHANGE', fetchState),
    takeLatest(FETCH_STATE, fetchState),
    takeEvery('global/FETCH_JSON', fetchJson),
];

export function* getContentCaller(action) {
    yield getContent(action.payload);
}

let is_initial_state = true;
export function* fetchState(location_change_action) {
    const { pathname } = location_change_action.payload;
    const m = pathname.match(/^\/@([a-z0-9\.-]+)/);
    if (m && m.length === 2) {
        const username = m[1];
        yield fork(fetchFollowCount, username);
        yield fork(loadFollows, 'getFollowersAsync', username, 'blog');
        yield fork(loadFollows, 'getFollowingAsync', username, 'blog');
    }

    // `ignore_fetch` case should only trigger on initial page load. No need to call
    // fetchState immediately after loading fresh state from the server. Details: #593
    const server_location = yield select(state =>
        state.offchain.get('server_location')
    );
    const ignore_fetch = pathname === server_location && is_initial_state;
    is_initial_state = false;
    if (ignore_fetch) {
        return;
    }

    const url = pathname;

    yield put(appActions.fetchDataBegin());
    try {
        const state = yield call(getStateAsync, url, 'maitland');
        yield put(globalActions.receiveState(state));
        yield call(syncSpecialPosts);
    } catch (error) {
        console.error('~~ Saga fetchState error ~~>', url, error);
        yield put(appActions.steemApiError(error.message));
    }

    yield put(appActions.fetchDataEnd());
}

function* syncSpecialPosts() {
    // Bail if we're rendering serverside since there is no localStorage
    if (!process.env.BROWSER) return null;

    // Get special posts from the store.
    const specialPosts = yield select(state =>
        state.offchain.get('special_posts')
    );

    // Mark seen featured posts.
    const seenFeaturedPosts = specialPosts.get('featured_posts').map(post => {
        const id = `${post.get('author')}/${post.get('permlink')}`;
        return post.set(
            'seen',
            localStorage.getItem(`featured-post-seen:${id}`) === 'true'
        );
    });

    // Mark seen promoted posts.
    const seenPromotedPosts = specialPosts.get('promoted_posts').map(post => {
        const id = `${post.get('author')}/${post.get('permlink')}`;
        return post.set(
            'seen',
            localStorage.getItem(`promoted-post-seen:${id}`) === 'true'
        );
    });

    // Look up seen post URLs.
    yield put(
        globalActions.syncSpecialPosts({
            featuredPosts: seenFeaturedPosts,
            promotedPosts: seenPromotedPosts,
        })
    );

    // Mark all featured posts as seen.
    specialPosts.get('featured_posts').forEach(post => {
        const id = `${post.get('author')}/${post.get('permlink')}`;
        localStorage.setItem(`featured-post-seen:${id}`, 'true');
    });

    // Mark all promoted posts as seen.
    specialPosts.get('promoted_posts').forEach(post => {
        const id = `${post.get('author')}/${post.get('permlink')}`;
        localStorage.setItem(`promoted-post-seen:${id}`, 'true');
    });
}

/**
 * Request account data for a set of usernames.
 *
 * @todo batch the put()s
 *
 * @param {Iterable} usernames
 */
function* getAccounts(usernames) {
    const accounts = yield call([api, api.getAccountsAsync], usernames);
    yield put(globalActions.receiveAccounts({ accounts }));
}

export function* fetchData(action) {
    const { order, author, permlink, accountname, postFilter } = action.payload;
    let { category } = action.payload;
    if (!category) category = '';
    category = category.toLowerCase();

    yield put(globalActions.fetchingData({ order, category }));
    let call_name, args;
    if (
        order === 'trending' ||
        order == 'hot' ||
        order == 'promoted' ||
        order == 'payout' ||
        order == 'created'
    ) {
        call_name = 'get_ranked_posts';
        args = {
            sort: order,
            tag: category,
            limit: constants.FETCH_DATA_BATCH_SIZE,
            start_author: author,
            start_permlink: permlink,
        };
    } else if (
        order === 'by_replies' ||
        order == 'by_feed' ||
        order == 'by_author' ||
        order == 'by_comments'
    ) {
        const sort = {
            by_replies: 'replies',
            by_feed: 'feed',
            by_author: 'blog',
            by_comments: 'comments',
        }[order];
        call_name = 'get_account_posts';
        args = {
            sort,
            account: accountname,
            limit: constants.FETCH_DATA_BATCH_SIZE,
            start_author: author,
            start_permlink: permlink,
        };
    }

    yield put(appActions.fetchDataBegin());
    try {
        const firstPermlink = permlink;
        let fetched = 0;
        let endOfData = false;
        let fetchLimitReached = false;
        let fetchDone = false;
        let batch = 0;
        while (!fetchDone) {
            const data = yield call(callBridge, call_name, args);

            endOfData = data.length < constants.FETCH_DATA_BATCH_SIZE;

            batch++;
            fetchLimitReached = batch >= constants.MAX_BATCHES;

            if (data.length > 0) {
                const lastValue = data[data.length - 1];
                args.start_author = lastValue.author;
                args.start_permlink = lastValue.permlink;
            }

            // Still return all data but only count ones matching the filter.
            // Rely on UI to actually hide the posts.
            fetched += postFilter
                ? data.filter(postFilter).length
                : data.length;

            fetchDone =
                endOfData ||
                fetchLimitReached ||
                fetched >= constants.FETCH_DATA_BATCH_SIZE;

            yield put(
                globalActions.receiveData({
                    data,
                    order,
                    category,
                    author,
                    firstPermlink,
                    accountname,
                    fetching: !fetchDone,
                    endOfData,
                })
            );
        }
    } catch (error) {
        console.error('~~ Saga fetchData error ~~>', call_name, args, error);
        yield put(appActions.steemApiError(error.message));
    }
    yield put(appActions.fetchDataEnd());
}

/**
    @arg {string} id unique key for result global['fetchJson_' + id]
    @arg {string} url
    @arg {object} body (for JSON.stringify)
*/
function* fetchJson({
    payload: { id, url, body, successCallback, skipLoading = false },
}) {
    try {
        const payload = {
            method: body ? 'POST' : 'GET',
            headers: {
                Accept: 'application/json',
                'Content-Type': 'application/json',
            },
            body: body ? JSON.stringify(body) : undefined,
        };
        let result = yield skipLoading
            ? fetch(url, payload)
            : call(fetch, url, payload);
        result = yield result.json();
        if (successCallback) result = successCallback(result);
        yield put(globalActions.fetchJsonResult({ id, result }));
    } catch (error) {
        console.error('fetchJson', error);
        yield put(globalActions.fetchJsonResult({ id, error }));
    }
}

// Action creators
export const actions = {
    requestData: payload => ({
        type: REQUEST_DATA,
        payload,
    }),

    getContent: payload => ({
        type: GET_CONTENT,
        payload,
    }),

    fetchState: payload => ({
        type: FETCH_STATE,
        payload,
    }),
};
<commit_msg>add username to getStateAsync in FetchDataSaga<commit_after>import {
    call,
    put,
    select,
    fork,
    takeLatest,
    takeEvery,
} from 'redux-saga/effects';
import { api } from '@steemit/steem-js';
import { loadFollows, fetchFollowCount } from 'app/redux/FollowSaga';
import { getContent } from 'app/redux/SagaShared';
import * as globalActions from './GlobalReducer';
import * as appActions from './AppReducer';
import constants from './constants';
import { fromJS, Map, Set } from 'immutable';
import { getStateAsync, callBridge } from 'app/utils/steemApi';

const REQUEST_DATA = 'fetchDataSaga/REQUEST_DATA';
const GET_CONTENT = 'fetchDataSaga/GET_CONTENT';
const FETCH_STATE = 'fetchDataSaga/FETCH_STATE';

export const fetchDataWatches = [
    takeLatest(REQUEST_DATA, fetchData),
    takeEvery(GET_CONTENT, getContentCaller),
    takeLatest('@@router/LOCATION_CHANGE', fetchState),
    takeLatest(FETCH_STATE, fetchState),
    takeEvery('global/FETCH_JSON', fetchJson),
];

export function* getContentCaller(action) {
    yield getContent(action.payload);
}

let is_initial_state = true;
export function* fetchState(location_change_action) {
    const { pathname } = location_change_action.payload;
    const m = pathname.match(/^\/@([a-z0-9\.-]+)/);
    if (m && m.length === 2) {
        const username = m[1];
        yield fork(fetchFollowCount, username);
        yield fork(loadFollows, 'getFollowersAsync', username, 'blog');
        yield fork(loadFollows, 'getFollowingAsync', username, 'blog');
    }

    // `ignore_fetch` case should only trigger on initial page load. No need to call
    // fetchState immediately after loading fresh state from the server. Details: #593
    const server_location = yield select(state =>
        state.offchain.get('server_location')
    );
    const ignore_fetch = pathname === server_location && is_initial_state;
    is_initial_state = false;
    if (ignore_fetch) {
        return;
    }

    const url = pathname;

    yield put(appActions.fetchDataBegin());
    try {
        let username = null;
        if (process.env.BROWSER) {
            [username] = yield select(state => [
                state.user.getIn(['current', 'username']),
            ]);
        }
        const state = yield call(getStateAsync, url, username);
        yield put(globalActions.receiveState(state));
        yield call(syncSpecialPosts);
    } catch (error) {
        console.error('~~ Saga fetchState error ~~>', url, error);
        yield put(appActions.steemApiError(error.message));
    }

    yield put(appActions.fetchDataEnd());
}

function* syncSpecialPosts() {
    // Bail if we're rendering serverside since there is no localStorage
    if (!process.env.BROWSER) return null;

    // Get special posts from the store.
    const specialPosts = yield select(state =>
        state.offchain.get('special_posts')
    );

    // Mark seen featured posts.
    const seenFeaturedPosts = specialPosts.get('featured_posts').map(post => {
        const id = `${post.get('author')}/${post.get('permlink')}`;
        return post.set(
            'seen',
            localStorage.getItem(`featured-post-seen:${id}`) === 'true'
        );
    });

    // Mark seen promoted posts.
    const seenPromotedPosts = specialPosts.get('promoted_posts').map(post => {
        const id = `${post.get('author')}/${post.get('permlink')}`;
        return post.set(
            'seen',
            localStorage.getItem(`promoted-post-seen:${id}`) === 'true'
        );
    });

    // Look up seen post URLs.
    yield put(
        globalActions.syncSpecialPosts({
            featuredPosts: seenFeaturedPosts,
            promotedPosts: seenPromotedPosts,
        })
    );

    // Mark all featured posts as seen.
    specialPosts.get('featured_posts').forEach(post => {
        const id = `${post.get('author')}/${post.get('permlink')}`;
        localStorage.setItem(`featured-post-seen:${id}`, 'true');
    });

    // Mark all promoted posts as seen.
    specialPosts.get('promoted_posts').forEach(post => {
        const id = `${post.get('author')}/${post.get('permlink')}`;
        localStorage.setItem(`promoted-post-seen:${id}`, 'true');
    });
}

/**
 * Request account data for a set of usernames.
 *
 * @todo batch the put()s
 *
 * @param {Iterable} usernames
 */
function* getAccounts(usernames) {
    const accounts = yield call([api, api.getAccountsAsync], usernames);
    yield put(globalActions.receiveAccounts({ accounts }));
}

export function* fetchData(action) {
    const { order, author, permlink, accountname, postFilter } = action.payload;
    let { category } = action.payload;
    if (!category) category = '';
    category = category.toLowerCase();

    yield put(globalActions.fetchingData({ order, category }));
    let call_name, args;
    if (
        order === 'trending' ||
        order == 'hot' ||
        order == 'promoted' ||
        order == 'payout' ||
        order == 'created'
    ) {
        call_name = 'get_ranked_posts';
        args = {
            sort: order,
            tag: category,
            limit: constants.FETCH_DATA_BATCH_SIZE,
            start_author: author,
            start_permlink: permlink,
        };
    } else if (
        order === 'by_replies' ||
        order == 'by_feed' ||
        order == 'by_author' ||
        order == 'by_comments'
    ) {
        const sort = {
            by_replies: 'replies',
            by_feed: 'feed',
            by_author: 'blog',
            by_comments: 'comments',
        }[order];
        call_name = 'get_account_posts';
        args = {
            sort,
            account: accountname,
            limit: constants.FETCH_DATA_BATCH_SIZE,
            start_author: author,
            start_permlink: permlink,
        };
    }

    yield put(appActions.fetchDataBegin());
    try {
        const firstPermlink = permlink;
        let fetched = 0;
        let endOfData = false;
        let fetchLimitReached = false;
        let fetchDone = false;
        let batch = 0;
        while (!fetchDone) {
            const data = yield call(callBridge, call_name, args);

            endOfData = data.length < constants.FETCH_DATA_BATCH_SIZE;

            batch++;
            fetchLimitReached = batch >= constants.MAX_BATCHES;

            if (data.length > 0) {
                const lastValue = data[data.length - 1];
                args.start_author = lastValue.author;
                args.start_permlink = lastValue.permlink;
            }

            // Still return all data but only count ones matching the filter.
            // Rely on UI to actually hide the posts.
            fetched += postFilter
                ? data.filter(postFilter).length
                : data.length;

            fetchDone =
                endOfData ||
                fetchLimitReached ||
                fetched >= constants.FETCH_DATA_BATCH_SIZE;

            yield put(
                globalActions.receiveData({
                    data,
                    order,
                    category,
                    author,
                    firstPermlink,
                    accountname,
                    fetching: !fetchDone,
                    endOfData,
                })
            );
        }
    } catch (error) {
        console.error('~~ Saga fetchData error ~~>', call_name, args, error);
        yield put(appActions.steemApiError(error.message));
    }
    yield put(appActions.fetchDataEnd());
}

/**
    @arg {string} id unique key for result global['fetchJson_' + id]
    @arg {string} url
    @arg {object} body (for JSON.stringify)
*/
function* fetchJson({
    payload: { id, url, body, successCallback, skipLoading = false },
}) {
    try {
        const payload = {
            method: body ? 'POST' : 'GET',
            headers: {
                Accept: 'application/json',
                'Content-Type': 'application/json',
            },
            body: body ? JSON.stringify(body) : undefined,
        };
        let result = yield skipLoading
            ? fetch(url, payload)
            : call(fetch, url, payload);
        result = yield result.json();
        if (successCallback) result = successCallback(result);
        yield put(globalActions.fetchJsonResult({ id, result }));
    } catch (error) {
        console.error('fetchJson', error);
        yield put(globalActions.fetchJsonResult({ id, error }));
    }
}

// Action creators
export const actions = {
    requestData: payload => ({
        type: REQUEST_DATA,
        payload,
    }),

    getContent: payload => ({
        type: GET_CONTENT,
        payload,
    }),

    fetchState: payload => ({
        type: FETCH_STATE,
        payload,
    }),
};
<|endoftext|>


<commit_before>'use strict';

angular.module('csyywx')
	.service('CommonApi', function($http, md5, utils, HOST_URL) {
		var v = 'm.nonobank.com/msapi/'+ utils.getDate(),
				vMd5 = md5.createHash(v),
				headers = {'Authorization': vMd5,'Content-Type': 'application/x-www-form-urlencoded'};

		this.addFeedback = function(obj) {
			return $http({
				method: 'POST',
				url: HOST_URL + '/feedback/addFeedback',
				headers: headers,
				data: utils.param({
					sessionId: obj.sessionId,
					message: obj.message
				})
			}).success(function(data) {
			})
		};
	})<commit_msg>add bankBaseData api<commit_after>'use strict';

angular.module('csyywx')
	.service('CommonApi', function($http, md5, utils, HOST_URL) {
		var v = 'm.nonobank.com/msapi/'+ utils.getDate(),
				vMd5 = md5.createHash(v),
				headers = {'Authorization': vMd5,'Content-Type': 'application/x-www-form-urlencoded'};

		this.addFeedback = function(obj) {
			return $http({
				method: 'POST',
				url: HOST_URL + '/feedback/addFeedback',
				headers: headers,
				data: utils.param({
					sessionId: obj.sessionId,
					message: obj.message
				})
			}).success(function(data) {
			})
		};

		this.bankBaseData = function() {
			return $http({
				method: 'POST',
				url: HOST_URL + '/system/bankBaseData',
				headers: headers,
				data: null
			})
		}
	})<|endoftext|>


<commit_before>'use strict';

export default {
  extend,
};

/**
 * Extends the request objects
 *
 * @param {Object} ctx
 * @param {function} next
 * @return {Promise}
 */
function extend(ctx, next) {
  ctx.req.isAuthenticated = () => (ctx.session && ctx.session.identityDesk && ctx.session.identityDesk.user) ? true : false;
  ctx.isAuthenticated = ctx.req.isAuthenticated;

  ctx.req.isUnauthenticated = () => !ctx.req.isAuthenticated();
  ctx.isUnauthenticated = ctx.req.isUnauthenticated;

  // TODO implement req.login/req.logIn?

  ctx.req.logout = () => {
    ctx.session = null;
    if (ctx.res.redirect) {
      ctx.res.redirect('/'); // required for apps using `CoreFramework#expressify`, such as Express apps
    } else {
      ctx.redirect('/'); // required for Koa-like apps
    }
  };
  ctx.req.logOut = ctx.req.logout;
  ctx.logout = ctx.req.logout;
  ctx.logOut = ctx.req.logOut;

  return next();
}<commit_msg>Use custom session methods for extended requests<commit_after>'use strict';

export default {
  extend,
};

/**
 * Extends the request objects
 *
 * @param {Object} ctx
 * @param {function} next
 * @return {Promise}
 */
function extend(ctx, next) {
  ctx.req.isAuthenticated = () => (ctx.identityDesk.get('user')) ? true : false;
  ctx.isAuthenticated = ctx.req.isAuthenticated;

  ctx.req.isUnauthenticated = () => !ctx.req.isAuthenticated();
  ctx.isUnauthenticated = ctx.req.isUnauthenticated;

  // TODO implement req.login/req.logIn?

  ctx.req.logout = () => {
    ctx.session = null;
    if (ctx.res.redirect) {
      ctx.res.redirect('/'); // required for apps using `CoreFramework#expressify`, such as Express apps
    } else {
      ctx.redirect('/'); // required for Koa-like apps
    }
  };
  ctx.req.logOut = ctx.req.logout;
  ctx.logout = ctx.req.logout;
  ctx.logOut = ctx.req.logOut;

  return next();
}<|endoftext|>


<commit_before>/*!
 * backbone-hypermedia (https://github.com/linn/backbone.hypermedia
 * Licensed under MIT (https://github.com/linn/backbone.hypermedia/blob/master/LICENSE)
 */

(function (factory) {
    if (typeof exports === 'object') {
        module.exports = factory(require('backbone'), require('underscore'), require('jquery'));
    } else if (typeof define === 'function' && define.amd) {
        define(['backbone', 'underscore', 'jquery'], factory);
    }
} (function (Backbone, _, $) {
    Backbone.HypermediaModel = Backbone.Model.extend({
        follow: function (relsToFollow) {
            var promises = [];
            var self = this;

            if (this.links) {
                var keys = _.keys(this.links);

                for (var i = 0; i < keys.length; i++) {
                    var key = keys[i];
                    var context;
                    var links;

                    if (!relsToFollow || _.contains(relsToFollow, key)) {
                        if (key.split('.').length > 1) {
                            var item;
                            var attr = key.split('.')[0];
                            key = key.split('.')[1];
                            context = this.get(attr);

                            if (context instanceof Array) {
                                for (var a = 0; a < context.length; a++) {
                                    item = context[a];
                                    links = item.links;
                                    addPromises(promises, links, model(self.links[keys[i]]), item, key, options(self.links[keys[i]]));
                                }

                                continue;
                            } else if (context instanceof Backbone.Collection) {
                                for (var b = 0; b < context.length; b++) {
                                    item = context[b];
                                    links = item.get('links');
                                    addPromises(promises, links, model(self.links[keys[i]]), item, key, options(self.links[keys[i]]));
                                }
                            } else if (context instanceof Backbone.Model) {
                                links = context.get('links');
                            } else {
                                links = context.links;
                            }
                        } else {
                            context = this;
                            links = this.get('links');
                        }

                        addPromises(promises, links, model(this.links[keys[i]]), context, key, options(this.links[keys[i]]));
                    }
                }
            }

            return $.when.apply($, promises)
                .then(function () {
                    self.trigger('follow', self);
                });
        },

        fetch: function (options) {
            var self = this;

            return Backbone.Model.prototype.fetch.call(this, options)
                .then(function () {
                    return options ? self.follow(options.follow) : self.follow();
                });
        },

        sync: function (method, model, options) {
            if (this.mediaType) {
                options.accepts = { json: this.mediaType };

                if (method === 'create' || method === 'update' || method === 'patch') {
                    options.contentType = this.mediaType;
                }
            }

            return Backbone.sync(method, model, options);
        },

        toJSON: function () {
            var json = Backbone.Model.prototype.toJSON.apply(this, arguments);

            // use extend to create a deep copy, otherwise complex properties
            // are copied by reference, and any subsequent changes
            // are made against the model
            json = $.extend(true, {}, json);

            if (this.links) {
                var keys = _.keys(this.links);
                var self = this;

                _.each(keys, function (key) {
                    var context;
                    var jsonContext;

                    if (key.split('.').length > 1) {
                        var attr = key.split('.')[0];
                        key = key.split('.')[1];
                        context = self.get(attr);
                        jsonContext = json[attr];
                    } else {
                        context = self;
                        jsonContext = json;
                    }

                    var isArray = key.indexOf('[]', key.length - 2) !== -1;

                    if (isArray) {
                        key = key.substr(0, key.length - 2);
                    }

                    if (context && context[key]) {
                        if (isArray) {
                            jsonContext[key] = [];

                            for (var i = 0; i < context[key].length; i++) {
                                jsonContext[key].push(context[key][i].toJSON());
                            }
                        } else {
                            jsonContext[key] = context[key].toJSON();
                        }
                    }
                });
            }

            return json;
        }
    });

    Backbone.HypermediaCollection = Backbone.Collection.extend({
        fetch: function (options) {
            var self = this;

            return Backbone.Collection.prototype.fetch.call(this, options).then(function () {
                return $.when.apply($, self.chain()
                    .filter(function (model) { return model instanceof Backbone.HypermediaModel; })
                    .map(function (model) { return model.follow(); })
                    .value());
            })
            .then(function () {
                self.trigger('follow', self);
            });
        },

        sync: function (method, model, options) {
            if (this.mediaType) {
                options.accepts = { json: this.mediaType };
            }

            return Backbone.sync(method, model, options);
        }
    });

    var model = function (thing) {
        if (_.isFunction(thing)) {
            return thing;
        }
        if (thing) {
            return thing.model;
        }
    };

    var options = function (thing) {
        if (_.isFunction(thing)) {
            return {};
        }
        if (thing) {
            return thing.options;
        }
    };

    var addPromises = function (promises, links, model, context, key, options) {
        var isArray = key.indexOf('[]', key.length - 2) !== -1;

        if (isArray) {
            key = key.substr(0, key.length - 2);
        }

        var rels = _.filter(links, function (l) { return l.rel == key; });

        if (rels.length > 1 && !isArray) {
            throw new Error('Found more than one link with rel \'' + key + '\', but the link was not specified as allowing multiple values. To allow multiple values, suffix the link key with \'[]\'.');
        }

        for (var i = 0; i < rels.length; i++) {
            var related = new model(),
                    urlFactory = _.isFunction(related.urlFactory) ? related.urlFactory : _.identity;

            if (isArray) {
                if (!context[key]) {
                    context[key] = [];
                }

                context[key].push(related);
            } else {
                context[key] = related;
            }

            var fetchOptions = _.extend({}, options, { url: urlFactory.call(related, rels[i].href) });

            promises.push(related.fetch(fetchOptions));
        }
    };
}));
<commit_msg>heterogeneous collection fix<commit_after>/*!
 * backbone-hypermedia (https://github.com/linn/backbone.hypermedia
 * Licensed under MIT (https://github.com/linn/backbone.hypermedia/blob/master/LICENSE)
 */

(function (factory) {
    if (typeof exports === 'object') {
        module.exports = factory(require('backbone'), require('underscore'), require('jquery'));
    } else if (typeof define === 'function' && define.amd) {
        define(['backbone', 'underscore', 'jquery'], factory);
    }
} (function (Backbone, _, $) {
    Backbone.HypermediaModel = Backbone.Model.extend({
        follow: function (relsToFollow) {
            var promises = [];
            var self = this;

            if (this.links) {
                var keys = _.keys(this.links);

                for (var i = 0; i < keys.length; i++) {
                    var key = keys[i];
                    var context;
                    var links;

                    if (!relsToFollow || _.contains(relsToFollow, key)) {
                        if (key.split('.').length > 1) {
                            var item;
                            var attr = key.split('.')[0];
                            key = key.split('.')[1];
                            context = this.get(attr);

							// allows for following links on child objects within a hetereogenous collection
							// where some elements in the collection may have the child, and others may not
							if (!context) {
                                continue;
                            }

                            if (context instanceof Array) {
                                for (var a = 0; a < context.length; a++) {
                                    item = context[a];
                                    links = item.links;
                                    addPromises(promises, links, model(self.links[keys[i]]), item, key, options(self.links[keys[i]]));
                                }

                                continue;
                            } else if (context instanceof Backbone.Collection) {
                                for (var b = 0; b < context.length; b++) {
                                    item = context[b];
                                    links = item.get('links');
                                    addPromises(promises, links, model(self.links[keys[i]]), item, key, options(self.links[keys[i]]));
                                }
                            } else if (context instanceof Backbone.Model) {
                                links = context.get('links');
                            } else {
                                links = context.links;
                            }
                        } else {
                            context = this;
                            links = this.get('links');
                        }

                        addPromises(promises, links, model(this.links[keys[i]]), context, key, options(this.links[keys[i]]));
                    }
                }
            }

            return $.when.apply($, promises)
                .then(function () {
                    self.trigger('follow', self);
                });
        },

        fetch: function (options) {
            var self = this;

            return Backbone.Model.prototype.fetch.call(this, options)
                .then(function () {
                    return options ? self.follow(options.follow) : self.follow();
                });
        },

        sync: function (method, model, options) {
            if (this.mediaType) {
                options.accepts = { json: this.mediaType };

                if (method === 'create' || method === 'update' || method === 'patch') {
                    options.contentType = this.mediaType;
                }
            }

            return Backbone.sync(method, model, options);
        },

        toJSON: function () {
            var json = Backbone.Model.prototype.toJSON.apply(this, arguments);

            // use extend to create a deep copy, otherwise complex properties
            // are copied by reference, and any subsequent changes
            // are made against the model
            json = $.extend(true, {}, json);

            if (this.links) {
                var keys = _.keys(this.links);
                var self = this;

                _.each(keys, function (key) {
                    var context;
                    var jsonContext;

                    if (key.split('.').length > 1) {
                        var attr = key.split('.')[0];
                        key = key.split('.')[1];
                        context = self.get(attr);
                        jsonContext = json[attr];
                    } else {
                        context = self;
                        jsonContext = json;
                    }

                    var isArray = key.indexOf('[]', key.length - 2) !== -1;

                    if (isArray) {
                        key = key.substr(0, key.length - 2);
                    }

                    if (context && context[key]) {
                        if (isArray) {
                            jsonContext[key] = [];

                            for (var i = 0; i < context[key].length; i++) {
                                jsonContext[key].push(context[key][i].toJSON());
                            }
                        } else {
                            jsonContext[key] = context[key].toJSON();
                        }
                    }
                });
            }

            return json;
        }
    });

    Backbone.HypermediaCollection = Backbone.Collection.extend({
        fetch: function (options) {
            var self = this;

            return Backbone.Collection.prototype.fetch.call(this, options).then(function () {
                return $.when.apply($, self.chain()
                    .filter(function (model) { return model instanceof Backbone.HypermediaModel; })
                    .map(function (model) { return model.follow(); })
                    .value());
            })
            .then(function () {
                self.trigger('follow', self);
            });
        },

        sync: function (method, model, options) {
            if (this.mediaType) {
                options.accepts = { json: this.mediaType };
            }

            return Backbone.sync(method, model, options);
        }
    });

    var model = function (thing) {
        if (_.isFunction(thing)) {
            return thing;
        }
        if (thing) {
            return thing.model;
        }
    };

    var options = function (thing) {
        if (_.isFunction(thing)) {
            return {};
        }
        if (thing) {
            return thing.options;
        }
    };

    var addPromises = function (promises, links, model, context, key, options) {
        var isArray = key.indexOf('[]', key.length - 2) !== -1;

        if (isArray) {
            key = key.substr(0, key.length - 2);
        }

        var rels = _.filter(links, function (l) { return l.rel == key; });

        if (rels.length > 1 && !isArray) {
            throw new Error('Found more than one link with rel \'' + key + '\', but the link was not specified as allowing multiple values. To allow multiple values, suffix the link key with \'[]\'.');
        }

        for (var i = 0; i < rels.length; i++) {
            var related = new model(),
                    urlFactory = _.isFunction(related.urlFactory) ? related.urlFactory : _.identity;

            if (isArray) {
                if (!context[key]) {
                    context[key] = [];
                }

                context[key].push(related);
            } else {
                context[key] = related;
            }

            var fetchOptions = _.extend({}, options, { url: urlFactory.call(related, rels[i].href) });

            promises.push(related.fetch(fetchOptions));
        }
    };
}));
<|endoftext|>


<commit_before>var themeData = [
  {
    name: 'Creative Tim',

    description: `[Creative Tim](https://creative-tim.com?affiliate_id=116187) のパートナーによって作られた以下の例を見ると、実世界のアプリケーションがどのように構築されるのか、その背後にある技術スタック、ここまで学んだ主要なコンセプトが実世界のアプリケーションにどう適用されるのか、を知ることができます。`,
    seeMoreUrl: 'https://www.creative-tim.com/bootstrap-themes/vuejs-themes?affiliate_id=116187',
    products: [
      {
        name: 'Vue Argon Design System',
        price: 0,
        description: 'Free Vue.js Design System',
        url: 'https://www.creative-tim.com/product/vue-argon-design-system?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-argon-design-system/vue-argon-design-system.jpg'
      },
      {
        name: 'Vue Black Dashboard Pro',
        price: 59,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-black-dashboard-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-black-dashboard-pro/vue-black-dashboard-pro.jpg'
      },
      {
        name: 'Vue Paper Dashboard 2 Pro',
        price: 59,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-paper-dashboard-2-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-black-dashboard-pro/vue-black-dashboard-pro.jpg'
      },
      {
        name: 'Vue Material Kit',
        price: 0,
        description: 'Free Vue.js UI Kit',
        url: 'https://www.creative-tim.com/product/vue-material-kit?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-material-kit/vue-material-kit.jpg'
      },
      {
        name: 'Vue Black Dashboard',
        price: 0,
        description: 'Free Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-black-dashboard?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-black-dashboard/vue-black-dashboard.jpg'
      },
      {
        name: 'Vue Now UI Kit Pro',
        price: 79,
        description: 'Premium Vue.js UI Kit',
        url: 'https://www.creative-tim.com/product/vue-now-ui-kit-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-now-ui-kit-pro/vue-now-ui-kit-pro.jpg'
      },
      {
        name: 'Vue Now UI Dashboard Pro',
        price: 59,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-now-ui-dashboard-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-now-ui-dashboard-pro/vue-now-ui-dashboard-pro.jpg'
      },
      {
        name: 'Vue Now UI Kit',
        price: 0,
        description: 'Free Vue.js UI Kit',
        url: 'https://www.creative-tim.com/product/vue-now-ui-kit?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-now-ui-kit/vue-now-ui-kit.jpg'
      },
      {
        name: 'Vue Light Bootstrap Dashboard Pro',
        price: 49,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-light-bootstrap-dashboard-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-light-bootstrap-dashboard-pro/vue-light-bootstrap-dashboard-pro.jpg'
      },
      {
        name: 'Vue Material Dashboard Pro',
        price: 59,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-material-dashboard-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-material-dashboard-pro/vue-material-dashboard-pro.jpg'
      },
      {
        name: 'Vue Material Kit Pro',
        price: 89,
        description: 'Premium Vue.js UI Kit',
        url: 'https://www.creative-tim.com/product/vue-material-kit-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-material-kit-pro/vue-material-kit-pro.jpg'
      },
      {
        name: 'Vue Light Bootstrap Dashboard',
        price: 0,
        description: 'Free Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-light-bootstrap-dashboard?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-light-bootstrap-dashboard/vue-light-bootstrap-dashboard.jpg'
      }
    ]
  },
  {
    name: "MDBootstrap",
    description: `Check out the themes, UI Kits and plugins below built by our partners from [MDBootstrap](https://mdbootstrap.com/?utm_ref_id=82665). Learn how to use Vue in professional projects along such technologies as Bootstrap. Templates & ready components make your development faster and more efficient.`,
    seeMoreUrl: "https://mdbootstrap.com/docs/vue?utm_ref_id=82665",
    products: [
      {
        url: "https://mdbootstrap.com/products/vue-ui-kit?utm_ref_id=82665",
        name: "Vue Material Bootstrap Pro",
        description: "Premium Vue.js UI Kit",
        image: "https://mdbootstrap.com/img/Marketing/mdb-press-pack/vuejs/ui-kit.jpg",
        price: 99
      },
      {
        url: "https://mdbootstrap.com/docs/vue?utm_ref_id=82665",
        name: "Vue Material Bootstrap Kit",
        description: "Free Vue.js UI Kit",
        image: "https://mdbootstrap.com/img/Marketing/mdb-press-pack/vuejs/ui-kit.jpg",
        price: 0
      },
      {
        url: "https://mdbootstrap.com/products/vue-admin-dashboard?utm_ref_id=82665",
        name: " Vue Admin Bootstrap Pro",
        description: "Premium Vue.js Templates Pack",
        image: "https://mdbootstrap.com/img/Marketing/mdb-press-pack/vuejs/admin-pack.jpg",
        price: 79
      },
      {
        url: "https://mdbootstrap.com/freebies/vue/admin-dashboard?utm_ref_id=82665",
        name: "Vue Admin Bootstrap",
        description: "Free Vue.js Templates Pack",
        image: "https://mdbootstrap.com/img/Marketing/mdb-press-pack/vuejs/admin-pack.jpg",
        price: 0
      }
    ]
  },
  {
    name: 'PrimeVue',
    description: `The open-source UI component library [PrimeVue](https://www.primefaces.org/primevue/#/?af_id=4218) offers over 50 flexible components to build your apps with! They have a ton of different component themes and Vue-CLI application templates available to get the look&feel that suits you best.`,
    seeMoreUrl: 'https://www.primefaces.org/primevue/#/?af_id=4218',
    products: [
      {
        name: 'Sapphire',
        price: 79,
        description: 'Material Design Admin Template',
        url: 'https://www.primefaces.org/layouts/sapphire-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/sapphire.jpg'
      },
      {
        name: 'Avalon',
        price: 79,
        description: 'Bootstrap Inspired Admin Template',
        url: 'https://www.primefaces.org/layouts/avalon-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/avalon.jpg'
      },
      {
        name: 'Serenity',
        price: 79,
        description: 'Material Design Admin Template',
        url: 'https://www.primefaces.org/layouts/serenity-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/serenity.jpg'
      },
      {
        name: 'Apollo',
        price: 79,
        description: 'Admin Template with a Dark Mode',
        url: 'https://www.primefaces.org/layouts/apollo-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/apollo.jpg'
      },
      {
        name: 'Babylon',
        price: 79,
        description: 'Admin Template with Extensive Options',
        url: 'https://www.primefaces.org/layouts/babylon-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/babylon.jpg'
      },
      {
        name: 'Roma',
        price: 59,
        description: 'Admin Template with a Clean Design System',
        url: 'https://www.primefaces.org/layouts/roma-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/roma.jpg'
      },
      {
        name: 'Ultima',
        price: 79,
        description: 'Material Design Admin Template',
        url: 'https://www.primefaces.org/layouts/ultima-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/ultima.jpg'
      },
      {
        name: 'Prestige',
        price: 59,
        description: 'Highly Customizable Admin Template',
        url: 'https://www.primefaces.org/layouts/prestige-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/prestige.jpg'
      },
      {
        name: 'Sigma',
        price: 0,
        description: 'Free Admin Template',
        url: 'https://www.primefaces.org/sigma-vue/#/?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/sigma.jpg'
      }
    ]
  }

]
<commit_msg>Add flatlogic themes (#2669) (#2201)<commit_after>var themeData = [
  {
    name: 'Creative Tim',

    description: `[Creative Tim](https://creative-tim.com?affiliate_id=116187) のパートナーによって作られた以下の例を見ると、実世界のアプリケーションがどのように構築されるのか、その背後にある技術スタック、ここまで学んだ主要なコンセプトが実世界のアプリケーションにどう適用されるのか、を知ることができます。`,
    seeMoreUrl: 'https://www.creative-tim.com/bootstrap-themes/vuejs-themes?affiliate_id=116187',
    products: [
      {
        name: 'Vue Argon Design System',
        price: 0,
        description: 'Free Vue.js Design System',
        url: 'https://www.creative-tim.com/product/vue-argon-design-system?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-argon-design-system/vue-argon-design-system.jpg'
      },
      {
        name: 'Vue Black Dashboard Pro',
        price: 59,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-black-dashboard-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-black-dashboard-pro/vue-black-dashboard-pro.jpg'
      },
      {
        name: 'Vue Paper Dashboard 2 Pro',
        price: 59,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-paper-dashboard-2-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-black-dashboard-pro/vue-black-dashboard-pro.jpg'
      },
      {
        name: 'Vue Material Kit',
        price: 0,
        description: 'Free Vue.js UI Kit',
        url: 'https://www.creative-tim.com/product/vue-material-kit?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-material-kit/vue-material-kit.jpg'
      },
      {
        name: 'Vue Black Dashboard',
        price: 0,
        description: 'Free Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-black-dashboard?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-black-dashboard/vue-black-dashboard.jpg'
      },
      {
        name: 'Vue Now UI Kit Pro',
        price: 79,
        description: 'Premium Vue.js UI Kit',
        url: 'https://www.creative-tim.com/product/vue-now-ui-kit-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-now-ui-kit-pro/vue-now-ui-kit-pro.jpg'
      },
      {
        name: 'Vue Now UI Dashboard Pro',
        price: 59,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-now-ui-dashboard-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-now-ui-dashboard-pro/vue-now-ui-dashboard-pro.jpg'
      },
      {
        name: 'Vue Now UI Kit',
        price: 0,
        description: 'Free Vue.js UI Kit',
        url: 'https://www.creative-tim.com/product/vue-now-ui-kit?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-now-ui-kit/vue-now-ui-kit.jpg'
      },
      {
        name: 'Vue Light Bootstrap Dashboard Pro',
        price: 49,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-light-bootstrap-dashboard-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-light-bootstrap-dashboard-pro/vue-light-bootstrap-dashboard-pro.jpg'
      },
      {
        name: 'Vue Material Dashboard Pro',
        price: 59,
        description: 'Premium Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-material-dashboard-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-material-dashboard-pro/vue-material-dashboard-pro.jpg'
      },
      {
        name: 'Vue Material Kit Pro',
        price: 89,
        description: 'Premium Vue.js UI Kit',
        url: 'https://www.creative-tim.com/product/vue-material-kit-pro?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-material-kit-pro/vue-material-kit-pro.jpg'
      },
      {
        name: 'Vue Light Bootstrap Dashboard',
        price: 0,
        description: 'Free Vue.js Admin Template',
        url: 'https://www.creative-tim.com/product/vue-light-bootstrap-dashboard?affiliate_id=116187',
        image: 'https://raw.githubusercontent.com/creativetimofficial/public-assets/master/vue-light-bootstrap-dashboard/vue-light-bootstrap-dashboard.jpg'
      }
    ]
  },
  {
    name: "MDBootstrap",
    description: `Check out the themes, UI Kits and plugins below built by our partners from [MDBootstrap](https://mdbootstrap.com/?utm_ref_id=82665). Learn how to use Vue in professional projects along such technologies as Bootstrap. Templates & ready components make your development faster and more efficient.`,
    seeMoreUrl: "https://mdbootstrap.com/docs/vue?utm_ref_id=82665",
    products: [
      {
        url: "https://mdbootstrap.com/products/vue-ui-kit?utm_ref_id=82665",
        name: "Vue Material Bootstrap Pro",
        description: "Premium Vue.js UI Kit",
        image: "https://mdbootstrap.com/img/Marketing/mdb-press-pack/vuejs/ui-kit.jpg",
        price: 99
      },
      {
        url: "https://mdbootstrap.com/docs/vue?utm_ref_id=82665",
        name: "Vue Material Bootstrap Kit",
        description: "Free Vue.js UI Kit",
        image: "https://mdbootstrap.com/img/Marketing/mdb-press-pack/vuejs/ui-kit.jpg",
        price: 0
      },
      {
        url: "https://mdbootstrap.com/products/vue-admin-dashboard?utm_ref_id=82665",
        name: " Vue Admin Bootstrap Pro",
        description: "Premium Vue.js Templates Pack",
        image: "https://mdbootstrap.com/img/Marketing/mdb-press-pack/vuejs/admin-pack.jpg",
        price: 79
      },
      {
        url: "https://mdbootstrap.com/freebies/vue/admin-dashboard?utm_ref_id=82665",
        name: "Vue Admin Bootstrap",
        description: "Free Vue.js Templates Pack",
        image: "https://mdbootstrap.com/img/Marketing/mdb-press-pack/vuejs/admin-pack.jpg",
        price: 0
      }
    ]
  },
  {
    name: 'PrimeVue',
    description: `The open-source UI component library [PrimeVue](https://www.primefaces.org/primevue/#/?af_id=4218) offers over 50 flexible components to build your apps with! They have a ton of different component themes and Vue-CLI application templates available to get the look&feel that suits you best.`,
    seeMoreUrl: 'https://www.primefaces.org/primevue/#/?af_id=4218',
    products: [
      {
        name: 'Sapphire',
        price: 79,
        description: 'Material Design Admin Template',
        url: 'https://www.primefaces.org/layouts/sapphire-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/sapphire.jpg'
      },
      {
        name: 'Avalon',
        price: 79,
        description: 'Bootstrap Inspired Admin Template',
        url: 'https://www.primefaces.org/layouts/avalon-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/avalon.jpg'
      },
      {
        name: 'Serenity',
        price: 79,
        description: 'Material Design Admin Template',
        url: 'https://www.primefaces.org/layouts/serenity-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/serenity.jpg'
      },
      {
        name: 'Apollo',
        price: 79,
        description: 'Admin Template with a Dark Mode',
        url: 'https://www.primefaces.org/layouts/apollo-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/apollo.jpg'
      },
      {
        name: 'Babylon',
        price: 79,
        description: 'Admin Template with Extensive Options',
        url: 'https://www.primefaces.org/layouts/babylon-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/babylon.jpg'
      },
      {
        name: 'Roma',
        price: 59,
        description: 'Admin Template with a Clean Design System',
        url: 'https://www.primefaces.org/layouts/roma-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/roma.jpg'
      },
      {
        name: 'Ultima',
        price: 79,
        description: 'Material Design Admin Template',
        url: 'https://www.primefaces.org/layouts/ultima-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/ultima.jpg'
      },
      {
        name: 'Prestige',
        price: 59,
        description: 'Highly Customizable Admin Template',
        url: 'https://www.primefaces.org/layouts/prestige-vue?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/prestige.jpg'
      },
      {
        name: 'Sigma',
        price: 0,
        description: 'Free Admin Template',
        url: 'https://www.primefaces.org/sigma-vue/#/?af_id=4218',
        image: 'https://www.primefaces.org/vue-templates/sigma.jpg'
      }
    ]
  },
  {
    name: 'Flatlogic',
    description: `Check the admin dashboards templates built by our partners from [Flatlogic](https://flatlogic.com/templates?ref=x-fdkuTAVW). With these themes you can see how real applications is built. Additionally these templates will help you to start a new application and save you time and money.`,
    seeMoreUrl: 'https://flatlogic.com/templates?ref=x-fdkuTAVW',
    products: [
      {
        name: 'Sing App Vue',
        price: 99,
        description: 'Vue Admin Dashboard Template',
        url: 'https://flatlogic.com/templates/sing-app-vue?ref=x-fdkuTAVW',
        image: 'https://flatlogic.com/assets/templates/sing_vue_full-cb6b9fdd0465a0d60c0fafa2deca1929cc4966957c8e1ce57565a5e2f45ff043.webp'
      },
      {
        name: 'Sing App Vue Node.js',
        price: 149,
        description: 'Vue Admin Dashboard Template with Node.JS Backend',
        url: 'https://flatlogic.com/templates/sing-app-vue-node-js?ref=x-fdkuTAVW',
        image: 'https://flatlogic.com/assets/templates/sing_vue_full_nodejs-78af09220a2d085fec9bbc1071879915b30737f25d72669152f3b090291609aa.webp'
      },
      {
        name: 'Light Blue Vue',
        price: 99,
        description: 'Vue Admin Dashboard Template',
        url: 'https://flatlogic.com/templates/light-blue-vue?ref=x-fdkuTAVW',
        image: 'https://flatlogic.com/assets/templates/light_blue_vue_full-e044d30242912d5756b190e576bf30ea967b725c09d2e371a7eabf1c6a9c33b2.webp'
      },
      {
        name: 'Light Blue Vue Admin',
        price: 0,
        description: 'Free and Open Source Vue JS Template for Admin Dashboard',
        url: 'https://flatlogic.com/templates/light-blue-vue-lite?ref=x-fdkuTAVW',
        image: 'https://flatlogic.com/assets/templates/light_blue_vue_lite-c1937a2a01e5e8e8ede29a3ead011126e9dab5c58b9017daefe36411f9a1089e.webp'
      },
      {
        name: 'Sing App Vue Dashboard',
        price: 0,
        description: 'Free and Open Source Vue.js Admin Dashboard Template',
        url: 'https://flatlogic.com/templates/sing-app-vue-dashboard?ref=x-fdkuTAVW',
        image: 'https://flatlogic.com/assets/templates/sing_vue_lite-ae3ec037404bc9ae29599a16afe276e638bd916713d75946c604feb69323ec21.webp'
      },
      {
        name: 'Light Blue Vue Node.js',
        price: 149,
        description: 'Vue Admin Template with Node.JS Backend',
        url: 'https://flatlogic.com/templates/light-blue-vue-node-js?ref=x-fdkuTAVW',
        image: 'https://flatlogic.com/assets/templates/light_blue_vue_full_nodejs-06f941c849f0a83a3c0d38c728101eace06a7515ea048f12bcd91f165280246f.webp'
      }
    ]
  }

]
<|endoftext|>


<commit_before>var Cast = require('../util/cast');

var Scratch3SensingBlocks = function (runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
};

/**
 * Retrieve the block primitives implemented by this package.
 * @return {Object.<string, Function>} Mapping of opcode to Function.
 */
Scratch3SensingBlocks.prototype.getPrimitives = function () {
    return {
        sensing_touchingobject: this.touchingObject,
        sensing_touchingcolor: this.touchingColor,
        sensing_coloristouchingcolor: this.colorTouchingColor,
        sensing_distanceto: this.distanceTo,
        sensing_timer: this.getTimer,
        sensing_resettimer: this.resetTimer,
        sensing_of: this.getAttributeOf,
        sensing_mousex: this.getMouseX,
        sensing_mousey: this.getMouseY,
        sensing_mousedown: this.getMouseDown,
        sensing_keypressed: this.getKeyPressed,
        sensing_current: this.current,
        sensing_dayssince2000: this.daysSince2000
    };
};

Scratch3SensingBlocks.prototype.touchingObject = function (args, util) {
    var requestedObject = args.TOUCHINGOBJECTMENU;
    if (requestedObject === '_mouse_') {
        var mouseX = util.ioQuery('mouse', 'getX');
        var mouseY = util.ioQuery('mouse', 'getY');
        return util.target.isTouchingPoint(mouseX, mouseY);
    } else if (requestedObject === '_edge_') {
        return util.target.isTouchingEdge();
    } else {
        return util.target.isTouchingSprite(requestedObject);
    }
};

Scratch3SensingBlocks.prototype.touchingColor = function (args, util) {
    var color = Cast.toRgbColorList(args.COLOR);
    return util.target.isTouchingColor(color);
};

Scratch3SensingBlocks.prototype.colorTouchingColor = function (args, util) {
    var maskColor = Cast.toRgbColorList(args.COLOR);
    var targetColor = Cast.toRgbColorList(args.COLOR2);
    return util.target.colorIsTouchingColor(targetColor, maskColor);
};

Scratch3SensingBlocks.prototype.distanceTo = function (args, util) {
    if (util.target.isStage) return 10000;

    var targetX = 0;
    var targetY = 0;
    if (args.DISTANCETOMENU === '_mouse_') {
        targetX = util.ioQuery('mouse', 'getX');
        targetY = util.ioQuery('mouse', 'getY');
    } else {
        var distTarget = this.runtime.getSpriteTargetByName(
            args.DISTANCETOMENU
        );
        if (!distTarget) return 10000;
        targetX = distTarget.x;
        targetY = distTarget.y;
    }

    var dx = util.target.x - targetX;
    var dy = util.target.y - targetY;
    return Math.sqrt((dx * dx) + (dy * dy));
};

Scratch3SensingBlocks.prototype.getTimer = function (args, util) {
    return util.ioQuery('clock', 'projectTimer');
};

Scratch3SensingBlocks.prototype.resetTimer = function (args, util) {
    util.ioQuery('clock', 'resetProjectTimer');
};

Scratch3SensingBlocks.prototype.getMouseX = function (args, util) {
    return util.ioQuery('mouse', 'getX');
};

Scratch3SensingBlocks.prototype.getMouseY = function (args, util) {
    return util.ioQuery('mouse', 'getY');
};

Scratch3SensingBlocks.prototype.getMouseDown = function (args, util) {
    return util.ioQuery('mouse', 'getIsDown');
};

Scratch3SensingBlocks.prototype.current = function (args) {
    var menuOption = Cast.toString(args.CURRENTMENU).toLowerCase();
    var date = new Date();
    switch (menuOption) {
    case 'year': return date.getFullYear();
    case 'month': return date.getMonth() + 1; // getMonth is zero-based
    case 'date': return date.getDate();
    case 'dayofweek': return date.getDay() + 1; // getDay is zero-based, Sun=0
    case 'hour': return date.getHours();
    case 'minute': return date.getMinutes();
    case 'second': return date.getSeconds();
    }
    return 0;
};

Scratch3SensingBlocks.prototype.getKeyPressed = function (args, util) {
    return util.ioQuery('keyboard', 'getKeyIsDown', args.KEY_OPTION);
};

Scratch3SensingBlocks.prototype.daysSince2000 = function () {
    var msPerDay = 24 * 60 * 60 * 1000;
    var start = new Date(2000, 1 - 1, 1);
    var today = new Date();
    var dstAdjust = today.getTimezoneOffset() - start.getTimezoneOffset();
    var mSecsSinceStart = today.valueOf() - start.valueOf();
    mSecsSinceStart += ((today.getTimezoneOffset() - dstAdjust) * 60 * 1000);
    return mSecsSinceStart / msPerDay;
};

Scratch3SensingBlocks.prototype.getAttributeOf = function (args) {
    var attrTarget;

    if (args.OBJECT === '_stage_') {
        attrTarget = this.runtime.getTargetForStage();
    } else {
        attrTarget = this.runtime.getSpriteTargetByName(args.OBJECT);
    }

    // Generic attributes
    if (attrTarget.isStage) {
        switch (args.PROPERTY) {
        // Scratch 1.4 support
        case 'background #': return attrTarget.currentCostume + 1;

        case 'backdrop #': return attrTarget.currentCostume + 1;
        case 'backdrop name':
            return attrTarget.sprite.costumes[attrTarget.currentCostume].name;
        case 'volume': return; // @todo: Keep this in mind for sound blocks!
        }
    } else {
        switch (args.PROPERTY) {
        case 'x position': return attrTarget.x;
        case 'y position': return attrTarget.y;
        case 'direction': return attrTarget.direction;
        case 'costume #': return attrTarget.currentCostume + 1;
        case 'costume name':
            return attrTarget.sprite.costumes[attrTarget.currentCostume].name;
        case 'size': return attrTarget.size;
        case 'volume': return; // @todo: above, keep in mind for sound blocks..
        }
    }

    // Variables
    var varName = args.PROPERTY;
    if (attrTarget.variables.hasOwnProperty(varName)) {
        return attrTarget.variables[varName].value;
    }

    // Otherwise, 0
    return 0;
};

module.exports = Scratch3SensingBlocks;
<commit_msg>Fix 1-1 in daysSince2000<commit_after>var Cast = require('../util/cast');

var Scratch3SensingBlocks = function (runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
};

/**
 * Retrieve the block primitives implemented by this package.
 * @return {Object.<string, Function>} Mapping of opcode to Function.
 */
Scratch3SensingBlocks.prototype.getPrimitives = function () {
    return {
        sensing_touchingobject: this.touchingObject,
        sensing_touchingcolor: this.touchingColor,
        sensing_coloristouchingcolor: this.colorTouchingColor,
        sensing_distanceto: this.distanceTo,
        sensing_timer: this.getTimer,
        sensing_resettimer: this.resetTimer,
        sensing_of: this.getAttributeOf,
        sensing_mousex: this.getMouseX,
        sensing_mousey: this.getMouseY,
        sensing_mousedown: this.getMouseDown,
        sensing_keypressed: this.getKeyPressed,
        sensing_current: this.current,
        sensing_dayssince2000: this.daysSince2000
    };
};

Scratch3SensingBlocks.prototype.touchingObject = function (args, util) {
    var requestedObject = args.TOUCHINGOBJECTMENU;
    if (requestedObject === '_mouse_') {
        var mouseX = util.ioQuery('mouse', 'getX');
        var mouseY = util.ioQuery('mouse', 'getY');
        return util.target.isTouchingPoint(mouseX, mouseY);
    } else if (requestedObject === '_edge_') {
        return util.target.isTouchingEdge();
    } else {
        return util.target.isTouchingSprite(requestedObject);
    }
};

Scratch3SensingBlocks.prototype.touchingColor = function (args, util) {
    var color = Cast.toRgbColorList(args.COLOR);
    return util.target.isTouchingColor(color);
};

Scratch3SensingBlocks.prototype.colorTouchingColor = function (args, util) {
    var maskColor = Cast.toRgbColorList(args.COLOR);
    var targetColor = Cast.toRgbColorList(args.COLOR2);
    return util.target.colorIsTouchingColor(targetColor, maskColor);
};

Scratch3SensingBlocks.prototype.distanceTo = function (args, util) {
    if (util.target.isStage) return 10000;

    var targetX = 0;
    var targetY = 0;
    if (args.DISTANCETOMENU === '_mouse_') {
        targetX = util.ioQuery('mouse', 'getX');
        targetY = util.ioQuery('mouse', 'getY');
    } else {
        var distTarget = this.runtime.getSpriteTargetByName(
            args.DISTANCETOMENU
        );
        if (!distTarget) return 10000;
        targetX = distTarget.x;
        targetY = distTarget.y;
    }

    var dx = util.target.x - targetX;
    var dy = util.target.y - targetY;
    return Math.sqrt((dx * dx) + (dy * dy));
};

Scratch3SensingBlocks.prototype.getTimer = function (args, util) {
    return util.ioQuery('clock', 'projectTimer');
};

Scratch3SensingBlocks.prototype.resetTimer = function (args, util) {
    util.ioQuery('clock', 'resetProjectTimer');
};

Scratch3SensingBlocks.prototype.getMouseX = function (args, util) {
    return util.ioQuery('mouse', 'getX');
};

Scratch3SensingBlocks.prototype.getMouseY = function (args, util) {
    return util.ioQuery('mouse', 'getY');
};

Scratch3SensingBlocks.prototype.getMouseDown = function (args, util) {
    return util.ioQuery('mouse', 'getIsDown');
};

Scratch3SensingBlocks.prototype.current = function (args) {
    var menuOption = Cast.toString(args.CURRENTMENU).toLowerCase();
    var date = new Date();
    switch (menuOption) {
    case 'year': return date.getFullYear();
    case 'month': return date.getMonth() + 1; // getMonth is zero-based
    case 'date': return date.getDate();
    case 'dayofweek': return date.getDay() + 1; // getDay is zero-based, Sun=0
    case 'hour': return date.getHours();
    case 'minute': return date.getMinutes();
    case 'second': return date.getSeconds();
    }
    return 0;
};

Scratch3SensingBlocks.prototype.getKeyPressed = function (args, util) {
    return util.ioQuery('keyboard', 'getKeyIsDown', args.KEY_OPTION);
};

Scratch3SensingBlocks.prototype.daysSince2000 = function () {
    var msPerDay = 24 * 60 * 60 * 1000;
    var start = new Date(2000, 0, 1); // Months are 0-indexed.
    var today = new Date();
    var dstAdjust = today.getTimezoneOffset() - start.getTimezoneOffset();
    var mSecsSinceStart = today.valueOf() - start.valueOf();
    mSecsSinceStart += ((today.getTimezoneOffset() - dstAdjust) * 60 * 1000);
    return mSecsSinceStart / msPerDay;
};

Scratch3SensingBlocks.prototype.getAttributeOf = function (args) {
    var attrTarget;

    if (args.OBJECT === '_stage_') {
        attrTarget = this.runtime.getTargetForStage();
    } else {
        attrTarget = this.runtime.getSpriteTargetByName(args.OBJECT);
    }

    // Generic attributes
    if (attrTarget.isStage) {
        switch (args.PROPERTY) {
        // Scratch 1.4 support
        case 'background #': return attrTarget.currentCostume + 1;

        case 'backdrop #': return attrTarget.currentCostume + 1;
        case 'backdrop name':
            return attrTarget.sprite.costumes[attrTarget.currentCostume].name;
        case 'volume': return; // @todo: Keep this in mind for sound blocks!
        }
    } else {
        switch (args.PROPERTY) {
        case 'x position': return attrTarget.x;
        case 'y position': return attrTarget.y;
        case 'direction': return attrTarget.direction;
        case 'costume #': return attrTarget.currentCostume + 1;
        case 'costume name':
            return attrTarget.sprite.costumes[attrTarget.currentCostume].name;
        case 'size': return attrTarget.size;
        case 'volume': return; // @todo: above, keep in mind for sound blocks..
        }
    }

    // Variables
    var varName = args.PROPERTY;
    if (attrTarget.variables.hasOwnProperty(varName)) {
        return attrTarget.variables[varName].value;
    }

    // Otherwise, 0
    return 0;
};

module.exports = Scratch3SensingBlocks;
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import { connect } from 'utils';
import styles from './styles.css';

const cn = require('classnames/bind').bind(styles);

const Sponsors = ({
  all = false,
  store: {
    data: {
      sponsorsItems,
    },
  },
}) => (

    {sponsorsItems.map((items, itemsIndex) => (

 0 && itemsIndex < 2),
        isSilver: (itemsIndex >= 2 && itemsIndex < 6),
        isBronze: itemsIndex >= 6,
        isVisible: itemsIndex < 2 || all,
      })}>
        {Array.from(Array(
          itemsIndex < 3 ? itemsIndex + 1 : 4
        ).keys()).map((index) => (

        ))
      }

    ))}

);

Sponsors.propTypes = {
  all: PropTypes.bool,
  store: PropTypes.object.isRequired,
};

export default connect(Sponsors);
<commit_msg>[#131] Added link to sponsors website (#132)<commit_after>import React, { PropTypes } from 'react';
import { connect } from 'utils';
import styles from './styles.css';

const cn = require('classnames/bind').bind(styles);

const Sponsors = ({
  all = false,
  store: {
    data: {
      sponsorsItems,
    },
  },
}) => (

    {sponsorsItems.map((items, itemsIndex) => (

 0 && itemsIndex < 2),
        isSilver: (itemsIndex >= 2 && itemsIndex < 6),
        isBronze: itemsIndex >= 6,
        isVisible: itemsIndex < 2 || all,
      })}>
        {Array.from(Array(
          itemsIndex < 3 ? itemsIndex + 1 : 4
        ).keys()).map((index) => (

        ))
      }

    ))}

);

Sponsors.propTypes = {
  all: PropTypes.bool,
  store: PropTypes.object.isRequired,
};

export default connect(Sponsors);
<|endoftext|>