<commit_before>'use strict';

import { connect } from 'react-redux';
import { toggleDropdown } from 'app/actions/dropdowns';
import { showAllTripIdeasOnMap } from 'app/actions/map';
import { hideModal } from 'app/actions/modals';
import { apiDeleteTripIdea } from 'app/actions/trips';
import TripIdeasList from 'app/components/TripIdeasList';
import { acComponents, modalComponents } from 'app/constants';

const mapStateToProps = (state) => {
  const { user } = state.authState;
  const {
    addTripIdeas,
    filterTripIdeas
  } = state.componentsState.dropdownsState;
  const { showDropdown: showAddIdeasDropdown } = addTripIdeas;
  const { showDropdown: showFilterIdeasDropdown } = filterTripIdeas;
  const {
    error,
    isFetching,
    trip,
    tripIdeaToDelete
  } = state.tripState;
  const { showModal } = state.componentsState.modalsState.deleteTripIdea;
  const { creator, ideas, visibility } = trip;

  return {
    error,
    ideas,
    isFetching,
    isViewOnly: visibility === 'viewOnly' && (!user || user._id !== creator),
    showAddIdeasDropdown,
    showFilterIdeasDropdown,
    showModal,
    tripIdeaToDelete
  };
};

const mapDispatchToProps = (dispatch) => {
  const { tripIdeaAC } = acComponents;
  const { deleteTripIdea } = modalComponents;

  return {
    onDeleteTripIdea() {
      dispatch(apiDeleteTripIdea());
    },

    onHide() {
      dispatch(hideModal(deleteTripIdea));
    },

    onShowAllIdeas() {
      dispatch(showAllTripIdeasOnMap());
    },

    onShowDropdown(dropdownId) {
      dispatch(toggleDropdown(dropdownId));
    }
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(TripIdeasList);
<commit_msg>Filter left column ideas based on filtered categories<commit_after>'use strict';

import { connect } from 'react-redux';
import { toggleDropdown } from 'app/actions/dropdowns';
import { showAllTripIdeasOnMap } from 'app/actions/map';
import { hideModal } from 'app/actions/modals';
import { apiDeleteTripIdea } from 'app/actions/trips';
import TripIdeasList from 'app/components/TripIdeasList';
import { acComponents, modalComponents } from 'app/constants';

const mapStateToProps = (state) => {
  const { user } = state.authState;
  const {
    addTripIdeas,
    filterTripIdeas
  } = state.componentsState.dropdownsState;
  const { categories: filterCategories } = state.componentsState.filtersState;
  const { showDropdown: showAddIdeasDropdown } = addTripIdeas;
  const { showDropdown: showFilterIdeasDropdown } = filterTripIdeas;
  const {
    error,
    isFetching,
    trip,
    tripIdeaToDelete
  } = state.tripState;
  const { showModal } = state.componentsState.modalsState.deleteTripIdea;
  const { creator, ideas, visibility } = trip;
  const filteredIdeas = filterCategories.length ?
    ideas.filter(idea => filterCategories.includes(idea.category)) :
    ideas;

  return {
    error,
    ideas: filteredIdeas,
    isFetching,
    isViewOnly: visibility === 'viewOnly' && (!user || user._id !== creator),
    showAddIdeasDropdown,
    showFilterIdeasDropdown,
    showModal,
    tripIdeaToDelete
  };
};

const mapDispatchToProps = (dispatch) => {
  const { tripIdeaAC } = acComponents;
  const { deleteTripIdea } = modalComponents;

  return {
    onDeleteTripIdea() {
      dispatch(apiDeleteTripIdea());
    },

    onHide() {
      dispatch(hideModal(deleteTripIdea));
    },

    onShowAllIdeas() {
      dispatch(showAllTripIdeasOnMap());
    },

    onShowDropdown(dropdownId) {
      dispatch(toggleDropdown(dropdownId));
    }
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(TripIdeasList);
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var mongoose = require('mongoose')
  , Article = mongoose.model('Article')
  , utils = require('../../lib/utils')
  , _ = require('underscore')

/**
 * Load article
 */

exports.article = function(req, res, next, id){
  var User = mongoose.model('User')

  Article.load(id, function (err, article) {
    if (err) return next(err)
    if (!article) return next(new Error('not found'))
    req.article = article
    next()
  })
}

/**
 * New article
 */

exports.new = function(req, res){
  res.render('articles/new', {
    title: 'New Article',
    article: new Article({})
  })
}

/**
 * Create an article
 */

exports.create = function (req, res) {
  var article = new Article(req.body)
  article.user = req.user

  article.uploadAndSave(req.files.image, function (err) {
    if (!err) {
      req.flash('success', 'Successfully created article!')
      return res.redirect('/articles/'+article._id)
    }

    res.render('articles/new', {
      title: 'New Article',
      article: article,
      errors: utils.errors(err.errors || err)
    })
  })
}

/**
 * Edit an article
 */

exports.edit = function (req, res) {
  res.render('articles/edit', {
    title: 'Edit ' + req.article.title,
    article: req.article
  })
}

/**
 * Update article
 */

exports.update = function(req, res){
  var article = req.article
  article = _.extend(article, req.body)

  article.uploadAndSave(req.files.image, function(err) {
    if (!err) {
      return res.redirect('/articles/' + article._id)
    }

    res.render('articles/edit', {
      title: 'Edit Article',
      article: article,
      errors: err.errors
    })
  })
}

/**
 * View an article
 */

exports.show = function(req, res){
  res.render('articles/show', {
    title: req.article.title,
    article: req.article
  })
}

/**
 * Delete an article
 */

exports.destroy = function(req, res){
  var article = req.article
  article.remove(function(err){
    req.flash('info', 'Deleted successfully')
    res.redirect('/articles')
  })
}

/**
 * List articles
 */

exports.index = function(req, res){
  var page = (req.param('page') > 0 ? req.param('page') : 1) - 1
  var perPage = 4
  var options = {
    perPage: perPage,
    page: page
  }

  Article.list(options, function(err, articles) {
    if (err) return res.render('500')
    Article.count().exec(function (err, count) {
      res.render('articles/index', {
        title: 'Articles',
        articles: articles,
        page: page + 1,
        pages: Math.ceil(count / perPage)
      })
    })
  })
}
<commit_msg>reset perPage to 30<commit_after>
/**
 * Module dependencies.
 */

var mongoose = require('mongoose')
  , Article = mongoose.model('Article')
  , utils = require('../../lib/utils')
  , _ = require('underscore')

/**
 * Load article
 */

exports.article = function(req, res, next, id){
  var User = mongoose.model('User')

  Article.load(id, function (err, article) {
    if (err) return next(err)
    if (!article) return next(new Error('not found'))
    req.article = article
    next()
  })
}

/**
 * New article
 */

exports.new = function(req, res){
  res.render('articles/new', {
    title: 'New Article',
    article: new Article({})
  })
}

/**
 * Create an article
 */

exports.create = function (req, res) {
  var article = new Article(req.body)
  article.user = req.user

  article.uploadAndSave(req.files.image, function (err) {
    if (!err) {
      req.flash('success', 'Successfully created article!')
      return res.redirect('/articles/'+article._id)
    }

    res.render('articles/new', {
      title: 'New Article',
      article: article,
      errors: utils.errors(err.errors || err)
    })
  })
}

/**
 * Edit an article
 */

exports.edit = function (req, res) {
  res.render('articles/edit', {
    title: 'Edit ' + req.article.title,
    article: req.article
  })
}

/**
 * Update article
 */

exports.update = function(req, res){
  var article = req.article
  article = _.extend(article, req.body)

  article.uploadAndSave(req.files.image, function(err) {
    if (!err) {
      return res.redirect('/articles/' + article._id)
    }

    res.render('articles/edit', {
      title: 'Edit Article',
      article: article,
      errors: err.errors
    })
  })
}

/**
 * View an article
 */

exports.show = function(req, res){
  res.render('articles/show', {
    title: req.article.title,
    article: req.article
  })
}

/**
 * Delete an article
 */

exports.destroy = function(req, res){
  var article = req.article
  article.remove(function(err){
    req.flash('info', 'Deleted successfully')
    res.redirect('/articles')
  })
}

/**
 * List articles
 */

exports.index = function(req, res){
  var page = (req.param('page') > 0 ? req.param('page') : 1) - 1
  var perPage = 30
  var options = {
    perPage: perPage,
    page: page
  }

  Article.list(options, function(err, articles) {
    if (err) return res.render('500')
    Article.count().exec(function (err, count) {
      res.render('articles/index', {
        title: 'Articles',
        articles: articles,
        page: page + 1,
        pages: Math.ceil(count / perPage)
      })
    })
  })
}
<|endoftext|>


<commit_before>transformFunction = function(model) {
	var mounths = Array('Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic');
	var transform = model.toJSON();
	var date = new Date(transform.date * 1000);
	transform.date = "\t" + date.getDate() + ' ' + mounths[date.getMonth()] + ' ' + date.getFullYear() + "\t";
	transform.title = "\t" + transform.title + "\t";
	transform.body = Alloy.CFG.pageCss + transform.body;
	return transform;
};
var args = arguments[0] || {};
var blog = Alloy.Collections.instance('blog');
blog.fetch();
blogPost = blog.get(args);
blogPost = transformFunction(blogPost);
var blogPostImage = (blogPost.image != null) ? blogPost.image : '/images/blog.png';

var image = Ti.UI.createImageView({
	image : blogPostImage,
	width : Ti.UI.FILL,
	top : 0
});
var viewLabel = Ti.UI.createView({
	layout : 'vertical',
	width : Ti.UI.FILL,
	top : "5%",
	height : Ti.UI.SIZE,
	left : 0
});
var labelTitle = Ti.UI.createLabel({
	text : blogPost.title
});
$.addClass(labelTitle,"listTitle");
var labelDate = Ti.UI.createLabel({
	text : blogPost.date
});
$.addClass(labelDate,"listDate");
viewLabel.add(labelDate);
$.headerBlog.add(image);
$.headerBlog.add(viewLabel);
$.blogPost.add(labelTitle);
var webView = Ti.UI.createWebView({
	html : blogPost.body,
	enableZoomControls : false,
	width : Ti.UI.SIZE
});
webView.addEventListener('beforeload', function(e) {
	if (e.url.match(/^file:\/\//)) {
	} else {
		Ti.Platform.openURL(e.url);
		webView.stopLoading();
	}
});
webView.addEventListener('load', function(e) {
	if (e.url.match(/^file:\/\//)) {
	} else {
		if (webView.canGoBack()) {
			webView.goBack();
		}
	}
});
$.blogPost.add(webView);
<commit_msg>fix blog title elaboration<commit_after>transformFunction = function(model) {
	var mounths = Array('Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic');
	var transform = model.toJSON();
	var date = new Date(transform.date * 1000);
	transform.date = "\t" + date.getDate() + ' ' + mounths[date.getMonth()] + ' ' + date.getFullYear() + "\t";
	transform.body = Alloy.CFG.pageCss + transform.body;
	return transform;
};
var args = arguments[0] || {};
var blog = Alloy.Collections.instance('blog');
blog.fetch();
blogPost = blog.get(args);
blogPost = transformFunction(blogPost);
var blogPostImage = (blogPost.image != null) ? blogPost.image : '/images/blog.png';

var image = Ti.UI.createImageView({
	image : blogPostImage,
	width : Ti.UI.FILL,
	top : 0
});
var viewLabel = Ti.UI.createView({
	layout : 'vertical',
	width : Ti.UI.FILL,
	top : "5%",
	height : Ti.UI.SIZE,
	left : 0
});
var labelTitle = Ti.UI.createLabel({
	text : blogPost.title
});
$.addClass(labelTitle,"listTitle");
var labelDate = Ti.UI.createLabel({
	text : blogPost.date
});
$.addClass(labelDate,"listDate");
viewLabel.add(labelDate);
$.headerBlog.add(image);
$.headerBlog.add(viewLabel);
$.blogPost.add(labelTitle);
var webView = Ti.UI.createWebView({
	html : blogPost.body,
	enableZoomControls : false,
	width : Ti.UI.SIZE
});
webView.addEventListener('beforeload', function(e) {
	if (e.url.match(/^file:\/\//)) {
	} else {
		Ti.Platform.openURL(e.url);
		webView.stopLoading();
	}
});
webView.addEventListener('load', function(e) {
	if (e.url.match(/^file:\/\//)) {
	} else {
		if (webView.canGoBack()) {
			webView.goBack();
		}
	}
});
$.blogPost.add(webView);
<|endoftext|>


<commit_before>//
// Messages Controller
//

'use strict';

module.exports = function() {

    var app = this.app,
        core = this.core,
        middlewares = this.middlewares;

    core.messages.on('messages:new', function(message) {
        app.io.room(message.room.id)
              .broadcast('messages:new', message);
    });

    //
    // Routes
    //
    app.get('/messages', middlewares.requireLogin, function(req, res) {
        req.io.route('messages:list');
    });

    app.post('/messages', middlewares.requireLogin, function(req, res) {
        req.io.route('messages:create');
    });

    //
    // Sockets
    //
    app.io.route('messages', {
        create: function(req) {
            var options = {
                owner: req.user._id,
                room: req.param('room'),
                text: req.param('text')
            };

            core.messages.create(options, function(err, message) {
                if (err) {
                    req.io.respond(err, 400);
                    return;
                }

                req.io.respond(message, 201);
            });
        },
        list: function(req) {
            var data = req.data || req.query,
                options = {
                    room: data.room || null,
                    from: data.from || null,
                    limit: data.limit || null,
                };

            core.messages.list(options, function(err, messages) {
                if (err) {
                    req.io.respond(err, 400);
                    return;
                }
                req.io.respond(messages);
            });
        }
    });

};
<commit_msg>Restore message POST data<commit_after>//
// Messages Controller
//

'use strict';

module.exports = function() {

    var app = this.app,
        core = this.core,
        middlewares = this.middlewares;

    core.messages.on('messages:new', function(message) {
        app.io.room(message.room.id)
              .broadcast('messages:new', message);
    });

    //
    // Routes
    //
    app.get('/messages', middlewares.requireLogin, function(req, res) {
        req.io.route('messages:list');
    });

    app.post('/messages', middlewares.requireLogin, function(req, res) {
        req.io.route('messages:create');
    });

    //
    // Sockets
    //
    app.io.route('messages', {
        create: function(req) {
            var data = req.data || req.body,
                options = {
                    owner: req.user._id,
                    room: data.room,
                    text: data.text
                };
            core.messages.create(options, function(err, message) {
                if (err) {
                    req.io.respond(err, 400);
                    return;
                }
                req.io.respond(message, 201);
            });
        },
        list: function(req) {
            var data = req.data || req.query,
                options = {
                    room: data.room || null,
                    from: data.from || null,
                    limit: data.limit || null,
                };
            core.messages.list(options, function(err, messages) {
                if (err) {
                    req.io.respond(err, 400);
                    return;
                }
                req.io.respond(messages);
            });
        }
    });

};
<|endoftext|>


<commit_before>"use strict";

//Load dependencies
var request = require("request");
var _ = require('lodash');
var cheerio = require("cheerio");
var async = require("async");
var bnetAPI = process.require("core/api/bnet.js");
var updateModel = process.require("updates/updateModel.js");

//Configuration
var applicationStorage = process.require("core/applicationStorage.js");

module.exports.getWoWProgressPage = function (path, callback) {
    var url = "http://www.wowprogress.com" + path;
    var logger = applicationStorage.logger;
    logger.verbose("GET wowprogress URL %s", url);
    request(url, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            callback(null, body);
        } else {
            callback(new Error("Error HTTP " + error.message + " on fetching wowprogress api " + url));
        }
    });
};

module.exports.getGuildsUrlsOnPage = function (number, callback) {
    var self = this;
    async.waterfall([
        function (callback) {
            if (number == -1) {
                var url = "/pve/rating/next";
            } else {
                var url = "/pve/rating/next/" + number + "/rating";
            }
            self.getWoWProgressPage(url, function (error, body) {
                callback(error, body)
            })
        },
        function (body, callback) {
            var $ = cheerio.load(body);

            var urls = [];
            $('body').find('.ratingContainer table.rating tr a.guild').each(function (i, elem) {
                urls.push($(this).attr('href'));
            });

            callback(null, urls);
        }
    ], function (error, urls) {
        callback(error, urls);
    })

};

module.exports.getKills = function (url, callback) {
    var self = this;
    var logger = applicationStorage.logger;

    async.waterfall([
            function (callback) {
                self.getWoWProgressPage(url, function (error, body) {
                    callback(error, body)
                })
            },
            function (body, callback) {

                var $ = cheerio.load(body);

                var armoryUrl = decodeURIComponent(($('.armoryLink').attr('href')));

                if (armoryUrl == "undefined") {
                    return callback(new Error('Armory link undefined'));
                }
                try {
                    var name = $('h1').text().match("“(.*)” WoW Guild")[1];
                    var realm = armoryUrl.match('battle.net/wow/guild/(.*)/(.*)/')[1];
                    var region = armoryUrl.match('http://(.*).battle.net/wow/guild/')[1];
                } catch (e) {
                    logger.info("Parsing error, trying CN");
                    try {
                        var name = $('h1').text().match("“(.*)” WoW Guild")[1];
                        var realm = armoryUrl.match('www.battlenet.com.cn/wow/guild/(.*)/(.*)/')[1];
                        var region = armoryUrl.match('http://www.battlenet.com.(.*)/wow/guild/')[1];
                    } catch (e) {
                        logger.error("Error on page parsing %s", url);
                        callback(true);
                    }
                }

                bnetAPI.getGuild(region, realm, name, [], function (error, guild) {
                    if (!guild || !guild.realm || !guild.name) {
                        logger.warn("Bnet return empty guild so try to update %s-%s-%s, skip it", region, realm, name);

                        //Bug on chinese ...
                        if (region == "cn") {
                            updateModel.insert("wp_pu", region, realm, name, 3, function () {
                                logger.verbose("Insert GuildProgress to update %s-%s-%s with priority %s", region, realm, name, 3);
                                callback(true);
                            });
                        } else {
                            callback(true);
                        }
                    } else {
                        callback(error, $, region, guild.realm, guild.name);
                    }
                });

            }
            ,
            function ($, region, realm, name, callback) {
                var kills = [];

                var bestTimestamp = 999999999999999999999;
                var normalCount = 0;
                var heroicCount = 0;
                var mythicCount = 0;
                var normalArchimonde = false;
                var heroicArchimonde = false;
                var mythicArchimonde = false;
                var normalArchimondeTimestamp = 0;
                var heroicArchimondeTimestamp = 0;
                var mythicArchimondeTimestamp = 0;

                $('table.rating a.boss_kills_link').each(function (i, elem) {

                    var kill = {region: region.toLowerCase(), realm: realm, name: name};

                    var bossName = $(this).text();

                    var bossArray = bossName.replace(/(^\+)/g, "").trim().split(':');
                    kill["boss"] = bossArray[1].trim();

                    if (bossArray[0] == 'N') {
                        kill["difficulty"] = 'normal';
                        normalCount++;
                    } else if (bossArray[0] == 'H') {
                        kill["difficulty"] = 'heroic'
                        heroicCount++;
                    } else if (bossArray[0] == 'M') {
                        kill["difficulty"] = 'mythic';
                        mythicCount++;
                    } else {
                        callback(new Error("WOWPROGRESS_PARSING_ERROR"));
                    }

                    if (kill["boss"] == "Xhul'horac") {
                        kill["boss"] = "Xhul'horac";
                    }
                    var date = $(this).parent().next().text();
                    var timestamp = new Date(date + " GMT+0000").getTime();

                    if (isNaN(timestamp)) {
                        timestamp = new Date(date.substring(1, date.length) + " GMT+0000").getTime();
                    }

                    if (isNaN(timestamp) || timestamp < 1388534400000) {
                        timestamp = parseInt($(this).parent().next().find('.datetime').attr('data-ts'), 10) * 1000;
                    }

                    //Fix incorrect kill ...
                    if (kill["boss"] == "Archimonde") {
                        bestTimestamp = timestamp;

                        if (kill["difficulty"] == "normal") {
                            normalArchimonde = true;
                            normalArchimondeTimestamp = timestamp;
                        } else if (kill["difficulty"] == "heroic") {
                            heroicArchimonde = true;
                            heroicArchimondeTimestamp = timestamp;
                        } else if (kill["difficulty"] == "mythic") {
                            mythicArchimonde = true;
                            mythicArchimondeTimestamp = timestamp;
                        }

                    }

                    if (timestamp > bestTimestamp) {
                        kill["timestamp"] = bestTimestamp;
                    } else {
                        kill["timestamp"] = timestamp;
                    }

                    if (kill["timestamp"] < new Date("Jun 01 2016 GMT+0000").getTime()) {
                        kills.push(kill);
                    }

                });

                var bossList = [
                    "Hellfire Assault",
                    "Iron Reaver",
                    "Kormrok", "Hellfire High Council",
                    "Kilrogg Deadeye",
                    "Gorefiend",
                    "Shadow-Lord Iskar",
                    "Socrethar the Eternal",
                    "Tyrant Velhari",
                    "Fel Lord Zakuun",
                    "Xhul'horac",
                    "Mannoroth",
                    "Archimonde"];

                if (normalArchimonde && normalCount < 13) {
                    var normalKills = [];
                    async.forEach(bossList, function (boss) {
                        var normalKill = {};
                        normalKill.boss = boss;
                        normalKill.difficulty = "normal";
                        normalKill.region = region.toLowerCase();
                        normalKill.realm = realm;
                        normalKill.name = name;
                        normalKill.timestamp = normalArchimonde;
                        normalKills.push(normalKill);

                    });
                    kills.concat(normalKills);

                }

                if (heroicArchimonde && heroicCount < 13) {
                    var heroicKills = [];
                    async.forEach(bossList, function (boss) {
                        var heroicKill = {};
                        heroicKill.boss = boss;
                        heroicKill.difficulty = "heroic";
                        heroicKill.region = region.toLowerCase();
                        heroicKill.realm = realm;
                        heroicKill.name = name;
                        heroicKill.timestamp = heroicArchimondeTimestamp;
                        heroicKills.push(heroicKill);

                    });
                    kills = kills.concat(heroicKills);
                }
                if (mythicArchimonde && mythicCount < 13) {
                    var mythicKills = [];
                    async.forEach(bossList, function (boss) {
                        var mythicKill = {};
                        mythicKill.boss = boss;
                        mythicKill.difficulty = "mythic";
                        mythicKill.region = region.toLowerCase();
                        mythicKill.realm = realm;
                        mythicKill.name = name;
                        mythicKill.timestamp = mythicArchimondeTimestamp;
                        mythicKills.push(mythicKill);
                    });
                    kills = kills.concat(mythicKills);
                }
                kills = _.uniqBy(kills, function (elem) {
                    return elem.boss + elem.difficulty;
                });
                callback(null, kills);
            }

        ],
        function (error, kills) {
            callback(error, kills);
        }
    );

};

<commit_msg>Fix bug on realm name for chinese<commit_after>"use strict";

//Load dependencies
var request = require("request");
var _ = require('lodash');
var cheerio = require("cheerio");
var async = require("async");
var bnetAPI = process.require("core/api/bnet.js");
var updateModel = process.require("updates/updateModel.js");

//Configuration
var applicationStorage = process.require("core/applicationStorage.js");

module.exports.getWoWProgressPage = function (path, callback) {
    var url = "http://www.wowprogress.com" + path;
    var logger = applicationStorage.logger;
    logger.verbose("GET wowprogress URL %s", url);
    request(url, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            callback(null, body);
        } else {
            callback(new Error("Error HTTP " + error.message + " on fetching wowprogress api " + url));
        }
    });
};

module.exports.getGuildsUrlsOnPage = function (number, callback) {
    var self = this;
    async.waterfall([
        function (callback) {
            if (number == -1) {
                var url = "/pve/rating/next";
            } else {
                var url = "/pve/rating/next/" + number + "/rating";
            }
            self.getWoWProgressPage(url, function (error, body) {
                callback(error, body)
            })
        },
        function (body, callback) {
            var $ = cheerio.load(body);

            var urls = [];
            $('body').find('.ratingContainer table.rating tr a.guild').each(function (i, elem) {
                urls.push($(this).attr('href'));
            });

            callback(null, urls);
        }
    ], function (error, urls) {
        callback(error, urls);
    })

};

module.exports.getKills = function (url, callback) {
    var self = this;
    var logger = applicationStorage.logger;

    async.waterfall([
            function (callback) {
                self.getWoWProgressPage(url, function (error, body) {
                    callback(error, body)
                })
            },
            function (body, callback) {

                var $ = cheerio.load(body);

                var armoryUrl = decodeURIComponent(($('.armoryLink').attr('href')));

                if (armoryUrl == "undefined") {
                    return callback(new Error('Armory link undefined'));
                }
                try {
                    var name = $('h1').text().match("“(.*)” WoW Guild")[1];
                    var realm = armoryUrl.match('battle.net/wow/guild/(.*)/(.*)/')[1];
                    var region = armoryUrl.match('http://(.*).battle.net/wow/guild/')[1];
                } catch (e) {
                    logger.info("Parsing error, trying CN");
                    try {
                        var name = $('h1').text().match("“(.*)” WoW Guild")[1];
                        var realm = $('.realm').text().substring(3);
                        //var realm = armoryUrl.match('www.battlenet.com.cn/wow/guild/(.*)/(.*)/')[1];
                        var region = armoryUrl.match('http://www.battlenet.com.(.*)/wow/guild/')[1];
                    } catch (e) {
                        logger.error("Error on page parsing %s", url);
                        callback(true);
                    }
                }

                bnetAPI.getGuild(region, realm, name, [], function (error, guild) {
                    if (!guild || !guild.realm || !guild.name) {
                        logger.warn("Bnet return empty guild so try to update %s-%s-%s, skip it", region, realm, name);

                        //Bug on chinese ...
                        if (region == "cn") {
                            updateModel.insert("wp_pu", region, realm, name, 3, function () {
                                logger.verbose("Insert GuildProgress to update %s-%s-%s with priority %s", region, realm, name, 3);
                                callback(true);
                            });
                        } else {
                            callback(true);
                        }
                    } else {
                        callback(error, $, region, guild.realm, guild.name);
                    }
                });

            }
            ,
            function ($, region, realm, name, callback) {
                var kills = [];

                var bestTimestamp = 999999999999999999999;
                var normalCount = 0;
                var heroicCount = 0;
                var mythicCount = 0;
                var normalArchimonde = false;
                var heroicArchimonde = false;
                var mythicArchimonde = false;
                var normalArchimondeTimestamp = 0;
                var heroicArchimondeTimestamp = 0;
                var mythicArchimondeTimestamp = 0;

                $('table.rating a.boss_kills_link').each(function (i, elem) {

                    var kill = {region: region.toLowerCase(), realm: realm, name: name};

                    var bossName = $(this).text();

                    var bossArray = bossName.replace(/(^\+)/g, "").trim().split(':');
                    kill["boss"] = bossArray[1].trim();

                    if (bossArray[0] == 'N') {
                        kill["difficulty"] = 'normal';
                        normalCount++;
                    } else if (bossArray[0] == 'H') {
                        kill["difficulty"] = 'heroic'
                        heroicCount++;
                    } else if (bossArray[0] == 'M') {
                        kill["difficulty"] = 'mythic';
                        mythicCount++;
                    } else {
                        callback(new Error("WOWPROGRESS_PARSING_ERROR"));
                    }

                    if (kill["boss"] == "Xhul'horac") {
                        kill["boss"] = "Xhul'horac";
                    }
                    var date = $(this).parent().next().text();
                    var timestamp = new Date(date + " GMT+0000").getTime();

                    if (isNaN(timestamp)) {
                        timestamp = new Date(date.substring(1, date.length) + " GMT+0000").getTime();
                    }

                    if (isNaN(timestamp) || timestamp < 1388534400000) {
                        timestamp = parseInt($(this).parent().next().find('.datetime').attr('data-ts'), 10) * 1000;
                    }

                    //Fix incorrect kill ...
                    if (kill["boss"] == "Archimonde") {
                        bestTimestamp = timestamp;

                        if (kill["difficulty"] == "normal") {
                            normalArchimonde = true;
                            normalArchimondeTimestamp = timestamp;
                        } else if (kill["difficulty"] == "heroic") {
                            heroicArchimonde = true;
                            heroicArchimondeTimestamp = timestamp;
                        } else if (kill["difficulty"] == "mythic") {
                            mythicArchimonde = true;
                            mythicArchimondeTimestamp = timestamp;
                        }

                    }

                    if (timestamp > bestTimestamp) {
                        kill["timestamp"] = bestTimestamp;
                    } else {
                        kill["timestamp"] = timestamp;
                    }

                    if (kill["timestamp"] < new Date("Jun 01 2016 GMT+0000").getTime()) {
                        kills.push(kill);
                    }

                });

                var bossList = [
                    "Hellfire Assault",
                    "Iron Reaver",
                    "Kormrok", "Hellfire High Council",
                    "Kilrogg Deadeye",
                    "Gorefiend",
                    "Shadow-Lord Iskar",
                    "Socrethar the Eternal",
                    "Tyrant Velhari",
                    "Fel Lord Zakuun",
                    "Xhul'horac",
                    "Mannoroth",
                    "Archimonde"];

                if (normalArchimonde && normalCount < 13) {
                    var normalKills = [];
                    async.forEach(bossList, function (boss) {
                        var normalKill = {};
                        normalKill.boss = boss;
                        normalKill.difficulty = "normal";
                        normalKill.region = region.toLowerCase();
                        normalKill.realm = realm;
                        normalKill.name = name;
                        normalKill.timestamp = normalArchimonde;
                        normalKills.push(normalKill);

                    });
                    kills.concat(normalKills);

                }

                if (heroicArchimonde && heroicCount < 13) {
                    var heroicKills = [];
                    async.forEach(bossList, function (boss) {
                        var heroicKill = {};
                        heroicKill.boss = boss;
                        heroicKill.difficulty = "heroic";
                        heroicKill.region = region.toLowerCase();
                        heroicKill.realm = realm;
                        heroicKill.name = name;
                        heroicKill.timestamp = heroicArchimondeTimestamp;
                        heroicKills.push(heroicKill);

                    });
                    kills = kills.concat(heroicKills);
                }
                if (mythicArchimonde && mythicCount < 13) {
                    var mythicKills = [];
                    async.forEach(bossList, function (boss) {
                        var mythicKill = {};
                        mythicKill.boss = boss;
                        mythicKill.difficulty = "mythic";
                        mythicKill.region = region.toLowerCase();
                        mythicKill.realm = realm;
                        mythicKill.name = name;
                        mythicKill.timestamp = mythicArchimondeTimestamp;
                        mythicKills.push(mythicKill);
                    });
                    kills = kills.concat(mythicKills);
                }
                kills = _.uniqBy(kills, function (elem) {
                    return elem.boss + elem.difficulty;
                });
                callback(null, kills);
            }

        ],
        function (error, kills) {
            callback(error, kills);
        }
    );

};

<|endoftext|>


<commit_before>import * as _ from 'lodash'
import * as Immutable from 'immutable'
import * as React from 'react/addons'
import {State, Navigation} from 'react-router'

import RequirementSet from 'app/elements/requirementSet'

let cx = React.addons.classSet

let AreaOfStudy = React.createClass({
	mixins: [State, Navigation],

	propTypes: {
		area: React.PropTypes.shape({
			id: React.PropTypes.string.isRequired,
			title: React.PropTypes.string.isRequired,
			result: React.PropTypes.bool.isRequired,
			progress: React.PropTypes.shape({
				at: React.PropTypes.number.isRequired,
				of: React.PropTypes.number.isRequired,
				word: React.PropTypes.string,
			}).isRequired,
			type: React.PropTypes.string,
			details: React.PropTypes.arrayOf(React.PropTypes.object),
		})
	},

	toggle() {
		this.setState({expanded: !this.state.expanded})

		let query = this.getQuery()
		let sections = Immutable.Set(query.sections ? query.sections.split(',') : [])

		if (this.state.expanded) {
			sections = sections.delete(this.props.area.id)
		}
		else {
			sections = sections.add(this.props.area.id)
		}

		query.sections = sections.join(',')

		if (!query.sections.length) {
			delete query.sections
		}

		this.transitionTo('student', {id: this.getParams().id}, query)
	},

	componentWillReceiveProps(nextProps) {
		if (nextProps.initialExpansion !== undefined)
			this.setState({expanded: nextProps.initialExpansion})
	},

	getInitialState() {
		return { expanded: this.props.initialExpansion }
	},

	getDefaultProps() {
		return { initialExpansion: false }
	},

	render() {
		// console.log(`render areaOfStudy for ${this.props.area.id}`)

		let requirementSets = null;
		if (this.state.expanded)
			requirementSets = _.map(this.props.area.details, (reqset) =>
				React.createElement(RequirementSet, _.extend({key: reqset.title}, reqset)));

		let header = React.createElement('header',
			{className: 'summary', onClick: this.toggle},
			React.createElement('h1', null, this.props.area.title),
			React.createElement('progress', {
				value: this.props.area.progress.at,
				max: this.props.area.progress.of,
				className: this.props.area.progress.word,
			}));

		let classes = cx({
			'area-of-study': true,
			open: this.state.expanded,
		})

		return React.createElement('div',
			{key: this.props.area.id, className: classes},
			header,
			requirementSets)
	},
})

export default AreaOfStudy
<commit_msg>Don't reset params when transitioning in areaOfStudy<commit_after>import * as _ from 'lodash'
import * as Immutable from 'immutable'
import * as React from 'react/addons'
import {State, Navigation} from 'react-router'

import RequirementSet from 'app/elements/requirementSet'

let cx = React.addons.classSet

let AreaOfStudy = React.createClass({
	mixins: [State, Navigation],

	propTypes: {
		area: React.PropTypes.shape({
			id: React.PropTypes.string.isRequired,
			title: React.PropTypes.string.isRequired,
			result: React.PropTypes.bool.isRequired,
			progress: React.PropTypes.shape({
				at: React.PropTypes.number.isRequired,
				of: React.PropTypes.number.isRequired,
				word: React.PropTypes.string,
			}).isRequired,
			type: React.PropTypes.string,
			details: React.PropTypes.arrayOf(React.PropTypes.object),
		})
	},

	toggle() {
		this.setState({expanded: !this.state.expanded})

		let query = this.getQuery()
		let sections = Immutable.Set(query.sections ? query.sections.split(',') : [])

		if (this.state.expanded) {
			sections = sections.delete(this.props.area.id)
		}
		else {
			sections = sections.add(this.props.area.id)
		}

		query.sections = sections.join(',')

		if (!query.sections.length) {
			delete query.sections
		}

		this.transitionTo('student', this.getParams(), query)
	},

	componentWillReceiveProps(nextProps) {
		if (nextProps.initialExpansion !== undefined)
			this.setState({expanded: nextProps.initialExpansion})
	},

	getInitialState() {
		return { expanded: this.props.initialExpansion }
	},

	getDefaultProps() {
		return { initialExpansion: false }
	},

	render() {
		// console.log(`render areaOfStudy for ${this.props.area.id}`)

		let requirementSets = null;
		if (this.state.expanded)
			requirementSets = _.map(this.props.area.details, (reqset) =>
				React.createElement(RequirementSet, _.extend({key: reqset.title}, reqset)));

		let header = React.createElement('header',
			{className: 'summary', onClick: this.toggle},
			React.createElement('h1', null, this.props.area.title),
			React.createElement('progress', {
				value: this.props.area.progress.at,
				max: this.props.area.progress.of,
				className: this.props.area.progress.word,
			}));

		let classes = cx({
			'area-of-study': true,
			open: this.state.expanded,
		})

		return React.createElement('div',
			{key: this.props.area.id, className: classes},
			header,
			requirementSets)
	},
})

export default AreaOfStudy
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of lineFeature
 *
 * @class
 * @extends geo.lineFeature
 * @returns {geo.gl.lineFeature}
 */
//////////////////////////////////////////////////////////////////////////////
geo.gl.lineFeature = function (arg) {
  'use strict';
  if (!(this instanceof geo.gl.lineFeature)) {
    return new geo.gl.lineFeature(arg);
  }
  arg = arg || {};
  geo.lineFeature.call(this, arg);

  ////////////////////////////////////////////////////////////////////////////
  /**
   * @private
   */
  ////////////////////////////////////////////////////////////////////////////
  var m_this = this,
      s_exit = this._exit,
      m_actor = null,
      m_mapper = null,
      m_material = null,
      m_pixelWidthUnif = null,
      m_dynamicDraw = arg.dynamicDraw === undefined ? false : arg.dynamicDraw,
      s_init = this._init,
      s_update = this._update;

  function createVertexShader() {
    var vertexShaderSource = [
      '#ifdef GL_ES',
      '  precision highp float;',
      '#endif',
      'attribute vec3 pos;',
      'attribute vec3 prev;',
      'attribute vec3 next;',
      'attribute float offset;',

      'attribute vec3 strokeColor;',
      'attribute float strokeOpacity;',
      'attribute float strokeWidth;',

      'uniform mat4 modelViewMatrix;',
      'uniform mat4 projectionMatrix;',
      'uniform float pixelWidth;',

      'varying vec3 strokeColorVar;',
      'varying float strokeWidthVar;',
      'varying float strokeOpacityVar;',

      'void main(void)',
      '{',
      /* If any vertex has been deliberately set to a negative opacity,
       * skip doing computations on it. */
      '  if (strokeOpacity < 0.0) {',
      '    gl_Position = vec4(2, 2, 0, 1);',
      '    return;',
      '  }',
      '  const float PI = 3.14159265358979323846264;',
      '  vec4 worldPos = projectionMatrix * modelViewMatrix * vec4(pos.xyz, 1);',
      '  if (worldPos.w != 0.0) {',
      '    worldPos = worldPos/worldPos.w;',
      '  }',
      '  vec4 worldNext = projectionMatrix * modelViewMatrix * vec4(next.xyz, 1);',
      '  if (worldNext.w != 0.0) {',
      '    worldNext = worldNext/worldNext.w;',
      '  }',
      '  vec4 worldPrev = projectionMatrix* modelViewMatrix * vec4(prev.xyz, 1);',
      '  if (worldPrev.w != 0.0) {',
      '    worldPrev = worldPrev/worldPrev.w;',
      '  }',
      '  strokeColorVar = strokeColor;',
      '  strokeWidthVar = strokeWidth;',
      '  strokeOpacityVar = strokeOpacity;',
      '  vec2 deltaNext = worldNext.xy - worldPos.xy;',
      '  vec2 deltaPrev = worldPos.xy - worldPrev.xy;',
      '  float angleNext = PI * 0.5;',
      '  if (deltaNext.y < 0.0) { angleNext = -angleNext; } ',
      '  if (deltaNext.x != 0.0) {',
      '    angleNext = atan(deltaNext.y, deltaNext.x);',
      '  }',
      '  float anglePrev = PI * 0.5;',
      '  if (deltaPrev.y < 0.0) { anglePrev = -anglePrev; } ',
      '  if (deltaPrev.x != 0.0) {',
      '    anglePrev = atan(deltaPrev.y, deltaPrev.x);',
      '  }',
      '  if (deltaPrev.xy == vec2(0.0, 0.0)) anglePrev = angleNext;',
      '  if (deltaNext.xy == vec2(0.0, 0.0)) angleNext = anglePrev;',
      '  float angle = (anglePrev + angleNext) / 2.0;',
      '  float cosAngle = cos(anglePrev - angle);',
      '  if (cosAngle < 0.1) { cosAngle = sign(cosAngle) * 1.0; angle = 0.0; }',
      '  float distance = (offset * strokeWidth * pixelWidth) /',
      '                    cosAngle;',
      '  worldPos.x += distance * sin(angle);',
      '  worldPos.y -= distance * cos(angle);',
      '  gl_Position = worldPos;',
      '}'
    ].join('\n'),
    shader = new vgl.shader(gl.VERTEX_SHADER);
    shader.setShaderSource(vertexShaderSource);
    return shader;
  }

  function createFragmentShader() {
    var fragmentShaderSource = [
      '#ifdef GL_ES',
      '  precision highp float;',
      '#endif',
      'varying vec3 strokeColorVar;',
      'varying float strokeWidthVar;',
      'varying float strokeOpacityVar;',
      'void main () {',
      '  gl_FragColor = vec4 (strokeColorVar, strokeOpacityVar);',
      '}'
    ].join('\n'),
    shader = new vgl.shader(gl.FRAGMENT_SHADER);
    shader.setShaderSource(fragmentShaderSource);
    return shader;
  }

  function createGLLines() {
    var data = m_this.data(),
        i, j, k, v,
        numSegments = 0,
        lineItem, lineItemData,
        vert = [{}, {}], vertTemp,
        pos, posIdx3,
        position = [],
        posFunc = m_this.position(),
        strkWidthFunc = m_this.style.get('strokeWidth'),
        strkColorFunc = m_this.style.get('strokeColor'),
        strkOpacityFunc = m_this.style.get('strokeOpacity'),
        order = m_this.featureVertices(),
        buffers,
        posBuf, nextBuf, prevBuf, offsetBuf, indicesBuf,
        strokeWidthBuf, strokeColorBuf, strokeOpacityBuf,
        dest, dest3,
        geom = m_mapper.geometryData();

    for (i = 0; i < data.length; i += 1) {
      lineItem = m_this.line()(data[i], i);
      numSegments += lineItem.length - 1;
      for (j = 0; j < lineItem.length; j += 1) {
        pos = posFunc(lineItem[j], j, lineItem, i);
        if (pos instanceof geo.latlng) {
          position.push(pos.x());
          position.push(pos.y());
          position.push(0.0);
        } else {
          position.push(pos.x);
          position.push(pos.y);
          position.push(pos.z || 0.0);
        }
      }
    }

    position = geo.transform.transformCoordinates(
                 m_this.gcs(), m_this.layer().map().gcs(),
                 position, 3);

    buffers = vgl.DataBuffers(numSegments * order.length);
    posBuf           = buffers.create('pos', 3);
    nextBuf          = buffers.create('next', 3);
    prevBuf          = buffers.create('prev', 3);
    offsetBuf        = buffers.create('offset', 1);
    indicesBuf       = buffers.create('indices', 1);
    strokeWidthBuf   = buffers.create('strokeWidth', 1);
    strokeColorBuf   = buffers.create('strokeColor', 3);
    strokeOpacityBuf = buffers.create('strokeOpacity', 1);

    for (i = posIdx3 = dest = dest3 = 0; i < data.length; i += 1) {
      lineItem = m_this.line()(data[i], i);
      for (j = 0; j < lineItem.length; j += 1, posIdx3 += 3) {
        lineItemData = lineItem[j];
        /* swap entries in vert so that vert[0] is the first vertex, and
         * vert[1] will be reused for the second vertex */
        if (j) {
          vertTemp = vert[0];
          vert[0] = vert[1];
          vert[1] = vertTemp;
        }
        vert[1].pos = posIdx3;
        vert[1].prev = posIdx3 - (j ? 3 : 0);
        vert[1].next = posIdx3 + (j + 1 < lineItem.length ? 3 : 0);
        vert[1].strokeWidth = strkWidthFunc(lineItemData, j, lineItem, i);
        vert[1].strokeColor = strkColorFunc(lineItemData, j, lineItem, i);
        vert[1].strokeOpacity = strkOpacityFunc(lineItemData, j, lineItem, i);
        if (!j) {
          continue;
        }
        for (k = 0; k < order.length; k += 1, dest += 1, dest3 += 3) {
          v = vert[order[k][0]];
          posBuf[dest3]     = position[v.pos];
          posBuf[dest3 + 1] = position[v.pos + 1];
          posBuf[dest3 + 2] = position[v.pos + 2];
          prevBuf[dest3]     = position[v.prev];
          prevBuf[dest3 + 1] = position[v.prev + 1];
          prevBuf[dest3 + 2] = position[v.prev + 2];
          nextBuf[dest3]     = position[v.next];
          nextBuf[dest3 + 1] = position[v.next + 1];
          nextBuf[dest3 + 2] = position[v.next + 2];
          offsetBuf[dest] = order[k][1];
          indicesBuf[dest] = dest;
          strokeWidthBuf[dest] = v.strokeWidth;
          strokeColorBuf[dest3]     = v.strokeColor.r;
          strokeColorBuf[dest3 + 1] = v.strokeColor.g;
          strokeColorBuf[dest3 + 2] = v.strokeColor.b;
          strokeOpacityBuf[dest] = v.strokeOpacity;
        }
      }
    }

    geom.sourceByName('pos').setData(buffers.get('pos'));
    geom.sourceByName('prev').setData(buffers.get('prev'));
    geom.sourceByName('next').setData(buffers.get('next'));
    geom.sourceByName('strokeWidth').setData(buffers.get('strokeWidth'));
    geom.sourceByName('strokeColor').setData(buffers.get('strokeColor'));
    geom.sourceByName('strokeOpacity').setData(buffers.get('strokeOpacity'));
    geom.sourceByName('offset').setData(buffers.get('offset'));
    geom.primitive(0).setIndices(buffers.get('indices'));
    geom.boundsDirty(true);
    m_mapper.modified();
    m_mapper.boundsDirtyTimestamp().modified();
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return the arrangement of vertices used for each line segment.
   *
   * @returns {Number}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.featureVertices = function () {
    return [[0, 1], [1, -1], [0, -1], [0, 1], [1, 1], [1, -1]];
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return the number of vertices used for each line segment.
   *
   * @returns {Number}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.verticesPerFeature = function () {
    return this.featureVertices().length;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Initialize
   */
  ////////////////////////////////////////////////////////////////////////////
  this._init = function (arg) {
    var prog = vgl.shaderProgram(),
        vs = createVertexShader(),
        fs = createFragmentShader(),
        // Vertex attributes
        posAttr = vgl.vertexAttribute('pos'),
        prvAttr = vgl.vertexAttribute('prev'),
        nxtAttr = vgl.vertexAttribute('next'),
        offAttr = vgl.vertexAttribute('offset'),
        strkWidthAttr = vgl.vertexAttribute('strokeWidth'),
        strkColorAttr = vgl.vertexAttribute('strokeColor'),
        strkOpacityAttr = vgl.vertexAttribute('strokeOpacity'),
        // Shader uniforms
        mviUnif = new vgl.modelViewUniform('modelViewMatrix'),
        prjUnif = new vgl.projectionUniform('projectionMatrix'),
        geom = vgl.geometryData(),
        // Sources
        posData = vgl.sourceDataP3fv({'name': 'pos'}),
        prvPosData = vgl.sourceDataAnyfv(
            3, vgl.vertexAttributeKeysIndexed.Four, {'name': 'prev'}),
        nxtPosData = vgl.sourceDataAnyfv(
            3, vgl.vertexAttributeKeysIndexed.Five, {'name': 'next'}),
        offPosData = vgl.sourceDataAnyfv(
            1, vgl.vertexAttributeKeysIndexed.Six, {'name': 'offset'}),
        strkWidthData = vgl.sourceDataAnyfv(
            1, vgl.vertexAttributeKeysIndexed.One, {'name': 'strokeWidth'}),
        strkColorData = vgl.sourceDataAnyfv(
            3, vgl.vertexAttributeKeysIndexed.Two, {'name': 'strokeColor'}),
        strkOpacityData = vgl.sourceDataAnyfv(
            1, vgl.vertexAttributeKeysIndexed.Three,
            {'name': 'strokeOpacity'}),
        // Primitive indices
        triangles = vgl.triangles();

    m_pixelWidthUnif =  new vgl.floatUniform('pixelWidth',
                          1.0 / m_this.renderer().width());
    s_init.call(m_this, arg);
    m_material = vgl.material();
    m_mapper = vgl.mapper({dynamicDraw: m_dynamicDraw});

    prog.addVertexAttribute(posAttr, vgl.vertexAttributeKeys.Position);
    prog.addVertexAttribute(strkWidthAttr, vgl.vertexAttributeKeysIndexed.One);
    prog.addVertexAttribute(strkColorAttr, vgl.vertexAttributeKeysIndexed.Two);
    prog.addVertexAttribute(strkOpacityAttr, vgl.vertexAttributeKeysIndexed.Three);
    prog.addVertexAttribute(prvAttr, vgl.vertexAttributeKeysIndexed.Four);
    prog.addVertexAttribute(nxtAttr, vgl.vertexAttributeKeysIndexed.Five);
    prog.addVertexAttribute(offAttr, vgl.vertexAttributeKeysIndexed.Six);

    prog.addUniform(mviUnif);
    prog.addUniform(prjUnif);
    prog.addUniform(m_pixelWidthUnif);

    prog.addShader(fs);
    prog.addShader(vs);

    m_material.addAttribute(prog);
    m_material.addAttribute(vgl.blend());

    m_actor = vgl.actor();
    m_actor.setMaterial(m_material);
    m_actor.setMapper(m_mapper);

    geom.addSource(posData);
    geom.addSource(prvPosData);
    geom.addSource(nxtPosData);
    geom.addSource(strkWidthData);
    geom.addSource(strkColorData);
    geom.addSource(strkOpacityData);
    geom.addSource(offPosData);
    geom.addPrimitive(triangles);
    m_mapper.setGeometryData(geom);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return list of actors
   *
   * @returns {vgl.actor[]}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.actors = function () {
    if (!m_actor) {
      return [];
    }
    return [m_actor];
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Build
   *
   * @override
   */
  ////////////////////////////////////////////////////////////////////////////
  this._build = function () {
    if (m_actor) {
      m_this.renderer().contextRenderer().removeActor(m_actor);
    }

    createGLLines();

    m_this.renderer().contextRenderer().addActor(m_actor);
    m_this.buildTime().modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Update
   *
   * @override
   */
  ////////////////////////////////////////////////////////////////////////////
  this._update = function () {
    s_update.call(m_this);

    if (m_this.dataTime().getMTime() >= m_this.buildTime().getMTime() ||
        m_this.updateTime().getMTime() <= m_this.getMTime()) {
      m_this._build();
    }

    m_pixelWidthUnif.set(1.0 / m_this.renderer().width());
    m_actor.setVisible(m_this.visible());
    m_actor.material().setBinNumber(m_this.bin());
    m_this.updateTime().modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Destroy
   */
  ////////////////////////////////////////////////////////////////////////////
  this._exit = function () {
    m_this.renderer().contextRenderer().removeActor(m_actor);
    s_exit();
  };

  this._init(arg);
  return this;
};

inherit(geo.gl.lineFeature, geo.lineFeature);

// Now register it
geo.registerFeature('vgl', 'line', geo.gl.lineFeature);
<commit_msg>Fixed a style complaint from jscs.<commit_after>//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of lineFeature
 *
 * @class
 * @extends geo.lineFeature
 * @returns {geo.gl.lineFeature}
 */
//////////////////////////////////////////////////////////////////////////////
geo.gl.lineFeature = function (arg) {
  'use strict';
  if (!(this instanceof geo.gl.lineFeature)) {
    return new geo.gl.lineFeature(arg);
  }
  arg = arg || {};
  geo.lineFeature.call(this, arg);

  ////////////////////////////////////////////////////////////////////////////
  /**
   * @private
   */
  ////////////////////////////////////////////////////////////////////////////
  var m_this = this,
      s_exit = this._exit,
      m_actor = null,
      m_mapper = null,
      m_material = null,
      m_pixelWidthUnif = null,
      m_dynamicDraw = arg.dynamicDraw === undefined ? false : arg.dynamicDraw,
      s_init = this._init,
      s_update = this._update;

  function createVertexShader() {
    var vertexShaderSource = [
      '#ifdef GL_ES',
      '  precision highp float;',
      '#endif',
      'attribute vec3 pos;',
      'attribute vec3 prev;',
      'attribute vec3 next;',
      'attribute float offset;',

      'attribute vec3 strokeColor;',
      'attribute float strokeOpacity;',
      'attribute float strokeWidth;',

      'uniform mat4 modelViewMatrix;',
      'uniform mat4 projectionMatrix;',
      'uniform float pixelWidth;',

      'varying vec3 strokeColorVar;',
      'varying float strokeWidthVar;',
      'varying float strokeOpacityVar;',

      'void main(void)',
      '{',
      /* If any vertex has been deliberately set to a negative opacity,
       * skip doing computations on it. */
      '  if (strokeOpacity < 0.0) {',
      '    gl_Position = vec4(2, 2, 0, 1);',
      '    return;',
      '  }',
      '  const float PI = 3.14159265358979323846264;',
      '  vec4 worldPos = projectionMatrix * modelViewMatrix * vec4(pos.xyz, 1);',
      '  if (worldPos.w != 0.0) {',
      '    worldPos = worldPos/worldPos.w;',
      '  }',
      '  vec4 worldNext = projectionMatrix * modelViewMatrix * vec4(next.xyz, 1);',
      '  if (worldNext.w != 0.0) {',
      '    worldNext = worldNext/worldNext.w;',
      '  }',
      '  vec4 worldPrev = projectionMatrix* modelViewMatrix * vec4(prev.xyz, 1);',
      '  if (worldPrev.w != 0.0) {',
      '    worldPrev = worldPrev/worldPrev.w;',
      '  }',
      '  strokeColorVar = strokeColor;',
      '  strokeWidthVar = strokeWidth;',
      '  strokeOpacityVar = strokeOpacity;',
      '  vec2 deltaNext = worldNext.xy - worldPos.xy;',
      '  vec2 deltaPrev = worldPos.xy - worldPrev.xy;',
      '  float angleNext = PI * 0.5;',
      '  if (deltaNext.y < 0.0) { angleNext = -angleNext; } ',
      '  if (deltaNext.x != 0.0) {',
      '    angleNext = atan(deltaNext.y, deltaNext.x);',
      '  }',
      '  float anglePrev = PI * 0.5;',
      '  if (deltaPrev.y < 0.0) { anglePrev = -anglePrev; } ',
      '  if (deltaPrev.x != 0.0) {',
      '    anglePrev = atan(deltaPrev.y, deltaPrev.x);',
      '  }',
      '  if (deltaPrev.xy == vec2(0.0, 0.0)) anglePrev = angleNext;',
      '  if (deltaNext.xy == vec2(0.0, 0.0)) angleNext = anglePrev;',
      '  float angle = (anglePrev + angleNext) / 2.0;',
      '  float cosAngle = cos(anglePrev - angle);',
      '  if (cosAngle < 0.1) { cosAngle = sign(cosAngle) * 1.0; angle = 0.0; }',
      '  float distance = (offset * strokeWidth * pixelWidth) /',
      '                    cosAngle;',
      '  worldPos.x += distance * sin(angle);',
      '  worldPos.y -= distance * cos(angle);',
      '  gl_Position = worldPos;',
      '}'
    ].join('\n'),
    shader = new vgl.shader(gl.VERTEX_SHADER);
    shader.setShaderSource(vertexShaderSource);
    return shader;
  }

  function createFragmentShader() {
    var fragmentShaderSource = [
      '#ifdef GL_ES',
      '  precision highp float;',
      '#endif',
      'varying vec3 strokeColorVar;',
      'varying float strokeWidthVar;',
      'varying float strokeOpacityVar;',
      'void main () {',
      '  gl_FragColor = vec4 (strokeColorVar, strokeOpacityVar);',
      '}'
    ].join('\n'),
    shader = new vgl.shader(gl.FRAGMENT_SHADER);
    shader.setShaderSource(fragmentShaderSource);
    return shader;
  }

  function createGLLines() {
    var data = m_this.data(),
        i, j, k, v,
        numSegments = 0,
        lineItem, lineItemData,
        vert = [{}, {}], vertTemp,
        pos, posIdx3,
        position = [],
        posFunc = m_this.position(),
        strkWidthFunc = m_this.style.get('strokeWidth'),
        strkColorFunc = m_this.style.get('strokeColor'),
        strkOpacityFunc = m_this.style.get('strokeOpacity'),
        order = m_this.featureVertices(),
        buffers,
        posBuf, nextBuf, prevBuf, offsetBuf, indicesBuf,
        strokeWidthBuf, strokeColorBuf, strokeOpacityBuf,
        dest, dest3,
        geom = m_mapper.geometryData();

    for (i = 0; i < data.length; i += 1) {
      lineItem = m_this.line()(data[i], i);
      numSegments += lineItem.length - 1;
      for (j = 0; j < lineItem.length; j += 1) {
        pos = posFunc(lineItem[j], j, lineItem, i);
        if (pos instanceof geo.latlng) {
          position.push(pos.x());
          position.push(pos.y());
          position.push(0.0);
        } else {
          position.push(pos.x);
          position.push(pos.y);
          position.push(pos.z || 0.0);
        }
      }
    }

    position = geo.transform.transformCoordinates(
                 m_this.gcs(), m_this.layer().map().gcs(),
                 position, 3);

    buffers = vgl.DataBuffers(numSegments * order.length);
    posBuf           = buffers.create('pos', 3);
    nextBuf          = buffers.create('next', 3);
    prevBuf          = buffers.create('prev', 3);
    offsetBuf        = buffers.create('offset', 1);
    indicesBuf       = buffers.create('indices', 1);
    strokeWidthBuf   = buffers.create('strokeWidth', 1);
    strokeColorBuf   = buffers.create('strokeColor', 3);
    strokeOpacityBuf = buffers.create('strokeOpacity', 1);

    for (i = posIdx3 = dest = dest3 = 0; i < data.length; i += 1) {
      lineItem = m_this.line()(data[i], i);
      for (j = 0; j < lineItem.length; j += 1, posIdx3 += 3) {
        lineItemData = lineItem[j];
        /* swap entries in vert so that vert[0] is the first vertex, and
         * vert[1] will be reused for the second vertex */
        if (j) {
          vertTemp = vert[0];
          vert[0] = vert[1];
          vert[1] = vertTemp;
        }
        vert[1].pos = posIdx3;
        vert[1].prev = posIdx3 - (j ? 3 : 0);
        vert[1].next = posIdx3 + (j + 1 < lineItem.length ? 3 : 0);
        vert[1].strokeWidth = strkWidthFunc(lineItemData, j, lineItem, i);
        vert[1].strokeColor = strkColorFunc(lineItemData, j, lineItem, i);
        vert[1].strokeOpacity = strkOpacityFunc(lineItemData, j, lineItem, i);
        if (j) {
          for (k = 0; k < order.length; k += 1, dest += 1, dest3 += 3) {
            v = vert[order[k][0]];
            posBuf[dest3]     = position[v.pos];
            posBuf[dest3 + 1] = position[v.pos + 1];
            posBuf[dest3 + 2] = position[v.pos + 2];
            prevBuf[dest3]     = position[v.prev];
            prevBuf[dest3 + 1] = position[v.prev + 1];
            prevBuf[dest3 + 2] = position[v.prev + 2];
            nextBuf[dest3]     = position[v.next];
            nextBuf[dest3 + 1] = position[v.next + 1];
            nextBuf[dest3 + 2] = position[v.next + 2];
            offsetBuf[dest] = order[k][1];
            indicesBuf[dest] = dest;
            strokeWidthBuf[dest] = v.strokeWidth;
            strokeColorBuf[dest3]     = v.strokeColor.r;
            strokeColorBuf[dest3 + 1] = v.strokeColor.g;
            strokeColorBuf[dest3 + 2] = v.strokeColor.b;
            strokeOpacityBuf[dest] = v.strokeOpacity;
          }
        }
      }
    }

    geom.sourceByName('pos').setData(buffers.get('pos'));
    geom.sourceByName('prev').setData(buffers.get('prev'));
    geom.sourceByName('next').setData(buffers.get('next'));
    geom.sourceByName('strokeWidth').setData(buffers.get('strokeWidth'));
    geom.sourceByName('strokeColor').setData(buffers.get('strokeColor'));
    geom.sourceByName('strokeOpacity').setData(buffers.get('strokeOpacity'));
    geom.sourceByName('offset').setData(buffers.get('offset'));
    geom.primitive(0).setIndices(buffers.get('indices'));
    geom.boundsDirty(true);
    m_mapper.modified();
    m_mapper.boundsDirtyTimestamp().modified();
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return the arrangement of vertices used for each line segment.
   *
   * @returns {Number}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.featureVertices = function () {
    return [[0, 1], [1, -1], [0, -1], [0, 1], [1, 1], [1, -1]];
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return the number of vertices used for each line segment.
   *
   * @returns {Number}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.verticesPerFeature = function () {
    return this.featureVertices().length;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Initialize
   */
  ////////////////////////////////////////////////////////////////////////////
  this._init = function (arg) {
    var prog = vgl.shaderProgram(),
        vs = createVertexShader(),
        fs = createFragmentShader(),
        // Vertex attributes
        posAttr = vgl.vertexAttribute('pos'),
        prvAttr = vgl.vertexAttribute('prev'),
        nxtAttr = vgl.vertexAttribute('next'),
        offAttr = vgl.vertexAttribute('offset'),
        strkWidthAttr = vgl.vertexAttribute('strokeWidth'),
        strkColorAttr = vgl.vertexAttribute('strokeColor'),
        strkOpacityAttr = vgl.vertexAttribute('strokeOpacity'),
        // Shader uniforms
        mviUnif = new vgl.modelViewUniform('modelViewMatrix'),
        prjUnif = new vgl.projectionUniform('projectionMatrix'),
        geom = vgl.geometryData(),
        // Sources
        posData = vgl.sourceDataP3fv({'name': 'pos'}),
        prvPosData = vgl.sourceDataAnyfv(
            3, vgl.vertexAttributeKeysIndexed.Four, {'name': 'prev'}),
        nxtPosData = vgl.sourceDataAnyfv(
            3, vgl.vertexAttributeKeysIndexed.Five, {'name': 'next'}),
        offPosData = vgl.sourceDataAnyfv(
            1, vgl.vertexAttributeKeysIndexed.Six, {'name': 'offset'}),
        strkWidthData = vgl.sourceDataAnyfv(
            1, vgl.vertexAttributeKeysIndexed.One, {'name': 'strokeWidth'}),
        strkColorData = vgl.sourceDataAnyfv(
            3, vgl.vertexAttributeKeysIndexed.Two, {'name': 'strokeColor'}),
        strkOpacityData = vgl.sourceDataAnyfv(
            1, vgl.vertexAttributeKeysIndexed.Three,
            {'name': 'strokeOpacity'}),
        // Primitive indices
        triangles = vgl.triangles();

    m_pixelWidthUnif =  new vgl.floatUniform('pixelWidth',
                          1.0 / m_this.renderer().width());
    s_init.call(m_this, arg);
    m_material = vgl.material();
    m_mapper = vgl.mapper({dynamicDraw: m_dynamicDraw});

    prog.addVertexAttribute(posAttr, vgl.vertexAttributeKeys.Position);
    prog.addVertexAttribute(strkWidthAttr, vgl.vertexAttributeKeysIndexed.One);
    prog.addVertexAttribute(strkColorAttr, vgl.vertexAttributeKeysIndexed.Two);
    prog.addVertexAttribute(strkOpacityAttr, vgl.vertexAttributeKeysIndexed.Three);
    prog.addVertexAttribute(prvAttr, vgl.vertexAttributeKeysIndexed.Four);
    prog.addVertexAttribute(nxtAttr, vgl.vertexAttributeKeysIndexed.Five);
    prog.addVertexAttribute(offAttr, vgl.vertexAttributeKeysIndexed.Six);

    prog.addUniform(mviUnif);
    prog.addUniform(prjUnif);
    prog.addUniform(m_pixelWidthUnif);

    prog.addShader(fs);
    prog.addShader(vs);

    m_material.addAttribute(prog);
    m_material.addAttribute(vgl.blend());

    m_actor = vgl.actor();
    m_actor.setMaterial(m_material);
    m_actor.setMapper(m_mapper);

    geom.addSource(posData);
    geom.addSource(prvPosData);
    geom.addSource(nxtPosData);
    geom.addSource(strkWidthData);
    geom.addSource(strkColorData);
    geom.addSource(strkOpacityData);
    geom.addSource(offPosData);
    geom.addPrimitive(triangles);
    m_mapper.setGeometryData(geom);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return list of actors
   *
   * @returns {vgl.actor[]}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.actors = function () {
    if (!m_actor) {
      return [];
    }
    return [m_actor];
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Build
   *
   * @override
   */
  ////////////////////////////////////////////////////////////////////////////
  this._build = function () {
    if (m_actor) {
      m_this.renderer().contextRenderer().removeActor(m_actor);
    }

    createGLLines();

    m_this.renderer().contextRenderer().addActor(m_actor);
    m_this.buildTime().modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Update
   *
   * @override
   */
  ////////////////////////////////////////////////////////////////////////////
  this._update = function () {
    s_update.call(m_this);

    if (m_this.dataTime().getMTime() >= m_this.buildTime().getMTime() ||
        m_this.updateTime().getMTime() <= m_this.getMTime()) {
      m_this._build();
    }

    m_pixelWidthUnif.set(1.0 / m_this.renderer().width());
    m_actor.setVisible(m_this.visible());
    m_actor.material().setBinNumber(m_this.bin());
    m_this.updateTime().modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Destroy
   */
  ////////////////////////////////////////////////////////////////////////////
  this._exit = function () {
    m_this.renderer().contextRenderer().removeActor(m_actor);
    s_exit();
  };

  this._init(arg);
  return this;
};

inherit(geo.gl.lineFeature, geo.lineFeature);

// Now register it
geo.registerFeature('vgl', 'line', geo.gl.lineFeature);
<|endoftext|>


<commit_before>(function() {

  /* _FROM_SVG_START_ */
  function getColorStop(el) {
    var style = el.getAttribute('style'),
        offset = el.getAttribute('offset'),
        color, opacity;

    // convert percents to absolute values
    offset = parseFloat(offset) / (/%$/.test(offset) ? 100 : 1);

    if (style) {
      var keyValuePairs = style.split(/\s*;\s*/);

      if (keyValuePairs[keyValuePairs.length - 1] === '') {
        keyValuePairs.pop();
      }

      for (var i = keyValuePairs.length; i--; ) {

        var split = keyValuePairs[i].split(/\s*:\s*/),
            key = split[0].trim(),
            value = split[1].trim();

        if (key === 'stop-color') {
          color = value;
        }
        else if (key === 'stop-opacity') {
          opacity = value;
        }
      }
    }

    if (!color) {
      color = el.getAttribute('stop-color') || 'rgb(0,0,0)';
    }
    if (!opacity) {
      opacity = el.getAttribute('stop-opacity');
    }

    // convert rgba color to rgb color - alpha value has no affect in svg
    color = new fabric.Color(color).toRgb();

    return {
      offset: offset,
      color: color,
      opacity: isNaN(parseFloat(opacity)) ? 1 : parseFloat(opacity)
    };
  }

  function getLinearCoords(el) {
    return {
      x1: el.getAttribute('x1') || 0,
      y1: el.getAttribute('y1') || 0,
      x2: el.getAttribute('x2') || '100%',
      y2: el.getAttribute('y2') || 0
    };
  }

  function getRadialCoords(el) {
    return {
      x1: el.getAttribute('fx') || el.getAttribute('cx') || '50%',
      y1: el.getAttribute('fy') || el.getAttribute('cy') || '50%',
      r1: 0,
      x2: el.getAttribute('cx') || '50%',
      y2: el.getAttribute('cy') || '50%',
      r2: el.getAttribute('r') || '50%'
    };
  }
  /* _FROM_SVG_END_ */

  /**
   * Gradient class
   * @class fabric.Gradient
   * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#gradients}
   * @see {@link fabric.Gradient#initialize} for constructor definition
   */
  fabric.Gradient = fabric.util.createClass(/** @lends fabric.Gradient.prototype */ {
    /*
     * Stores the original position of the gradient when we convert from % to fixed values,
     * On objectBoundingBox case. We could you object.left and top, but those are  subject
     * to change, and we need the original value when exporting back to SVG.
     */
    origX: 0,

    /*
     * Stores the original position of the gradient when we convert from % to fixed values,
     * On objectBoundingBox case. We could you object.left and top, but those are  subject
     * to change, and we need the original value when exporting back to SVG.
     */
    origY: 0,

    /**
     * Constructor
     * @param {Object} [options] Options object with type, coords, gradientUnits and colorStops
     * @return {fabric.Gradient} thisArg
     */
    initialize: function(options) {
      options || (options = { });

      var coords = { };

      this.id = fabric.Object.__uid++;
      this.type = options.type || 'linear';

      coords = {
        x1: options.coords.x1 || 0,
        y1: options.coords.y1 || 0,
        x2: options.coords.x2 || 0,
        y2: options.coords.y2 || 0
      };

      if (this.type === 'radial') {
        coords.r1 = options.coords.r1 || 0;
        coords.r2 = options.coords.r2 || 0;
      }

      this.coords = coords;
      this.gradientUnits = options.gradientUnits || 'objectBoundingBox';
      this.colorStops = options.colorStops.slice();
      if (options.gradientTransform) {
        this.gradientTransform = options.gradientTransform;
      }
      this.origX = options.left;
      this.orgiY = options.top;
    },

    /**
     * Adds another colorStop
     * @param {Object} colorStop Object with offset and color
     * @return {fabric.Gradient} thisArg
     */
    addColorStop: function(colorStop) {
      for (var position in colorStop) {
        var color = new fabric.Color(colorStop[position]);
        this.colorStops.push({
          offset: position,
          color: color.toRgb(),
          opacity: color.getAlpha()
        });
      }
      return this;
    },

    /**
     * Returns object representation of a gradient
     * @return {Object}
     */
    toObject: function() {
      return {
        type: this.type,
        coords: this.coords,
        gradientUnits: this.gradientUnits,
        colorStops: this.colorStops
      };
    },

    /* _TO_SVG_START_ */
    /**
     * Returns SVG representation of an gradient
     * @param {Object} object Object to create a gradient for
     * @param {Boolean} normalize Whether coords should be normalized
     * @return {String} SVG representation of an gradient (linear/radial)
     */
    toSVG: function(object, normalize) {
      var coords = fabric.util.object.clone(this.coords),
          markup, commonAttributes;

      // colorStops must be sorted ascending
      this.colorStops.sort(function(a, b) {
        return a.offset - b.offset;
      });

      if (normalize && this.gradientUnits === 'userSpaceOnUse') {
        coords.x1 += object.width / 2;
        coords.y1 += object.height / 2;
        coords.x2 += object.width / 2;
        coords.y2 += object.height / 2;
      }
      else if (this.gradientUnits === 'objectBoundingBox') {
        _convertValuesToPercentUnits(object, coords);
      }
      commonAttributes = 'id="SVGID_' + this.id +
                     '" gradientUnits="' + this.gradientUnits + '"';
      if (this.gradientTransform) {
        commonAttributes += ' gradientTransform="matrix(' + this.gradientTransform.join(' ') + ')" ';
      }
      if (this.type === 'linear') {
        markup = [
          //jscs:disable validateIndentation
          '<linearGradient ',
            commonAttributes,
            ' x1="', coords.x1,
            '" y1="', coords.y1,
            '" x2="', coords.x2,
            '" y2="', coords.y2,
          '">\n'
          //jscs:enable validateIndentation
        ];
      }
      else if (this.type === 'radial') {
        markup = [
          //jscs:disable validateIndentation
          '<radialGradient ',
            commonAttributes,
            ' cx="', coords.x2,
            '" cy="', coords.y2,
            '" r="', coords.r2,
            '" fx="', coords.x1,
            '" fy="', coords.y1,
          '">\n'
          //jscs:enable validateIndentation
        ];
      }

      for (var i = 0; i < this.colorStops.length; i++) {
        markup.push(
          //jscs:disable validateIndentation
          '<stop ',
            'offset="', (this.colorStops[i].offset * 100) + '%',
            '" style="stop-color:', this.colorStops[i].color,
            (this.colorStops[i].opacity != null ? ';stop-opacity: ' + this.colorStops[i].opacity : ';'),
          '"/>\n'
          //jscs:enable validateIndentation
        );
      }

      markup.push((this.type === 'linear' ? '</linearGradient>\n' : '</radialGradient>\n'));

      return markup.join('');
    },
    /* _TO_SVG_END_ */

    /**
     * Returns an instance of CanvasGradient
     * @param {CanvasRenderingContext2D} ctx Context to render on
     * @return {CanvasGradient}
     */
    toLive: function(ctx) {
      var gradient;

      if (!this.type) {
        return;
      }

      if (this.type === 'linear') {
        gradient = ctx.createLinearGradient(
          this.coords.x1, this.coords.y1, this.coords.x2, this.coords.y2);
      }
      else if (this.type === 'radial') {
        gradient = ctx.createRadialGradient(
          this.coords.x1, this.coords.y1, this.coords.r1, this.coords.x2, this.coords.y2, this.coords.r2);
      }

      for (var i = 0, len = this.colorStops.length; i < len; i++) {
        var color = this.colorStops[i].color,
            opacity = this.colorStops[i].opacity,
            offset = this.colorStops[i].offset;

        if (typeof opacity !== 'undefined') {
          color = new fabric.Color(color).setAlpha(opacity).toRgba();
        }
        gradient.addColorStop(parseFloat(offset), color);
      }

      return gradient;
    }
  });

  fabric.util.object.extend(fabric.Gradient, {

    /* _FROM_SVG_START_ */
    /**
     * Returns {@link fabric.Gradient} instance from an SVG element
     * @static
     * @memberof fabric.Gradient
     * @param {SVGGradientElement} el SVG gradient element
     * @param {fabric.Object} instance
     * @return {fabric.Gradient} Gradient instance
     * @see http://www.w3.org/TR/SVG/pservers.html#LinearGradientElement
     * @see http://www.w3.org/TR/SVG/pservers.html#RadialGradientElement
     */
    fromElement: function(el, instance) {

      /**
       *  @example:
       *
       *  <linearGradient id="linearGrad1">
       *    <stop offset="0%" stop-color="white"/>
       *    <stop offset="100%" stop-color="black"/>
       *  </linearGradient>
       *
       *  OR
       *
       *  <linearGradient id="linearGrad2">
       *    <stop offset="0" style="stop-color:rgb(255,255,255)"/>
       *    <stop offset="1" style="stop-color:rgb(0,0,0)"/>
       *  </linearGradient>
       *
       *  OR
       *
       *  <radialGradient id="radialGrad1">
       *    <stop offset="0%" stop-color="white" stop-opacity="1" />
       *    <stop offset="50%" stop-color="black" stop-opacity="0.5" />
       *    <stop offset="100%" stop-color="white" stop-opacity="1" />
       *  </radialGradient>
       *
       *  OR
       *
       *  <radialGradient id="radialGrad2">
       *    <stop offset="0" stop-color="rgb(255,255,255)" />
       *    <stop offset="0.5" stop-color="rgb(0,0,0)" />
       *    <stop offset="1" stop-color="rgb(255,255,255)" />
       *  </radialGradient>
       *
       */

      var colorStopEls = el.getElementsByTagName('stop'),
          type = (el.nodeName === 'linearGradient' ? 'linear' : 'radial'),
          gradientUnits = el.getAttribute('gradientUnits') || 'objectBoundingBox',
          gradientTransform = el.getAttribute('gradientTransform'),
          colorStops = [],
          coords = { };

      if (type === 'linear') {
        coords = getLinearCoords(el);
      }
      else if (type === 'radial') {
        coords = getRadialCoords(el);
      }

      for (var i = colorStopEls.length; i--; ) {
        colorStops.push(getColorStop(colorStopEls[i]));
      }

      _convertPercentUnitsToValues(instance, coords);

      var gradient = new fabric.Gradient({
        type: type,
        coords: coords,
        gradientUnits: gradientUnits,
        colorStops: colorStops
      });

      if (gradientTransform) {
        gradient.gradientTransform = fabric.parseTransformAttribute(gradientTransform);
      }

      return gradient;
    },
    /* _FROM_SVG_END_ */

    /**
     * Returns {@link fabric.Gradient} instance from its object representation
     * @static
     * @memberof fabric.Gradient
     * @param {Object} obj
     * @param {Object} [options] Options object
     */
    forObject: function(obj, options) {
      options || (options = { });
      _convertPercentUnitsToValues(obj, options);
      return new fabric.Gradient(options);
    }
  });

  /**
   * @private
   */
  function _convertPercentUnitsToValues(object, options) {
    for (var prop in options) {
      if (typeof options[prop] === 'string' && /^\d+%$/.test(options[prop])) {
        var percents = parseFloat(options[prop], 10);
        if (prop === 'x1' || prop === 'x2' || prop === 'r2') {
          options[prop] = fabric.util.toFixed(object.width * percents / 100, 2)  + object.left;
        }
        else if (prop === 'y1' || prop === 'y2') {
          options[prop] = fabric.util.toFixed(object.height * percents / 100, 2) + object.top;
        }
      }
    }
  }

  /* _TO_SVG_START_ */
  /**
   * @private
   */
  function _convertValuesToPercentUnits(object, options) {
    for (var prop in options) {
      //convert to percent units
      if (prop === 'x1' || prop === 'x2' || prop === 'r2') {
        options[prop] = fabric.util.toFixed((options[prop] - object.origX) / object.width * 100, 2) + '%';
      }
      else if (prop === 'y1' || prop === 'y2') {
        options[prop] = fabric.util.toFixed((options[prop] - object.origY) / object.height * 100, 2) + '%';
      }
    }
  }
  /* _TO_SVG_END_ */

})();
<commit_msg>Update gradient.class.js<commit_after>(function() {

  /* _FROM_SVG_START_ */
  function getColorStop(el) {
    var style = el.getAttribute('style'),
        offset = el.getAttribute('offset'),
        color, opacity;

    // convert percents to absolute values
    offset = parseFloat(offset) / (/%$/.test(offset) ? 100 : 1);

    if (style) {
      var keyValuePairs = style.split(/\s*;\s*/);

      if (keyValuePairs[keyValuePairs.length - 1] === '') {
        keyValuePairs.pop();
      }

      for (var i = keyValuePairs.length; i--; ) {

        var split = keyValuePairs[i].split(/\s*:\s*/),
            key = split[0].trim(),
            value = split[1].trim();

        if (key === 'stop-color') {
          color = value;
        }
        else if (key === 'stop-opacity') {
          opacity = value;
        }
      }
    }

    if (!color) {
      color = el.getAttribute('stop-color') || 'rgb(0,0,0)';
    }
    if (!opacity) {
      opacity = el.getAttribute('stop-opacity');
    }

    // convert rgba color to rgb color - alpha value has no affect in svg
    color = new fabric.Color(color).toRgb();

    return {
      offset: offset,
      color: color,
      opacity: isNaN(parseFloat(opacity)) ? 1 : parseFloat(opacity)
    };
  }

  function getLinearCoords(el) {
    return {
      x1: el.getAttribute('x1') || 0,
      y1: el.getAttribute('y1') || 0,
      x2: el.getAttribute('x2') || '100%',
      y2: el.getAttribute('y2') || 0
    };
  }

  function getRadialCoords(el) {
    return {
      x1: el.getAttribute('fx') || el.getAttribute('cx') || '50%',
      y1: el.getAttribute('fy') || el.getAttribute('cy') || '50%',
      r1: 0,
      x2: el.getAttribute('cx') || '50%',
      y2: el.getAttribute('cy') || '50%',
      r2: el.getAttribute('r') || '50%'
    };
  }
  /* _FROM_SVG_END_ */

  /**
   * Gradient class
   * @class fabric.Gradient
   * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#gradients}
   * @see {@link fabric.Gradient#initialize} for constructor definition
   */
  fabric.Gradient = fabric.util.createClass(/** @lends fabric.Gradient.prototype */ {
    /*
     * Stores the original position of the gradient when we convert from % to fixed values, for objectBoundingBox case.
     * @type Number
     * @default 0
     */
    origX: 0,

    /*
     * Stores the original position of the gradient when we convert from % to fixed values, for objectBoundingBox case.
     * @type Number
     * @default 0
     */
    origY: 0,

    /**
     * Constructor
     * @param {Object} [options] Options object with type, coords, gradientUnits and colorStops
     * @return {fabric.Gradient} thisArg
     */
    initialize: function(options) {
      options || (options = { });

      var coords = { };

      this.id = fabric.Object.__uid++;
      this.type = options.type || 'linear';

      coords = {
        x1: options.coords.x1 || 0,
        y1: options.coords.y1 || 0,
        x2: options.coords.x2 || 0,
        y2: options.coords.y2 || 0
      };

      if (this.type === 'radial') {
        coords.r1 = options.coords.r1 || 0;
        coords.r2 = options.coords.r2 || 0;
      }

      this.coords = coords;
      this.gradientUnits = options.gradientUnits || 'objectBoundingBox';
      this.colorStops = options.colorStops.slice();
      if (options.gradientTransform) {
        this.gradientTransform = options.gradientTransform;
      }
      this.origX = options.left;
      this.orgiY = options.top;
    },

    /**
     * Adds another colorStop
     * @param {Object} colorStop Object with offset and color
     * @return {fabric.Gradient} thisArg
     */
    addColorStop: function(colorStop) {
      for (var position in colorStop) {
        var color = new fabric.Color(colorStop[position]);
        this.colorStops.push({
          offset: position,
          color: color.toRgb(),
          opacity: color.getAlpha()
        });
      }
      return this;
    },

    /**
     * Returns object representation of a gradient
     * @return {Object}
     */
    toObject: function() {
      return {
        type: this.type,
        coords: this.coords,
        gradientUnits: this.gradientUnits,
        colorStops: this.colorStops
      };
    },

    /* _TO_SVG_START_ */
    /**
     * Returns SVG representation of an gradient
     * @param {Object} object Object to create a gradient for
     * @param {Boolean} normalize Whether coords should be normalized
     * @return {String} SVG representation of an gradient (linear/radial)
     */
    toSVG: function(object, normalize) {
      var coords = fabric.util.object.clone(this.coords),
          markup, commonAttributes;

      // colorStops must be sorted ascending
      this.colorStops.sort(function(a, b) {
        return a.offset - b.offset;
      });

      if (normalize && this.gradientUnits === 'userSpaceOnUse') {
        coords.x1 += object.width / 2;
        coords.y1 += object.height / 2;
        coords.x2 += object.width / 2;
        coords.y2 += object.height / 2;
      }
      else if (this.gradientUnits === 'objectBoundingBox') {
        _convertValuesToPercentUnits(object, coords);
      }
      commonAttributes = 'id="SVGID_' + this.id +
                     '" gradientUnits="' + this.gradientUnits + '"';
      if (this.gradientTransform) {
        commonAttributes += ' gradientTransform="matrix(' + this.gradientTransform.join(' ') + ')" ';
      }
      if (this.type === 'linear') {
        markup = [
          //jscs:disable validateIndentation
          '<linearGradient ',
            commonAttributes,
            ' x1="', coords.x1,
            '" y1="', coords.y1,
            '" x2="', coords.x2,
            '" y2="', coords.y2,
          '">\n'
          //jscs:enable validateIndentation
        ];
      }
      else if (this.type === 'radial') {
        markup = [
          //jscs:disable validateIndentation
          '<radialGradient ',
            commonAttributes,
            ' cx="', coords.x2,
            '" cy="', coords.y2,
            '" r="', coords.r2,
            '" fx="', coords.x1,
            '" fy="', coords.y1,
          '">\n'
          //jscs:enable validateIndentation
        ];
      }

      for (var i = 0; i < this.colorStops.length; i++) {
        markup.push(
          //jscs:disable validateIndentation
          '<stop ',
            'offset="', (this.colorStops[i].offset * 100) + '%',
            '" style="stop-color:', this.colorStops[i].color,
            (this.colorStops[i].opacity != null ? ';stop-opacity: ' + this.colorStops[i].opacity : ';'),
          '"/>\n'
          //jscs:enable validateIndentation
        );
      }

      markup.push((this.type === 'linear' ? '</linearGradient>\n' : '</radialGradient>\n'));

      return markup.join('');
    },
    /* _TO_SVG_END_ */

    /**
     * Returns an instance of CanvasGradient
     * @param {CanvasRenderingContext2D} ctx Context to render on
     * @return {CanvasGradient}
     */
    toLive: function(ctx) {
      var gradient;

      if (!this.type) {
        return;
      }

      if (this.type === 'linear') {
        gradient = ctx.createLinearGradient(
          this.coords.x1, this.coords.y1, this.coords.x2, this.coords.y2);
      }
      else if (this.type === 'radial') {
        gradient = ctx.createRadialGradient(
          this.coords.x1, this.coords.y1, this.coords.r1, this.coords.x2, this.coords.y2, this.coords.r2);
      }

      for (var i = 0, len = this.colorStops.length; i < len; i++) {
        var color = this.colorStops[i].color,
            opacity = this.colorStops[i].opacity,
            offset = this.colorStops[i].offset;

        if (typeof opacity !== 'undefined') {
          color = new fabric.Color(color).setAlpha(opacity).toRgba();
        }
        gradient.addColorStop(parseFloat(offset), color);
      }

      return gradient;
    }
  });

  fabric.util.object.extend(fabric.Gradient, {

    /* _FROM_SVG_START_ */
    /**
     * Returns {@link fabric.Gradient} instance from an SVG element
     * @static
     * @memberof fabric.Gradient
     * @param {SVGGradientElement} el SVG gradient element
     * @param {fabric.Object} instance
     * @return {fabric.Gradient} Gradient instance
     * @see http://www.w3.org/TR/SVG/pservers.html#LinearGradientElement
     * @see http://www.w3.org/TR/SVG/pservers.html#RadialGradientElement
     */
    fromElement: function(el, instance) {

      /**
       *  @example:
       *
       *  <linearGradient id="linearGrad1">
       *    <stop offset="0%" stop-color="white"/>
       *    <stop offset="100%" stop-color="black"/>
       *  </linearGradient>
       *
       *  OR
       *
       *  <linearGradient id="linearGrad2">
       *    <stop offset="0" style="stop-color:rgb(255,255,255)"/>
       *    <stop offset="1" style="stop-color:rgb(0,0,0)"/>
       *  </linearGradient>
       *
       *  OR
       *
       *  <radialGradient id="radialGrad1">
       *    <stop offset="0%" stop-color="white" stop-opacity="1" />
       *    <stop offset="50%" stop-color="black" stop-opacity="0.5" />
       *    <stop offset="100%" stop-color="white" stop-opacity="1" />
       *  </radialGradient>
       *
       *  OR
       *
       *  <radialGradient id="radialGrad2">
       *    <stop offset="0" stop-color="rgb(255,255,255)" />
       *    <stop offset="0.5" stop-color="rgb(0,0,0)" />
       *    <stop offset="1" stop-color="rgb(255,255,255)" />
       *  </radialGradient>
       *
       */

      var colorStopEls = el.getElementsByTagName('stop'),
          type = (el.nodeName === 'linearGradient' ? 'linear' : 'radial'),
          gradientUnits = el.getAttribute('gradientUnits') || 'objectBoundingBox',
          gradientTransform = el.getAttribute('gradientTransform'),
          colorStops = [],
          coords = { };

      if (type === 'linear') {
        coords = getLinearCoords(el);
      }
      else if (type === 'radial') {
        coords = getRadialCoords(el);
      }

      for (var i = colorStopEls.length; i--; ) {
        colorStops.push(getColorStop(colorStopEls[i]));
      }

      _convertPercentUnitsToValues(instance, coords);

      var gradient = new fabric.Gradient({
        type: type,
        coords: coords,
        gradientUnits: gradientUnits,
        colorStops: colorStops
      });

      if (gradientTransform) {
        gradient.gradientTransform = fabric.parseTransformAttribute(gradientTransform);
      }

      return gradient;
    },
    /* _FROM_SVG_END_ */

    /**
     * Returns {@link fabric.Gradient} instance from its object representation
     * @static
     * @memberof fabric.Gradient
     * @param {Object} obj
     * @param {Object} [options] Options object
     */
    forObject: function(obj, options) {
      options || (options = { });
      _convertPercentUnitsToValues(obj, options);
      return new fabric.Gradient(options);
    }
  });

  /**
   * @private
   */
  function _convertPercentUnitsToValues(object, options) {
    for (var prop in options) {
      if (typeof options[prop] === 'string' && /^\d+%$/.test(options[prop])) {
        var percents = parseFloat(options[prop], 10);
        if (prop === 'x1' || prop === 'x2' || prop === 'r2') {
          options[prop] = fabric.util.toFixed(object.width * percents / 100, 2)  + object.left;
        }
        else if (prop === 'y1' || prop === 'y2') {
          options[prop] = fabric.util.toFixed(object.height * percents / 100, 2) + object.top;
        }
      }
    }
  }

  /* _TO_SVG_START_ */
  /**
   * @private
   */
  function _convertValuesToPercentUnits(object, options) {
    for (var prop in options) {
      //convert to percent units
      if (prop === 'x1' || prop === 'x2' || prop === 'r2') {
        options[prop] = fabric.util.toFixed((options[prop] - object.origX) / object.width * 100, 2) + '%';
      }
      else if (prop === 'y1' || prop === 'y2') {
        options[prop] = fabric.util.toFixed((options[prop] - object.origY) / object.height * 100, 2) + '%';
      }
    }
  }
  /* _TO_SVG_END_ */

})();
<|endoftext|>