<commit_before>"use strict"
/**
 * @fileoverview
 * @author @litzenberger rlitzenberger@solidearth.com (ron litzenberger)
 *
 */
var _ = require('underscore');
var util = require('util');
var path = require('path');
var uuid = require('node-uuid');
var fs = require('fs');

module.exports = function( options) {

	var seneca = this;
	var config = options;

	var noop = function () {};
	var plugin = "mongoDb";
	var MongoProvider = require('./lib/MongoProvider');
	var MongoStore = require('./lib/MongoStore');
	var dbClient,dbProvider;
	// init seneca plugins
	seneca.add('init:mongoDb',init);
	seneca.add({role : plugin,cmd : 'close'}, close);// store in a collection
	seneca.add({role : plugin,cmd : 'aggregateSave'}, aggregate);//
	seneca.add({role : plugin,cmd : 'insert'}, insert);//
	// store in a collection

	// initialize
	function init (args,cb) {
		var ertieneca = this;
		dbProvider= new MongoProvider(seneca.log);
		dbProvider.data(config.mongo_connection,config.mongo_options,function(err,db){
			if (err){cb(err)}
				dbClient = new MongoStore(db,seneca.log);
				return cb();
 		}); // end mongo provider
	}

	function close (args,cb) {
		var seneca = this;
		MongoProvider.close();
		cb();
	}

	function aggregate(args,cb){
		var seneca = this;
		//TODO use async.series
		seneca.log.debug("-- start listings agg --");
		eventClient.use("events",function(){
			eventClient.aggregate(args.class,args.start,args.daybefore,args.pipeline,function(err,results) {
				if(err){return cb(err)}
				statsClient.use("spring_daily",function(){
				statsClient.insertsite(results,function(err,action){
					if(err){cb(err);}
					console.log("stats insert")
					cb();
				})
				});

			});
		})
	}

/* function
* @param args.collection - collection to use
* @param args.doc - doc to be inserted
*/
	function insert(args,cb){
		var seneca = this;
		var collection=args.collection;
		seneca.log.debug("-- start insert --");
		dbClient.use(collection,function(){
			dbClient.insert(args.doc,function(err,results) {
				if(err){return cb(err)}
					console.log("stats insert")
					cb();
			})
		});
	}

/* function
* @param args.collection - collection to use
* @param args.doc - doc to be inserted
*/

	function store(data,cb){
		var seneca = this;
			eventClient.listing(function(err,results) {
				if(err){return cb(err)}
				seneca.log.debug(results);
				cb(null,results);
			});
	}
	return {
		name:plugin
	}

}<commit_msg>cleanup<commit_after>"use strict"
/**
 * @fileoverview
 * @author @litzenberger rlitzenberger@solidearth.com (ron litzenberger)
 *
 */
var _ = require('underscore');
var util = require('util');
var path = require('path');
var fs = require('fs');

module.exports = function( options) {

	var seneca = this;
	var config = options;

	var noop = function () {};
	var plugin = "mongoDb";
	var MongoProvider = require('./lib/MongoProvider');
	var MongoStore = require('./lib/MongoStore');
	var dbClient,dbProvider;
	// init seneca plugins
	seneca.add('init:mongoDb',init);
	seneca.add({role : plugin,cmd : 'close'}, close);// store in a collection
	seneca.add({role : plugin,cmd : 'aggregateSave'}, aggregate);//
	seneca.add({role : plugin,cmd : 'insert'}, insert);//
	// store in a collection

	// initialize
	function init (args,cb) {
		var ertieneca = this;
		dbProvider= new MongoProvider(seneca.log);
		dbProvider.data(config.mongo_connection,config.mongo_options,function(err,db){
			if (err){cb(err)}
				dbClient = new MongoStore(db,seneca.log);
				return cb();
 		}); // end mongo provider
	}

	function close (args,cb) {
		var seneca = this;
		MongoProvider.close();
		cb();
	}

	function aggregate(args,cb){
		var seneca = this;
		//TODO use async.series
		seneca.log.debug("-- start listings agg --");
		eventClient.use("events",function(){
			eventClient.aggregate(args.class,args.start,args.daybefore,args.pipeline,function(err,results) {
				if(err){return cb(err)}
				statsClient.use("spring_daily",function(){
				statsClient.insertsite(results,function(err,action){
					if(err){cb(err);}
					console.log("stats insert")
					cb();
				})
				});

			});
		})
	}

/* function
* @param args.collection - collection to use
* @param args.doc - doc to be inserted
*/
	function insert(args,cb){
		var seneca = this;
		var collection=args.collection;
		seneca.log.debug("-- start insert --");
		dbClient.use(collection,function(){
			dbClient.insert(args.doc,function(err,results) {
				if(err){return cb(err)}
					console.log("stats insert")
					cb();
			})
		});
	}

/* function
* @param args.collection - collection to use
* @param args.doc - doc to be inserted
*/

	function store(data,cb){
		var seneca = this;
			eventClient.listing(function(err,results) {
				if(err){return cb(err)}
				seneca.log.debug(results);
				cb(null,results);
			});
	}
	return {
		name:plugin
	}

}<|endoftext|>


<commit_before>"use strict";

/*
 */

var pollingInterval = 1000, // milliseconds
    runsTable;

var RunsTable = function($table, user, is_user_admin, $no_results, $active_filters, $navigation_links) {
    permissions.PermissionsTable.call(this, $table, is_user_admin, $navigation_links);
    this.$no_results = $no_results;
    var runsTable = this;

    this.user = user;

    this.filterSet = new permissions.FilterSet(
            $active_filters,
            function() {
                runsTable.page = 1;
                runsTable.reloadTable();
            });
    this.list_url = "/api/runs/status/";
    this.reload_interval = pollingInterval;

    this.create_url = "/api/runs/";

    this.registerColumn("Status", function($td, run) {
        $td.addClass("code").append($('')
                .attr('href', '/view_run/' + run.id)
                .text(run.run_progress.status));
    });

    this.registerColumn("Name", function($td, run) {
        var $name;
        if (run.id === undefined) {
            $name = $('');
        }
        else {
            $name = $('').attr("href", "view_results/" + run.id);
        }
        $td.append($name.text(run.display_name));
    });

    this.registerColumn("Start", function($td, run) {
        $td.text(run.run_progress.start || '-');
    });
    this.registerColumn("End", function($td, run) {
        $td.text(run.run_progress.end || '-');
    });

    this.registerStandardColumn("user");

    // This is a stop/rerun column.
    this.registerColumn(" ", function($td, run) {

        // A "rerun" link, accessible to anyone who can see this Run.
        var $rerun_link = $("");
        $rerun_link.attr("href", runsTable.create_url)
            .text("Rerun")
            .click({
                run: run,
                run_table: runsTable
            }, clickRerun);
        $td.append($rerun_link);

        if (run.stopped_by !== null) {
            $td.append(" (Stopped by user " + run.stopped_by + ")");
        }
        else if (run.run_progress.end === null &&
                (runsTable.user === run.user || ! runsTable.is_locked)) {
            var $stop_link = $("");
            $stop_link.attr("href", run.url)
                .attr("run_id", run.id)
                .text("Stop")
                .click(this, clickStop);
            $td.append(" (");
            $td.append($stop_link);
            $td.append(")");
        }
    });

    function clickStop(event) {
        var $a = $(this),
            run_id = $a.attr("run_id"),
            run_url = $a.attr("href"),
            run_table = event.data;
        event.preventDefault();
        $.getJSON(
            run_url,
            {},
            function () {
                var stop_message = "Are you sure you want to stop this run?";
                if (window.confirm(stop_message)) {
                    $.ajax(
                        {
                            url: run_url,
                            method: "PATCH",
                            data: {
                                is_stop_requested: true
                            },
                            success: function () {
                                run_table.reloadTable();
                            }
                        }
                    ).fail(
                        function (request) {
                            var response = request.responseJSON,
                                detail = (
                                    response ?
                                    response.detail :
                                    "Failed to redact"
                                );
                            window.alert(detail);
                        }
                    );
                }
            }
        );
    }

    function clickRerun(event) {
        var $a = $(this),
            run = event.data.run,
            run_table = event.data.run_table;

        event.preventDefault();

        $.getJSON(
            run_table.create_url,
            {},
            function () {
                $.ajax(
                    {
                        url: run_table.create_url,
                        method: "POST",
                        data: JSON.stringify({
                            pipeline: run.pipeline,
                            name: run.name,
                            description: run.description,
                            users_allowed: run.users_allowed,
                            groups_allowed: run.groups_allowed,
                            inputs: run.inputs
                        }),
                        contentType: "application/json",
                        processData: false,
                        success: function () {
                            run_table.reloadTable();
                        }
                    }
                ).fail(
                    function (request) {
                        var response = request.responseJSON,
                            detail = (
                                "Failed to rerun"
                            );
                        window.alert(detail);
                    }
                );
            }
        );
    }
};
RunsTable.prototype = Object.create(permissions.PermissionsTable.prototype);

RunsTable.prototype.getQueryParams = function() {
    var params = permissions.PermissionsTable.prototype.getQueryParams.call(this);
    params.filters = this.filterSet.getFilters();
    return params;
};

RunsTable.prototype.extractRows = function(response) {
    var $no_results = this.$no_results,
        runs;
    $no_results.empty();
    if ('detail' in response) {
        $no_results.append($('
Errors:
'));
        $no_results.append($('
').text(response.detail));
    } else {
        runs = response.results;
        if (runs !== undefined && runs.length > 0) {
            $no_results.hide();
            this.$table.children('caption').text(
                response.count + " matching runs"
            );
            return runs;
        }
        $no_results.html('
No runs match your query.
');
    }

    $no_results.show();
    return []; // no runs
};

$(function(){ // wait for page to finish loading before executing jQuery code
    // Security stuff to prevent cross-site scripting.
    noXSS();

    $('.advanced-filter').prepend('<input type="button" class="close ctrl" value="Close">');

    $('input[value="Advanced"]').on('click', function() {
        $(this).closest('.short-filter').fadeOut({ complete: function() {
            $(this).siblings('.advanced-filter').fadeIn()
                .closest('li').addClass('advanced');
        } });
    });

    $('.advanced-filter input.close.ctrl').on('click', function() {
        $(this).closest('.advanced-filter').fadeOut({ complete: function() {
            $(this).siblings('.short-filter').fadeIn()
                .closest('li').removeClass('advanced');
        } });
    });

    $('form.short-filter, form.advanced-filter').submit(function(e) {
        e.preventDefault();
        runsTable.filterSet.addFromForm(this);
    });

    runsTable = new RunsTable(
        $('#runs'),
        user,
        is_user_admin,
        $('.no_results'),
        $('#active_filters'),
        $(".navigation_links")
    );
    // runsTable.filterSet.add('active');
    runsTable.reloadTable();
});
<commit_msg>Fix #530 by removing unnecessary AJAX calls.<commit_after>"use strict";

/*
 */

var pollingInterval = 1000, // milliseconds
    runsTable;

var RunsTable = function($table, user, is_user_admin, $no_results, $active_filters, $navigation_links) {
    permissions.PermissionsTable.call(this, $table, is_user_admin, $navigation_links);
    this.$no_results = $no_results;
    var runsTable = this;

    this.user = user;

    this.filterSet = new permissions.FilterSet(
            $active_filters,
            function() {
                runsTable.page = 1;
                runsTable.reloadTable();
            });
    this.list_url = "/api/runs/status/";
    this.reload_interval = pollingInterval;

    this.create_url = "/api/runs/";

    this.registerColumn("Status", function($td, run) {
        $td.addClass("code").append($('')
                .attr('href', '/view_run/' + run.id)
                .text(run.run_progress.status));
    });

    this.registerColumn("Name", function($td, run) {
        var $name;
        if (run.id === undefined) {
            $name = $('');
        }
        else {
            $name = $('').attr("href", "view_results/" + run.id);
        }
        $td.append($name.text(run.display_name));
    });

    this.registerColumn("Start", function($td, run) {
        $td.text(run.run_progress.start || '-');
    });
    this.registerColumn("End", function($td, run) {
        $td.text(run.run_progress.end || '-');
    });

    this.registerStandardColumn("user");

    // This is a stop/rerun column.
    this.registerColumn(" ", function($td, run) {

        // A "rerun" link, accessible to anyone who can see this Run.
        var $rerun_link = $("");
        $rerun_link.attr("href", runsTable.create_url)
            .text("Rerun")
            .click({
                run: run,
                run_table: runsTable
            }, clickRerun);
        $td.append($rerun_link);

        if (run.stopped_by !== null) {
            $td.append(" (Stopped by user " + run.stopped_by + ")");
        }
        else if (run.run_progress.end === null &&
                (runsTable.user === run.user || ! runsTable.is_locked)) {
            var $stop_link = $("");
            $stop_link.attr("href", run.url)
                .attr("run_id", run.id)
                .text("Stop")
                .click(this, clickStop);
            $td.append(" (");
            $td.append($stop_link);
            $td.append(")");
        }
    });

    function clickStop(event) {
        var $a = $(this),
            run_id = $a.attr("run_id"),
            run_url = $a.attr("href"),
            run_table = event.data;
        event.preventDefault();
        var stop_message = "Are you sure you want to stop this run?";
        if (window.confirm(stop_message)) {
            $.ajax(
                {
                    url: run_url,
                    method: "PATCH",
                    data: {
                        is_stop_requested: true
                    },
                    success: function () {
                        run_table.reloadTable();
                    }
                }
            ).fail(
                function (request) {
                    var response = request.responseJSON,
                        detail = (
                            response ?
                            response.detail :
                            "Failed to redact"
                        );
                    window.alert(detail);
                }
            );
        }
    }

    function clickRerun(event) {
        var $a = $(this),
            run = event.data.run,
            run_table = event.data.run_table;

        event.preventDefault();

        $.ajax(
            {
                url: run_table.create_url,
                method: "POST",
                data: JSON.stringify({
                    pipeline: run.pipeline,
                    name: run.name,
                    description: run.description,
                    users_allowed: run.users_allowed,
                    groups_allowed: run.groups_allowed,
                    inputs: run.inputs
                }),
                contentType: "application/json",
                processData: false,
                success: function () {
                    run_table.reloadTable();
                }
            }
        ).fail(
            function (request) {
                var response = request.responseJSON,
                    detail = (
                        "Failed to rerun"
                    );
                window.alert(detail);
            }
        );
    }
};
RunsTable.prototype = Object.create(permissions.PermissionsTable.prototype);

RunsTable.prototype.getQueryParams = function() {
    var params = permissions.PermissionsTable.prototype.getQueryParams.call(this);
    params.filters = this.filterSet.getFilters();
    return params;
};

RunsTable.prototype.extractRows = function(response) {
    var $no_results = this.$no_results,
        runs;
    $no_results.empty();
    if ('detail' in response) {
        $no_results.append($('
Errors:
'));
        $no_results.append($('
').text(response.detail));
    } else {
        runs = response.results;
        if (runs !== undefined && runs.length > 0) {
            $no_results.hide();
            this.$table.children('caption').text(
                response.count + " matching runs"
            );
            return runs;
        }
        $no_results.html('
No runs match your query.
');
    }

    $no_results.show();
    return []; // no runs
};

$(function(){ // wait for page to finish loading before executing jQuery code
    // Security stuff to prevent cross-site scripting.
    noXSS();

    $('.advanced-filter').prepend('<input type="button" class="close ctrl" value="Close">');

    $('input[value="Advanced"]').on('click', function() {
        $(this).closest('.short-filter').fadeOut({ complete: function() {
            $(this).siblings('.advanced-filter').fadeIn()
                .closest('li').addClass('advanced');
        } });
    });

    $('.advanced-filter input.close.ctrl').on('click', function() {
        $(this).closest('.advanced-filter').fadeOut({ complete: function() {
            $(this).siblings('.short-filter').fadeIn()
                .closest('li').removeClass('advanced');
        } });
    });

    $('form.short-filter, form.advanced-filter').submit(function(e) {
        e.preventDefault();
        runsTable.filterSet.addFromForm(this);
    });

    runsTable = new RunsTable(
        $('#runs'),
        user,
        is_user_admin,
        $('.no_results'),
        $('#active_filters'),
        $(".navigation_links")
    );
    // runsTable.filterSet.add('active');
    runsTable.reloadTable();
});
<|endoftext|>


<commit_before>// getPostAdmin
var co = require('co');
var https = require('https');

var moment = require('moment');

var AWS = require('aws-sdk');
var docClient = new AWS.DynamoDB.DocumentClient();
var s3 = new AWS.S3();

var auth = require('../../lib/auth.js');
var dynamoObjects = require('../../lib/dynamoObjects.js');

exports.handler = (event, context, callback) => {
    // STAGE VARIABLES FROM API GATEWAY
    var stage = event.stage;
    var signing_key = event.signing_key;
    var cookie = event.cookie;
    var token_name = event.token_name;

    var stage_articles_bucket = event.articles_bucket;
    var stage_articles_bucket_path = event.articles_bucket_path;

    var stage_posts_table = event.posts_table;
    var stage_template = event.template;

    var post_id = event.post_id;

    co(function *(){
        var user = yield auth(signing_key, cookie, token_name);

        var post = yield getBlogPostFromDB(post_id);

        var post_html = yield getBlogPostHtml(post_id);

        callback(null, {
            post_id: post_id,
            categories: post.categories,
            date: post.date,
            title: post.title,
            post_html: post_html,
            post_status: post.post_status
        });
    }).catch(function(err) {
        console.log("ERROR!");
        console.log(err);
        console.log(arguments);
        callback(err.message);
    });

    function getBlogPostFromDB(post_id){
        console.log("getBlogPostFromDB");
        return new Promise(function(resolve, reject){
            var params = {
                TableName: stage_posts_table,
                KeyConditionExpression: "post_id = :post_id",
                ExpressionAttributeValues: {
                    ":post_id": post_id
                }
            };

            docClient.query(params, function(err, data) {
                if (err){
                    reject(err);
                }else{
                    resolve(data.Items[0]);
                }
            });

        })
    }

    function getBlogPostHtml(post_id){
        console.log("getBlogPostHtml");
        return new Promise(function(resolve, reject){
            https.get("https://www.s-media.si/"+stage_articles_bucket_path+"/"+post_id+"/index.html", (response) => {
                var body = [];
                response.on('data', function(chunk) {
                  body.push(chunk);
                }).on('end', function() {
                    body = "" + Buffer.concat(body).toString('utf8');
                    resolve(body);
                }).on('error', function(err) {
                    console.log("ERROR");
                    reject(err);
                });
            }).on('error', (err) => {
              console.log('Got error: ${err.message}');
              reject(err);
            });
        })
    }
}<commit_msg>fix hardcoded site url in get_post_admin<commit_after>// getPostAdmin
var co = require('co');
var https = require('https');

var moment = require('moment');

var AWS = require('aws-sdk');
var docClient = new AWS.DynamoDB.DocumentClient();
var s3 = new AWS.S3();

var auth = require('../../lib/auth.js');
var dynamoObjects = require('../../lib/dynamoObjects.js');

exports.handler = (event, context, callback) => {
    // STAGE VARIABLES FROM API GATEWAY
    var stage = event.stage;
    var signing_key = event.signing_key;
    var cookie = event.cookie;
    var token_name = event.token_name;

    var stage_articles_bucket = event.articles_bucket;
    var stage_articles_bucket_path = event.articles_bucket_path;

    var stage_posts_table = event.posts_table;
    var stage_template = event.template;

    var post_id = event.post_id;

    co(function *(){
        var user = yield auth(signing_key, cookie, token_name);

        var post = yield getBlogPostFromDB(post_id);

        var post_html = yield getBlogPostHtml(post_id);

        callback(null, {
            post_id: post_id,
            categories: post.categories,
            date: post.date,
            title: post.title,
            post_html: post_html,
            post_status: post.post_status
        });
    }).catch(function(err) {
        console.log("ERROR!");
        console.log(err);
        console.log(arguments);
        callback(err.message);
    });

    function getBlogPostFromDB(post_id){
        console.log("getBlogPostFromDB");
        return new Promise(function(resolve, reject){
            var params = {
                TableName: stage_posts_table,
                KeyConditionExpression: "post_id = :post_id",
                ExpressionAttributeValues: {
                    ":post_id": post_id
                }
            };

            docClient.query(params, function(err, data) {
                if (err){
                    reject(err);
                }else{
                    resolve(data.Items[0]);
                }
            });

        })
    }

    function getBlogPostHtml(post_id){
        console.log("getBlogPostHtml");
        return new Promise(function(resolve, reject){
            https.get(site_base_url+"/"+stage_articles_bucket_path+"/"+post_id+"/index.html", (response) => {
                var body = [];
                response.on('data', function(chunk) {
                  body.push(chunk);
                }).on('end', function() {
                    body = "" + Buffer.concat(body).toString('utf8');
                    resolve(body);
                }).on('error', function(err) {
                    console.log("ERROR");
                    reject(err);
                });
            }).on('error', (err) => {
              console.log('Got error: ${err.message}');
              reject(err);
            });
        })
    }
}
<|endoftext|>


<commit_before><commit_msg>Behandlung Modifikatoren aus Gegenständen<commit_after><|endoftext|>


<commit_before>// Akvo RSR is covered by the GNU Affero General Public License.
// See more details in the license.txt file located at the root folder of the
// Akvo RSR module. For additional details on the GNU license please see
// < http://www.gnu.org/licenses/agpl.html >.

window.AKVO_RSR.analytics = {

  render: function(resp, parseFn) {
    //  render the hits on the page.
    var hits = parseFn(resp);

    $("#footer_analytics").append(
      "" +
        AKVO_RSR.i18n.strings.visits + ": " + hits + "

"
    );

  },

  parse: function(data) {
    // Aggregate Piwik data for multiple langauges

    return _.reduce(data, function(hits, n) {
      return hits + n.nb_visits;
    }, 0);

  },

  success: function(resp, renderFn, parseFn) {
    // Dispatch on success

    if (resp.length > 0) {
      renderFn(resp, parseFn);
    }

  },

  segments: function(host, path) {
    // Return segment for bare, en, es & fr
    var barePath, pattern, urls;

    pattern = /\/en\/|\/es\/|\/fr\//i;
    barePath = path.replace(pattern, "/");

    urls = [encodeURIComponent(window.location.protocol + "//" + host + barePath)];
    _(AKVO_RSR.i18n.languages).forEach(function(langCode) {
      urls.push(
        encodeURIComponent(window.location.protocol + "//" + host + "/" + langCode + barePath)
      );
    });
    return urls;

  },

  backendUrl: function(host, path) {
    // Construct url for the Piwik API
    var resource, segment, siteId, period, token;

    resource = "http://analytics.akvo.org/" + "index.php?module=API&method=Actions.getPageUrls";
    segment = "segment=pageUrl==" + this.segments(host, path).join(",pageUrl==");
    siteId = "idSite=" + AKVO_RSR.piwik.idSite;
    period = "period=range&date=2013-05-30,yesterday";
    token = "token_auth=" + AKVO_RSR.piwik.authToken; //"3ae549f4e3fb9dbaa02e48f0d3aceb23"; // read-only token

    return [
      resource,
      segment,
      siteId,
      period,
      "format=json",
      "filter_limit=5000&flat=1",
      token,
      "callback=?"
    ].join('&');

  },

  getReport: function(callback, renderFn, parseFn) {
    // Inits request to the Piwik API and hands that to the success callback.

    $.getJSON(
      this.backendUrl(
        window.location.hostname,
        window.location.pathname

        // dev values
        // "projects.commonsites.net",
        // "/en/project/2330/"
      ),
      function(data) {
        callback(data, renderFn, parseFn);
      });
  },

  hits: function() {
    // Get some hits on the page.
    // piwikId = (typeof AKVO_RSR.page !== "undefined") ? AKVO_RSR.page.piwikId : 1;
    if (typeof AKVO_RSR.piwik.disabled === "undefined") {
      return this.getReport(
        this.success,
        this.render,
        this.parse
      );
    } else {
      console.log(typeof AKVO_RSR.piwik.disabled);
      console.log("Piwik reporting disabled");
    }

  }

};
<commit_msg>[#1624] Clean up.<commit_after>// Akvo RSR is covered by the GNU Affero General Public License.
// See more details in the license.txt file located at the root folder of the
// Akvo RSR module. For additional details on the GNU license please see
// < http://www.gnu.org/licenses/agpl.html >.

window.AKVO_RSR.analytics = {

  render: function(resp, parseFn) {
    //  render the hits on the page.
    var hits = parseFn(resp);

    $("#footer_analytics").append(
      "" +
        AKVO_RSR.i18n.strings.visits + ": " + hits + "

"
    );

  },

  parse: function(data) {
    // Aggregate Piwik data for multiple langauges

    return _.reduce(data, function(hits, n) {
      return hits + n.nb_visits;
    }, 0);

  },

  success: function(resp, renderFn, parseFn) {
    // Dispatch on success

    if (resp.length > 0) {
      renderFn(resp, parseFn);
    }

  },

  segments: function(host, path) {
    // Return segment for bare, en, es & fr
    var barePath, pattern, urls;

    pattern = /\/en\/|\/es\/|\/fr\//i;
    barePath = path.replace(pattern, "/");

    urls = [encodeURIComponent(window.location.protocol + "//" + host + barePath)];
    _(AKVO_RSR.i18n.languages).forEach(function(langCode) {
      urls.push(
        encodeURIComponent(window.location.protocol + "//" + host + "/" + langCode + barePath)
      );
    });
    return urls;

  },

  backendUrl: function(host, path) {
    // Construct url for the Piwik API
    var resource, segment, siteId, period, token;

    resource = "http://analytics.akvo.org/" + "index.php?module=API&method=Actions.getPageUrls";
    segment = "segment=pageUrl==" + this.segments(host, path).join(",pageUrl==");
    siteId = "idSite=" + AKVO_RSR.piwik.idSite;
    period = "period=range&date=2013-05-30,yesterday";
    token = "token_auth=" + AKVO_RSR.piwik.authToken; // Should be a read-only token

    return [
      resource,
      segment,
      siteId,
      period,
      "format=json",
      "filter_limit=5000&flat=1",
      token,
      "callback=?"
    ].join('&');

  },

  getReport: function(callback, renderFn, parseFn) {
    // Inits request to the Piwik API and hands that to the success callback.

    $.getJSON(
      this.backendUrl(
        window.location.hostname,
        window.location.pathname

        // dev values
        // "projects.commonsites.net",
        // "/en/project/2330/"
      ),
      function(data) {
        callback(data, renderFn, parseFn);
      });
  },

  hits: function() {
    // Get some hits on the page.
    if (typeof AKVO_RSR.piwik.disabled === "undefined") {
      return this.getReport(
        this.success,
        this.render,
        this.parse
      );
    } else {
      console.log("Piwik reporting disabled");
    }

  }

};
<|endoftext|>


<commit_before>import { isNil, isError } from '../utils/lodash';
import wrapDestroyed from '../utils/wrapDestroyed';
import { logLabeledError, logDispatch } from '../utils/log';
import Store from './Store';
import reducer, { RESET, INIT, MOUNT, UNMOUNT, SUBMIT, SUBMIT_OK, SUBMIT_FAILED, SUBMIT_START, VALIDATE_EXCEPTION, NORMALIZE_EXCEPTION, FORMAT_EXCEPTION, FOCUS, TOUCH, CHANGE_VALIDITY, BLUR, CHANGE_VALUE, RENAME } from './Mediator.reducer';

const errorIsEqual = (err1, err2) => err1 === err2 || (isError(err2) && isError(err1) && err1.message === err2.message);

export default class FractalMediator {
  /**
   * @constructor
   * @param {String} id
   * @param {FractalMediator?} parent
   * @param {Object} options
   * @param {Boolean} options.debug
   * */

  constructor (id, parent = null, { debug = false } = {}) {
    this.id = id;
    this._parent = parent;
    this._destroyed = false;
    this._debug = (parent ? parent._debug : debug) || debug;
    this._store = parent ? parent._store : new Store(reducer);
    this._clothesIsolatedMediator = this._parent ? this._parent._clothesIsolatedMediator : this;

    this._prevError = null;
    this._prevNormalizeError = null;
    this._prevFormatError = null;
    this._prevValidateError = null;

    [
      'destroy',
      '_dispatch',
      'mount',
      'init',
      'focus',
      'blur',
      'submitStart',
      'submit',
      'submitFailed',
      'submitDone',
      'sysNormalizeError',
      'sysValidateError',
      'sysFormatError',
      'changeValidity',
      'rename',
      'reset',
      'touch',
      'changeValue'
    ].forEach((name) => {
      this[name] = wrapDestroyed(this[name]);
    });

    this.onChange = wrapDestroyed(this.onChange, () => () => {});
    this.transaction = wrapDestroyed(this.transaction, () => () => {});
  }

  getClothesIsolatedMediator () {
    return this._clothesIsolatedMediator;
  }

  onChange (listener) {
    return this._store.onChange(listener);
  }

  onceChange (listener) {
    return this._store.onceChange(listener);
  }

  _dispatch (type, payload) {
    const flush = this.transaction('_dispatch');

    const action = { type, fieldID: this.id, payload };

    const endDispatch = logDispatch(this._debug, this._store, action);

    this._store.dispatch(action);

    endDispatch();

    flush();
  }

  transaction (...args) {
    return this._store.transaction(...args);
  }

  version () {
    return this._store.version;
  }

  getState () {
    return this.getFullState()[this.id];
  }

  getFullState () {
    return this._store.getState();
  }

  dehydrateNames (ids) {
    const fullState = this.getFullState();

    return ids.map((id) => fullState[id].name);
  }

  hasNamedChildren () {
    const fullState = this.getFullState();

    return fullState[this.id].children.some((id) => !isNil(fullState[id].name));
  }

  // handlers

  destroy () {
    this._dispatch(UNMOUNT);
    this._destroyed = true;
  }

  mount (params = {}) {
    const parentID = this._parent ? this._parent.id : null;

    this._dispatch(MOUNT, { parentID, ...params });
  }

  init () {
    this._dispatch(INIT);
  }

  focus () {
    this._dispatch(FOCUS);
  }

  blur () {
    this._dispatch(BLUR);
  }

  submitStart () {
    this._dispatch(SUBMIT_START);
  }

  submit () {
    const flush = this.transaction();

    this._dispatch(SUBMIT);
    this.touch();

    flush();
  }

  submitDone () {
    const flush = this.transaction();

    this._dispatch(SUBMIT_OK);
    this.touch();

    flush();
  }

  submitFailed () {
    this._dispatch(SUBMIT_FAILED);
  }

  sysNormalizeError (err) {
    if (errorIsEqual(this._prevNormalizeError, err)) {
      return;
    }

    this._prevNormalizeError = err;

    this._dispatch(NORMALIZE_EXCEPTION, err);

    if (err) {
      logLabeledError('normalize', err);
    }
  }

  sysFormatError (err) {
    if (errorIsEqual(this._prevFormatError, err)) {
      return;
    }

    this._prevFormatError = err;

    this._dispatch(FORMAT_EXCEPTION, err);

    if (err) {
      logLabeledError('format', err);
    }
  }

  sysValidateError (err) {
    if (errorIsEqual(this._prevValidateError, err)) {
      return;
    }

    this._prevValidateError = err;

    this._dispatch(VALIDATE_EXCEPTION, err);

    if (err) {
      logLabeledError('validate', err);
    }
  }

  changeValidity (error) {
    error = error || null;

    if (errorIsEqual(this._prevError, error)) {
      return;
    }

    this._prevError = error;

    this._dispatch(CHANGE_VALIDITY, error);
  }

  rename (name) {
    this._dispatch(RENAME, name);
  }

  reset (initialValue) {
    this._dispatch(RESET, initialValue);
  }

  touch () {
    if (!this.getState().meta.touched) {
      this._dispatch(TOUCH);
    }
  }

  changeValue (value, { touch = true } = {}) {
    const flush = this.transaction('changeValue');

    this._dispatch(CHANGE_VALUE, value);

    if (touch) {
      this.touch();
    }

    flush();
  }
}
<commit_msg>remove unused condition in touch action in mediator<commit_after>import { isNil, isError } from '../utils/lodash';
import wrapDestroyed from '../utils/wrapDestroyed';
import { logLabeledError, logDispatch } from '../utils/log';
import Store from './Store';
import reducer, { RESET, INIT, MOUNT, UNMOUNT, SUBMIT, SUBMIT_OK, SUBMIT_FAILED, SUBMIT_START, VALIDATE_EXCEPTION, NORMALIZE_EXCEPTION, FORMAT_EXCEPTION, FOCUS, TOUCH, CHANGE_VALIDITY, BLUR, CHANGE_VALUE, RENAME } from './Mediator.reducer';

const errorIsEqual = (err1, err2) => err1 === err2 || (isError(err2) && isError(err1) && err1.message === err2.message);

export default class FractalMediator {
  /**
   * @constructor
   * @param {String} id
   * @param {FractalMediator?} parent
   * @param {Object} options
   * @param {Boolean} options.debug
   * */

  constructor (id, parent = null, { debug = false } = {}) {
    this.id = id;
    this._parent = parent;
    this._destroyed = false;
    this._debug = (parent ? parent._debug : debug) || debug;
    this._store = parent ? parent._store : new Store(reducer);
    this._clothesIsolatedMediator = this._parent ? this._parent._clothesIsolatedMediator : this;

    this._prevError = null;
    this._prevNormalizeError = null;
    this._prevFormatError = null;
    this._prevValidateError = null;

    [
      'destroy',
      '_dispatch',
      'mount',
      'init',
      'focus',
      'blur',
      'submitStart',
      'submit',
      'submitFailed',
      'submitDone',
      'sysNormalizeError',
      'sysValidateError',
      'sysFormatError',
      'changeValidity',
      'rename',
      'reset',
      'touch',
      'changeValue'
    ].forEach((name) => {
      this[name] = wrapDestroyed(this[name]);
    });

    this.onChange = wrapDestroyed(this.onChange, () => () => {});
    this.transaction = wrapDestroyed(this.transaction, () => () => {});
  }

  getClothesIsolatedMediator () {
    return this._clothesIsolatedMediator;
  }

  onChange (listener) {
    return this._store.onChange(listener);
  }

  onceChange (listener) {
    return this._store.onceChange(listener);
  }

  _dispatch (type, payload) {
    const flush = this.transaction('_dispatch');

    const action = { type, fieldID: this.id, payload };

    const endDispatch = logDispatch(this._debug, this._store, action);

    this._store.dispatch(action);

    endDispatch();

    flush();
  }

  transaction (...args) {
    return this._store.transaction(...args);
  }

  version () {
    return this._store.version;
  }

  getState () {
    return this.getFullState()[this.id];
  }

  getFullState () {
    return this._store.getState();
  }

  dehydrateNames (ids) {
    const fullState = this.getFullState();

    return ids.map((id) => fullState[id].name);
  }

  hasNamedChildren () {
    const fullState = this.getFullState();

    return fullState[this.id].children.some((id) => !isNil(fullState[id].name));
  }

  // handlers

  destroy () {
    this._dispatch(UNMOUNT);
    this._destroyed = true;
  }

  mount (params = {}) {
    const parentID = this._parent ? this._parent.id : null;

    this._dispatch(MOUNT, { parentID, ...params });
  }

  init () {
    this._dispatch(INIT);
  }

  focus () {
    this._dispatch(FOCUS);
  }

  blur () {
    this._dispatch(BLUR);
  }

  submitStart () {
    this._dispatch(SUBMIT_START);
  }

  submit () {
    const flush = this.transaction();

    this._dispatch(SUBMIT);
    this.touch();

    flush();
  }

  submitDone () {
    const flush = this.transaction();

    this._dispatch(SUBMIT_OK);
    this.touch();

    flush();
  }

  submitFailed () {
    this._dispatch(SUBMIT_FAILED);
  }

  sysNormalizeError (err) {
    if (errorIsEqual(this._prevNormalizeError, err)) {
      return;
    }

    this._prevNormalizeError = err;

    this._dispatch(NORMALIZE_EXCEPTION, err);

    if (err) {
      logLabeledError('normalize', err);
    }
  }

  sysFormatError (err) {
    if (errorIsEqual(this._prevFormatError, err)) {
      return;
    }

    this._prevFormatError = err;

    this._dispatch(FORMAT_EXCEPTION, err);

    if (err) {
      logLabeledError('format', err);
    }
  }

  sysValidateError (err) {
    if (errorIsEqual(this._prevValidateError, err)) {
      return;
    }

    this._prevValidateError = err;

    this._dispatch(VALIDATE_EXCEPTION, err);

    if (err) {
      logLabeledError('validate', err);
    }
  }

  changeValidity (error) {
    error = error || null;

    if (errorIsEqual(this._prevError, error)) {
      return;
    }

    this._prevError = error;

    this._dispatch(CHANGE_VALIDITY, error);
  }

  rename (name) {
    this._dispatch(RENAME, name);
  }

  reset (initialValue) {
    this._dispatch(RESET, initialValue);
  }

  touch () {
    this._dispatch(TOUCH);
  }

  changeValue (value, { touch = true } = {}) {
    const flush = this.transaction('changeValue');

    this._dispatch(CHANGE_VALUE, value);

    if (touch) {
      this.touch();
    }

    flush();
  }
}
<|endoftext|>


<commit_before>var define = require('cli-define');
var merge = require('cli-util').merge;
var argparse = require('cli-argparse');

/**
 *  Retrieve a configuration suitable for passing to
 *  the arguments parser.
 */
function getParserConfiguration() {
  var config = {
    alias: {}, flags: [], options: []}, k, arg, key;
  var no = /^\[?no\]?/, nor = define.re.no();
  for(k in this._options) {
    arg = this._options[k];
    if(!(arg instanceof define.Flag) && !(arg instanceof define.Option)) {
      continue;
    }
    var names = arg.names().slice(0);
    names.forEach(function(nm, index, arr) {
      arr[index] = nm.replace(nor, '');
    })
    key = arg.key();
    //console.log(no);
    //console.log('key: %s', key);
    if(no.test(key)) {
      key = key.replace(no, '');
      //console.log('replacing no portion %s', key);
      key = key.charAt(0).toLowerCase() + key.slice(1);
    }
    config.alias[names.join(' ')] = key;
    console.log('names: %j', names);
    console.log('flag: %s', (arg instanceof define.Flag));
    if(arg instanceof define.Flag) {
      config.flags = config.flags.concat(names);
    }else{
      config.options = config.options.concat(names);
    }
  }
  return config;
}

/**
 *  Parses command line arguments, typically you would want
 *  this to be the first middleware.
 */
module.exports = function() {
  return function parser(req, next) {
    if(!arguments.length) return;
    var args = req.argv;
    var config = getParserConfiguration.call(this);
    var result = argparse(args, config);
    result.keys = Object.keys(result.flags).concat(Object.keys(result.options));
    result.all = {};
    merge(result.flags, result.all);
    merge(result.options, result.all);
    result.config = config;
    req.result = result;
    //console.log(JSON.stringify(req.result, undefined, 2));
    this.request(req);
    next();
  }
}
<commit_msg>Remove debug statements.<commit_after>var define = require('cli-define');
var merge = require('cli-util').merge;
var argparse = require('cli-argparse');

/**
 *  Retrieve a configuration suitable for passing to
 *  the arguments parser.
 */
function getParserConfiguration() {
  var config = {
    alias: {}, flags: [], options: []}, k, arg, key;
  var no = /^\[?no\]?/, nor = define.re.no();
  for(k in this._options) {
    arg = this._options[k];
    if(!(arg instanceof define.Flag) && !(arg instanceof define.Option)) {
      continue;
    }
    var names = arg.names().slice(0);
    names.forEach(function(nm, index, arr) {
      arr[index] = nm.replace(nor, '');
    })
    key = arg.key();
    //console.log(no);
    //console.log('key: %s', key);
    if(no.test(key)) {
      key = key.replace(no, '');
      //console.log('replacing no portion %s', key);
      key = key.charAt(0).toLowerCase() + key.slice(1);
    }
    config.alias[names.join(' ')] = key;
    //console.log('names: %j', names);
    //console.log('flag: %s', (arg instanceof define.Flag));
    if(arg instanceof define.Flag) {
      config.flags = config.flags.concat(names);
    }else{
      config.options = config.options.concat(names);
    }
  }
  return config;
}

/**
 *  Parses command line arguments, typically you would want
 *  this to be the first middleware.
 */
module.exports = function() {
  return function parser(req, next) {
    if(!arguments.length) return;
    var args = req.argv;
    var config = getParserConfiguration.call(this);
    var result = argparse(args, config);
    result.keys = Object.keys(result.flags).concat(Object.keys(result.options));
    result.all = {};
    merge(result.flags, result.all);
    merge(result.options, result.all);
    result.config = config;
    req.result = result;
    //console.log(JSON.stringify(req.result, undefined, 2));
    this.request(req);
    next();
  }
}
<|endoftext|>


<commit_before>var SessionStore = require('../stores/session')
var MissingStateError = require('../errors/missingstateerror');

module.exports = function(dispatcher, store, options) {
  if (typeof options == 'string') {
    options = { from: options };
  }
  options = options || {};

  var from = options.from;
  var getHandle = options.getHandle || function(req) {
    return (req.query && req.query.state) || (req.body && req.body.state);
  }

  return function resumeState(req, res, next) {
    console.log('## resumeState');
    console.log(options)
    console.log(req.state);
    console.log(req._state);
    console.log('--')

    function dispatch(name) {
      if (!name) { return next(new Error("Cannot resume unnamed flow")); }

      function cont(err) {
        try {
          dispatcher._resume(name, err, req, res, next);
        } catch(ex) {
          return next(ex);
        }
      }

      var yname = from || (req.yieldState && req.yieldState.name);
      if (yname) {
        try {
          dispatcher._transition(name, yname, null, req, res, cont);
        } catch(ex) {
          return next(ex);
        }
      } else {
        cont();
      }
    }

    function proceed(h, ystate) {
      if (!h) {
        // No state to resume.  `next` middleware is expected to implement
        // default behavior for responding to the request.
        return next();
      }

      store.load(req, h, function(err, state) {
        if (err) { return next(err); }
        if (!state) { return next(new MissingStateError("Failed to load previous state", ystate.prev)); }

        req.state = state;

        if (from && from === state.name) {
          // State has been loaded for the state that is the yeilding state, and
          // therefore needs finalizing and further resumption, if possible.
          return finalize(state);
        }

        // Expose the state that is yeilding control back to the previous state.
        // When the previous state is resumed, it can use this context to inform
        // its behavior.
        req.yieldState = ystate;

        dispatch(state.name);
      });
    }

    function finalize(state) {
      // Remove the current state from any persistent storage, due to the
      // fact that it is complete.  Proceed to load the previous state (if any)
      // and resume processing.
      //delete req.state;
      store.destroy(req, state.handle, function(err) {
        if (err) {
          //delete req.state;
          return next(err);
        }
        return proceed(state.prev, state);
      });
    }

    if (req.state) {
      if (from && from !== req.state.name) {
        // State has been loaded for a state that is not the yeilding state, and
        // therefore is the state that is being resumed.
        return dispatch(req.state.name);
      }

      finalize(req.state);
    } else if (req._state) {
      // State has been loaded for a state that was not the expected (and
      // yielding) state, and therefore is the state that is being resumed.
      // This is an optimization, supported by a prior call to `loadState`
      // with a specified name option.
      req.state = req._state;
      delete req._state;
      return dispatch(req.state.name);
    } else {
      return proceed(getHandle(req));
    }
  };
};
<commit_msg>Add note, remove unnecessary try/catch.<commit_after>var SessionStore = require('../stores/session')
var MissingStateError = require('../errors/missingstateerror');

module.exports = function(dispatcher, store, options) {
  if (typeof options == 'string') {
    options = { from: options };
  }
  options = options || {};

  var from = options.from;
  var getHandle = options.getHandle || function(req) {
    return (req.query && req.query.state) || (req.body && req.body.state);
  }

  return function resumeState(req, res, next) {
    console.log('## resumeState');
    console.log(options)
    console.log(req.state);
    console.log(req._state);
    console.log('--')

    function dispatch(name) {
      if (!name) { return next(new Error("Cannot resume unnamed flow")); }

      // NOTE: Once we have arrived at this point, the middleware chain
      //       processing the request is halted.  All further processing will
      //       be handled by the resume middleware chain of the flow, including
      //       error handling.

      function cont(err) {
        try {
          dispatcher._resume(name, err, req, res, next);
        } catch(ex) {
          return next(ex);
        }
      }

      var yname = from || (req.yieldState && req.yieldState.name);
      if (yname) {
        dispatcher._transition(name, yname, null, req, res, cont);
      } else {
        cont();
      }
    }

    function proceed(h, ystate) {
      if (!h) {
        // No state to resume.  `next` middleware is expected to implement
        // default behavior for responding to the request.
        return next();
      }

      store.load(req, h, function(err, state) {
        if (err) { return next(err); }
        if (!state) { return next(new MissingStateError("Failed to load previous state", ystate.prev)); }

        req.state = state;

        if (from && from === state.name) {
          // State has been loaded for the state that is the yeilding state, and
          // therefore needs finalizing and further resumption, if possible.
          return finalize(state);
        }

        // Expose the state that is yeilding control back to the previous state.
        // When the previous state is resumed, it can use this context to inform
        // its behavior.
        req.yieldState = ystate;

        dispatch(state.name);
      });
    }

    function finalize(state) {
      // Remove the current state from any persistent storage, due to the
      // fact that it is complete.  Proceed to load the previous state (if any)
      // and resume processing.
      //delete req.state;
      store.destroy(req, state.handle, function(err) {
        if (err) {
          //delete req.state;
          return next(err);
        }
        return proceed(state.prev, state);
      });
    }

    if (req.state) {
      if (from && from !== req.state.name) {
        // State has been loaded for a state that is not the yeilding state, and
        // therefore is the state that is being resumed.
        return dispatch(req.state.name);
      }

      finalize(req.state);
    } else if (req._state) {
      // State has been loaded for a state that was not the expected (and
      // yielding) state, and therefore is the state that is being resumed.
      // This is an optimization, supported by a prior call to `loadState`
      // with a specified name option.
      req.state = req._state;
      delete req._state;
      return dispatch(req.state.name);
    } else {
      return proceed(getHandle(req));
    }
  };
};
<|endoftext|>


<commit_before>module.exports = function(testOptions, testMonitor){
	this.start = function(){
		testMonitor.passed();
	};
};<commit_msg>refactoring<commit_after>var MochaTestRunner = function(testOptions, testMonitor){
	this.start = function(){
		testMonitor.passed();
	};
};

module.exports = MochaTestRunner;<|endoftext|>


<commit_before>var _ = require('lodash');
var parallel = require('when/parallel');
var sequence = require('when/sequence');
var Promises = require('backbone-promises');
var PromisedModel = Promises.Model;
var Backbone = require('backbone');
var SchemaModel = require('backbone-blueprint').ValidatingModel;
var acl = require('../acl');
var asyncUtils = require('../utils/async');
var ValidationError = require('../errors').ValidationError;
var logId = 'serverbone:models:base';
var _debug = require('debug');
var when = Promises.when;

var debug = {
  log: _debug(logId + ':log'),
  warn: _debug(logId + ':warn'),
  error: _debug(logId + ':error')
};

/**
 * Format 'templated' properties of object like {type: '{type}'}
 * using given this-context for the actual value
 * @param  {Object} properties
 * @return {Object}
 */
var formatProperties = function(properties) {
  var self = this;
  var realProperties = {};

  function getTemplateValue(property) {
    function getThisValue(match) {
      var cleanedValue = match.substring(1, match.length - 1);
      return self[cleanedValue];
    }
    return property.replace(/{([^}]+)}+/g, getThisValue);
  }

  for (var propertyKey in properties) {
    realProperties[propertyKey] = getTemplateValue(properties[propertyKey]);
  }

  return realProperties;
};

// convert error & reject
var failWithError = function(options, err) {
  options = options || {};
  if (options.ignoreFailures) return Promises.when.resolve();
  console.log(err.message);
  if (err.message === 'not found') {
    err.status = 404;
  }
  return Promises.when.reject(err);
};

var BaseModel = SchemaModel.extend({
  initialize: function(attributes, options) {
    BaseModel.__super__.initialize.apply(this, [attributes, options]);
    this.options = options ? _.clone(options) : {};
    this._processSchema();
  },

  _processSchema: function() {
    if (!this.schema) return;
    this.indexes = this.schema.indexes;
  },

  url: function() {
    if (this.isNew()) {
      return this.type;
    } else {
      return this.type + ':' + this.get(this.idAttribute);
    }
  },

  sync: function() {
    throw new Error('sync method should be overridden!');
  },

  save: function(key, val, options) {
    var opts = options || {};
    var self = this;
    if (key == null || typeof key === 'object') {
      opts = val || {};
    }
    var error = opts.error;
    return this.preSave(opts).then(function() {
      return PromisedModel.prototype.save.call(self, key, opts).then(function(model) {
        return self.afterSave(opts).then(function() {
          return model;
        });
      });
    }, function(err) {
      if (error) error(self, err);
      return err;
    });

    /*
    var fns = [
      _.bind(this.preSave, this, opts),
      _.bind(PromisedModel.prototype.save, this, key, val, options),
      _.bind(this.afterSave, this, opts)
    ];
    return asyncUtils.wrapToSequence(fns, 1, opts, this);*/
  },

  fetch: function(options) {
    var self = this;
    return PromisedModel.prototype
      .fetch.apply(this, arguments)
      .then(function() {
        self.fetchStatus = 'fetched';
        return self;
      }, _.bind(failWithError, null, options));
  },

  destroy: function(options) {
    var fns = [
      _.bind(this.preDelete, this),
      _.bind(PromisedModel.prototype.destroy, this, options)
    ];
    return sequence(fns)
      .then(function(results) {
        return results[1];
      }, _.bind(failWithError, null, options));
  },

  // stub, everyone can access
  canAccess: function() {
    return true;
  },

  //TODO: this is just a dummy method, should be implemented
  getRoles: function() {
    var roles = [];
    return roles;
  },

  // Field level access: get properties which user has permissions
  // field can override object level access mask
  propertiesWithAccess: function() {
    return Object.keys(this.schema.properties);
  },

  toJSON: function(options) {
    options = options || {};
    var json = BaseModel.__super__.toJSON.apply(this, arguments);
    return json;
  },

  validate: function(attrs, options) {
    options = options || {};
    var validationErrors = BaseModel.__super__.validate.apply(this, arguments);
    if (validationErrors) return new ValidationError({
      errors: validationErrors
    });
  },

  /**
   * Fetch current model & its relations
   * Need to fetch current model first in order to apply correct ids to relations
   */
  fetchAll: function(options) {
    options = options || {};
    var self = this;
    var when = Promises.when;

    function handleError(err) {
      if (options.ignoreFailures) {
        return when.resolve(self);
      } else {
        return when.reject(err);
      }
    }
    return this
      .fetch(options)
      .then(function() {
        if (!self.relationDefinitions) {
          return self;
        }
        return self
          .fetchRelations(options)
          .then(function() {
            return self;
          }, handleError);
      }, handleError);
  },

  /**
   * Fetches relations available based on current attributes
   * if options.onlyRelations Array is specified only those relations are fetched
   * e.g. fetchRelations({onlyRelations: ['user']}) will fetch only relation 'user'
   */
  fetchRelations: function(options) {
    options = options || {};
    var when = Promises.when;
    var self = this;
    var fns = [];

    _.each(self.relationDefinitions, function(relationAttributes, relationKey) {
      if (_.isArray(options.onlyRelations)) {
        if (options.onlyRelations.indexOf(relationKey) === -1) return;
      }
      var relation = self.get(relationKey);
      if (relation && relation.fetch) {
        fns.push(_.bind(relation.fetch, relation, _.clone(options)));
      }
    });

    return parallel(fns)
      .then(function relationsFetched() {
        self.fetchStatus = 'all_fetched';
        return self;
      }, function error(err) {
        if (options.ignoreFailures) {
          self.fetchStatus = 'all_fetched';
          return when.resolve(self);
        }
        return err;
      });
  },

  /**
   * Save Model & its relations
   * Note: Collection relations are not saved
   */
  saveAll: function(options) {
    options = options || {};
    var fns = [
      _.bind(this.save, this, options)
    ];
    _.each(this.relationDefinitions, function(relationAttributes, relationKey) {
      var relation = this.get(relationKey);
      if (relation && _.isFunction(relation.save)) {
        fns.push(_.bind(relation.save, relation, options));
      }
    }, this);
    return sequence(fns);
  },

  // apply function to this model & its relations
  applyToAll: function(fnStr, options) {
    options = options || {};
    var fns = [
      this[fnStr].apply(this, [options])
    ];
    _.each(this.relationDefinitions, function(relationAttributes, relationKey) {
      var relation = this.get(relationKey);
      if (relation) {
        fns.push(relation[fnStr](options));
      }
    }, this);
    return Promises.when.all(fns);
  },

  preSave: function() {
    debug.log('BaseModel.preSave %s', JSON.stringify(arguments));
    return when.resolve();
  },

  afterSave: function() {
    debug.log('BaseModel.afterSave %s', JSON.stringify(arguments));
    return when.resolve();
  },

  fetchAllRequired: function() {
    return this.relationDefinitions
      && Object.keys(this.relationDefinitions).length
      && this.fetchStatus !== 'all_fetched';
  },

  preDelete: function(options) {
    options = options || {};
    if (this.fetchAllRequired()) {
      options.ignoreFailures = true;
      // no need load collection models
      options.skipModelsFetch = true;
      return this.fetchAll(options);
    }
    return when.resolve();
  }
});

BaseModel.Associations = Backbone.Associations;

// Instance methods

// Helpers

module.exports = BaseModel;
module.exports.formatProperties = formatProperties;<commit_msg>use failWithError<commit_after>var _ = require('lodash');
var parallel = require('when/parallel');
var sequence = require('when/sequence');
var Promises = require('backbone-promises');
var PromisedModel = Promises.Model;
var Backbone = require('backbone');
var SchemaModel = require('backbone-blueprint').ValidatingModel;
var acl = require('../acl');
var asyncUtils = require('../utils/async');
var ValidationError = require('../errors').ValidationError;
var logId = 'serverbone:models:base';
var _debug = require('debug');
var when = Promises.when;

var debug = {
  log: _debug(logId + ':log'),
  warn: _debug(logId + ':warn'),
  error: _debug(logId + ':error')
};

/**
 * Format 'templated' properties of object like {type: '{type}'}
 * using given this-context for the actual value
 * @param  {Object} properties
 * @return {Object}
 */
var formatProperties = function(properties) {
  var self = this;
  var realProperties = {};

  function getTemplateValue(property) {
    function getThisValue(match) {
      var cleanedValue = match.substring(1, match.length - 1);
      return self[cleanedValue];
    }
    return property.replace(/{([^}]+)}+/g, getThisValue);
  }

  for (var propertyKey in properties) {
    realProperties[propertyKey] = getTemplateValue(properties[propertyKey]);
  }

  return realProperties;
};

// convert error & reject
var failWithError = function(options, err) {
  options = options || {};
  if (options.ignoreFailures) return Promises.when.resolve();
  console.log(err.message);
  if (err.message === 'not found') {
    err.status = 404;
  }
  return Promises.when.reject(err);
};

var BaseModel = SchemaModel.extend({
  initialize: function(attributes, options) {
    BaseModel.__super__.initialize.apply(this, [attributes, options]);
    this.options = options ? _.clone(options) : {};
    this._processSchema();
  },

  _processSchema: function() {
    if (!this.schema) return;
    this.indexes = this.schema.indexes;
  },

  url: function() {
    if (this.isNew()) {
      return this.type;
    } else {
      return this.type + ':' + this.get(this.idAttribute);
    }
  },

  sync: function() {
    throw new Error('sync method should be overridden!');
  },

  save: function(key, val, options) {
    var opts = options || {};
    var self = this;
    if (key == null || typeof key === 'object') {
      opts = val || {};
    }
    var error = opts.error;
    return this.preSave(opts).then(function() {
      return PromisedModel.prototype.save.call(self, key, opts).then(function(model) {
        return self.afterSave(opts).then(function() {
          return model;
        });
      });
    }, function(err) {
      if (error) error(self, err);
      return failWithError(opts, err);
    });
  },

  fetch: function(options) {
    var self = this;
    return PromisedModel.prototype
      .fetch.apply(this, arguments)
      .then(function() {
        self.fetchStatus = 'fetched';
        return self;
      }, _.bind(failWithError, null, options));
  },

  destroy: function(options) {
    var fns = [
      _.bind(this.preDelete, this),
      _.bind(PromisedModel.prototype.destroy, this, options)
    ];
    return sequence(fns)
      .then(function(results) {
        return results[1];
      }, _.bind(failWithError, null, options));
  },

  // stub, everyone can access
  canAccess: function() {
    return true;
  },

  //TODO: this is just a dummy method, should be implemented
  getRoles: function() {
    var roles = [];
    return roles;
  },

  // Field level access: get properties which user has permissions
  // field can override object level access mask
  propertiesWithAccess: function() {
    return Object.keys(this.schema.properties);
  },

  toJSON: function(options) {
    options = options || {};
    var json = BaseModel.__super__.toJSON.apply(this, arguments);
    return json;
  },

  validate: function(attrs, options) {
    options = options || {};
    var validationErrors = BaseModel.__super__.validate.apply(this, arguments);
    if (validationErrors) return new ValidationError({
      errors: validationErrors
    });
  },

  /**
   * Fetch current model & its relations
   * Need to fetch current model first in order to apply correct ids to relations
   */
  fetchAll: function(options) {
    options = options || {};
    var self = this;
    var when = Promises.when;

    function handleError(err) {
      if (options.ignoreFailures) {
        return when.resolve(self);
      } else {
        return when.reject(err);
      }
    }
    return this
      .fetch(options)
      .then(function() {
        if (!self.relationDefinitions) {
          return self;
        }
        return self
          .fetchRelations(options)
          .then(function() {
            return self;
          }, handleError);
      }, handleError);
  },

  /**
   * Fetches relations available based on current attributes
   * if options.onlyRelations Array is specified only those relations are fetched
   * e.g. fetchRelations({onlyRelations: ['user']}) will fetch only relation 'user'
   */
  fetchRelations: function(options) {
    options = options || {};
    var when = Promises.when;
    var self = this;
    var fns = [];

    _.each(self.relationDefinitions, function(relationAttributes, relationKey) {
      if (_.isArray(options.onlyRelations)) {
        if (options.onlyRelations.indexOf(relationKey) === -1) return;
      }
      var relation = self.get(relationKey);
      if (relation && relation.fetch) {
        fns.push(_.bind(relation.fetch, relation, _.clone(options)));
      }
    });

    return parallel(fns)
      .then(function relationsFetched() {
        self.fetchStatus = 'all_fetched';
        return self;
      }, function error(err) {
        if (options.ignoreFailures) {
          self.fetchStatus = 'all_fetched';
          return when.resolve(self);
        }
        return err;
      });
  },

  /**
   * Save Model & its relations
   * Note: Collection relations are not saved
   */
  saveAll: function(options) {
    options = options || {};
    var fns = [
      _.bind(this.save, this, options)
    ];
    _.each(this.relationDefinitions, function(relationAttributes, relationKey) {
      var relation = this.get(relationKey);
      if (relation && _.isFunction(relation.save)) {
        fns.push(_.bind(relation.save, relation, options));
      }
    }, this);
    return sequence(fns);
  },

  // apply function to this model & its relations
  applyToAll: function(fnStr, options) {
    options = options || {};
    var fns = [
      this[fnStr].apply(this, [options])
    ];
    _.each(this.relationDefinitions, function(relationAttributes, relationKey) {
      var relation = this.get(relationKey);
      if (relation) {
        fns.push(relation[fnStr](options));
      }
    }, this);
    return Promises.when.all(fns);
  },

  preSave: function() {
    debug.log('BaseModel.preSave %s', JSON.stringify(arguments));
    return when.resolve();
  },

  afterSave: function() {
    debug.log('BaseModel.afterSave %s', JSON.stringify(arguments));
    return when.resolve();
  },

  fetchAllRequired: function() {
    return this.relationDefinitions
      && Object.keys(this.relationDefinitions).length
      && this.fetchStatus !== 'all_fetched';
  },

  preDelete: function(options) {
    options = options || {};
    if (this.fetchAllRequired()) {
      options.ignoreFailures = true;
      // no need load collection models
      options.skipModelsFetch = true;
      return this.fetchAll(options);
    }
    return when.resolve();
  }
});

BaseModel.Associations = Backbone.Associations;

// Instance methods

// Helpers

module.exports = BaseModel;
module.exports.formatProperties = formatProperties;<|endoftext|>


<commit_before>// vim: fdm=marker et ts=2 sw=2

jQuery.noConflict()(function($){

var threadHovered = null;
var didResize = false;
var posts = $(".debiki .dw-post");
var rateFormTemplate = $("#dw-hidden-templates .dw-rat-template form");
var debateId = $('.debiki').attr('id');

$(".dw-post, .dw-thread-info").hover(
  function(event){
    var nextThread = $(this).closest('.dw-thread');

    if ($(this).hasClass('dw-post')) {
      // Show the #action-menu, unless the thread is closing (A)
      // and unless the thread doesn't already have a reply-form (B)
      // or a rating-form (C) or an edit form child (D).
      // (B, C and D: Better not open many action forms at once.)
      if (!nextThread.hasClass('dw-collapsed') &&  // A
          !nextThread.children().filter( // B, C, D
            '.dw-reply-form, .dw-rat-form, .dw-edit-form-wrap').length) {
        $(this).after($('#dw-action-menu'))
      }
    }
    else if (nextThread.find('#dw-action-menu').length) {
      // This is a .thread-info and it has the #action-menu inside.
      // This is not a safe place for the menu! If this
      // .thread-info is clicked, the thread will
      // collapsed itself, and the #action-menu will be hidden inside
      // the collapsed thread -- the menu becomes buggy gone -- unless
      // moved to somewhere safe.
      $('#dw-hidden-templates').append($('#dw-action-menu'));
    }

    if (threadHovered && threadHovered[0] == nextThread[0])
      return;

    if ($(this).hasClass('dw-thread-info') &&
        !threadHovered.hasClass('dw-collapsed')) {
      // This .thread-info is not visible (it's only visible by default
      // if the thread is collapsed). Don't fade it in, because the user
      // might be navigating the #action-menu, and then we don't want
      // this .thread-info to appear below that menu. Instead,
      // only open the .thread-info if the threaad's *post* is hovered.
      return;
    }

    // Fade last thread-info, unless thread collapsed.
    if (threadHovered && !threadHovered.hasClass('dw-collapsed')) {
      threadHovered.children('.dw-thread-info')
                      .stop(true, true).fadeTo(1000, 0);
      threadHovered.stop(true, true)
          .removeClass('dw-demarcated')
          .removeClass('dw-demarcated-fx', 1000);
    }
    // Show thread-info for current thread.
    nextThread.children('.dw-thread-info').stop(true, true).fadeTo(600, 1);
    nextThread.stop(true, true)
        .addClass('dw-demarcated') // gives functionality instantly
        .addClass('dw-demarcated-fx', 600); // just for class animation effects
    threadHovered = nextThread;
  },
  function(event){
  });

$(".dw-thread-info").click(function() {
  var thread = $(this).closest(".dw-thread");
  if (! thread.hasClass('dw-demarcated')) return;
  thread
    .children(":not(.dw-thread-info)").stop(true,true).slideToggle(800)
    .end()
    .stop(true, true)
    .toggleClass('dw-collapsed')
    .toggleClass('dw-collapsed-fx', 600);
});

// Outline new posts
(function(){
  var myLastVersion = $.cookie('myLastPageVersion');
  if (!myLastVersion) return;
  var newPosts = posts.filter(function(index){
    return $(this).find('.dw-date').attr('title') > myLastVersion;
  })
  newPosts.closest('.dw-thread').addClass('dw-new');
})()

// Indicate which posts are cropped.
posts.filter('.dw-cropped-s').append(
    '
. . . truncated
');

// Makes whole post visible on click.
//
posts.filter('.dw-cropped-s').click(function(){
  console.log('click: Removind cropped-s.');
  // (Some rather long posts are cropped, using max-width and -height.
  // Don't remove max-width, or some posts might end up rather wide.)
  $(this).closest('.dw-post').removeClass('dw-cropped-s');
})

// ------- Resizing

// Make posts and threads resizable.
// Fails with a TypeError on Android: Cathching it and ignoring it.
// (On Android, posts and threads won't be resizable.)
try {
  posts
  .resizable({
      autoHide: true,
      start: function(event, ui) {
        // Remove max height and width restrictions.
        $(this).closest('.dw-post').removeClass('dw-cropped-s dw-cropped-e');
        didResize = true;
      }
     })
  .find('.ui-resizable-se')
    // Make the resize grip larger.
    .removeClass('.ui-icon-gripsmall-diagonal-se')  // exchange small grip...
    .addClass('ui-icon-grip-diagonal-se')  // ...against the normal one
  .end()
  // Remove max-height and -width when mouse *up* on the resize-e handle.
  // (This is a shortcut to reveal the whole post - only triggered if
  // *clicking* the resize handle, but not dragging it.)
  .find('.ui-resizable-e')
    .mouseup(function(){
      // (Removing only max-width usually results in nothing:
      // The thread usually has a max-width.).
      var post = $(this).closest('.dw-post');
      post.removeClass('dw-cropped-s dw-cropped-e');
      // Expand post eastwards if resize handle was clicked not dragged.
      // (Also expands southwards, but browsers usually expand to east first.)
      if (!didResize) post.css('width', null).css('height', null);
    })
  .end()
  .find('.ui-resizable-s, .ui-resizable-se')
    // Expand post southwards if resize handle was clicked not dragged.
    .mouseup(function(){
      if (!didResize) $(this).closest('.dw-post').css('height', null);
    })
  .end()
  .find('.ui-resizable-handle')
    .mousedown(function(){
      didResize = false;
    })
  .end();

  // Resize threads.
  // Bug: Resizing a thread might toggle it collapsed / expanded, since
  // a click on the .thread-info might happen.
  $('.debiki .dw-thread-info').each(function(index) {
    var thread = $(this).closest('.dw-thread');
    $(this).resizable({
        alsoResize: thread,
        handles: 'e',
        stop: function(event, ui) {
          // jQuery has added `height: ...' to the thread's style attribute.
          // Unless removed, the therad won't resize itself when child
          // threads are opened/closed.
          thread.css('height', null);
        }
      })
      // Add a resize icon.
      .children('.ui-resizable-e')
      .addClass('ui-icon ui-icon-grip-solid-vertical');
  });
}
catch (e) {
  if (e.name == 'TypeError') console.log(e.name +': Failed to make '+
      'post resizable. Ignoring error (this is a smartphone?)');
  else throw e;
}

// ------- Forms and actions

// Generic cancel button
$('.debiki').delegate('.dw-cancel', 'click', function() {
  $(this).closest('form').remove();
});

// ------- Voting

$('#dw-action-menu .dw-rate').button().click(function(){
  // Warning: Some duplicated code, see .dw-reply and dw-edit click() below.
  var thread = $(this).closest('.dw-thread');
  clearfix(thread); // ensures the rating appears nested inside the thread
  var post = thread.children('.dw-post');
  var $rateForm = rateFormTemplate.clone(true);
  var postId = post.attr('id').substr(8, 999); // drop initial 'dw-post-'
  $rateForm.find("input[name='dw-fi-post']").attr('value', postId);

  // The rating-value inputs are labeled checkboxes. Hence they
  // have ids --- which right now remain the same as the ids
  // in the rateFormTemplate. Make the cloned ids unique:
  makeIdsUniqueUpdateLabels($rateForm, '-post-'+ postId);

  post.after($rateForm);
  // Dismiss action menu
  $('#dw-action-menu').appendTo($('#dw-hidden-templates'));
  // Enable submit button when ratings specified
  $rateForm.find("input[type='checkbox']").click(function(){
    $rateForm.find("input[type='submit']").button("option", "disabled", false);
  });
  //
  // Fancy GUI
  // Seems this must be done *after* the rateFormTemplate has been
  // copied --- otherwise, if the Cancel button is clicked,
  // the rateFormTemplate itself has all its jQueryUI markup removed.
  // (Is that a jQuery bug? Only the *clone* ought to be affected?)
  $rateForm.find('.dw-rat-tag-set input, .dw-submit-set input').button();
  // Disable the submit button (until any checkbox clicked)
  $rateForm.find("input[type='submit']").button("option", "disabled", true);
  $rateForm.find('.dw-show-more-rat-tags').
    button().addClass('dw-linkify-ui-state-default');
});

// Show more rating tags when clicking the "More..." button.
rateFormTemplate.find('.dw-more-rat-tags').hide();
rateFormTemplate.find('.dw-show-more-rat-tags').show().
  click(function() {
    $(this).hide().
      closest('form').find('.dw-more-rat-tags').show();
  });

// ------- Replying

$("#dw-action-menu .dw-reply").button().click(function() {
  // Warning: Some duplicated code, see .dw-rat-tag and dw-edit click() above.
  var thread = $(this).closest('.dw-thread');
  clearfix(thread); // ensures the reply appears nested inside the thread
  var post = thread.children('.dw-post');
  var reply = $("#dw-hidden-templates .dw-reply-template").
                children().clone(true);
  var postId = post.attr('id').substr(8, 999); // drop initial "dw-post-"
  reply.find("input[name='dw-fi-post']").attr('value', postId);
  makeIdsUniqueUpdateLabels(reply, '-post-'+ postId);
  reply.resizable({ alsoResize: reply.find('textarea') });
  post.after(reply);
  // Dismiss action menu
  $('#dw-action-menu').appendTo($('#dw-hidden-templates'));
  // Build fancy jQuery UI widgets
  reply.find('.dw-submit-set input').button();
  reply.find('label').addClass( // color and font that matches <input> buttons
    'dw-color-from-ui-state-default dw-font-from-ui-widget');
  // Resize the root thread (in case this reply-thread is a new child of it).
  DebikiLayout.resizeRootThread(); // see debiki-layout.js
});

// ------- Editing

$("#dw-action-menu .dw-edit").button().click(function() {
  // Warning: Some duplicated code, see .dw-rat-tag and .dw-reply click() above.
  var $thread = $(this).closest('.dw-thread');
  clearfix($thread); // makes edit area appear inside $thread
  var $post = $thread.children('.dw-post');
  //var $actions = $thread.children('.dw-post');
  var $formWrap = $("

").insertAfter($post);
  var postId = $post.attr('id').substr(8, 999); // drop initial "dw-post-"
  $formWrap.load(debateId +'/edits/proposed/post/'+ postId +'.html form',
      function(editFormHtml) {

    var $editForm = $formWrap.find('form');
    var $accordion = $editForm.find('.dw-edit-suggestions');
    // (Concerning > 1, not > 0: One suggestion is a hidden template.)
    var anyEditSuggestions = $accordion.children('h4').length > 1;
    //$editForm.find("input[name='dw-fi-post']").attr('value', postId);
    makeIdsUniqueUpdateLabels($editForm, '-post-'+ postId);

    // Copy the post text to -edit-your tab.
    var curText = '';
    $post.find('.dw-text p').each(
        function(){ curText += $(this).text() + '\n\n'; });
    $editForm.find('textarea').val(curText.trim() + '\n');

    // Make form and accordion resizable
    $accordion.wrap("
");
    var $accwrap = $editForm.find('.dw-resize-accordion');
    $editForm.resizable({
        alsoResize: $accwrap,
        resize: function(){ $accordion.accordion("resize"); },
        minHeight: 100
      });

    // Adjust dimensions. (Smaller if no suggestions, or only my.)
    var initialHeight = anyEditSuggestions ? 300 : 180;
    $editForm.css('width', $post.css('width'));
    $accwrap.css('height', '' + initialHeight + 'px');
    $accordion.accordion("resize"); // parent container height changed

    // jQuery.tabs() must be invoked after above line, weird!
    // Cannot use autoHeight, since other people's edit suggestions
    // might be arbitrary long?
    $accordion.accordion({ autoHeight: false, fillSpace: true });

    // Dismiss action menu
    $('#dw-action-menu').appendTo($('#dw-hidden-templates'));

    $editForm.find('.dw-new-edit-btn').click(function(){
      $(this).remove();
      $editForm.find('.dw-hidden-new-edit').removeClass(
        'dw-hidden-new-edit').addClass(
        'dw-your-edit dw-live-edit dw-your-new-edit');
      $accordion.accordion('activate', '.dw-your-new-edit');
      $editForm.css('height', null); // otherwise it might be too high
    });

    // Build fancy jQuery UI widgets
    $editForm.find(
        "input[type='button'], input[type='submit'], input[type='radio']").
        button();
    $editForm.find('label, .dw-edit-suggestions-label').addClass(
      // color and font matching <input> buttons
      'dw-color-from-ui-state-default dw-font-from-ui-widget');
    // Resize the root thread (in case this reply-thread is a new child of it).
    DebikiLayout.resizeRootThread(); // see debiki-layout.js
  });
});

// ------- Miscellaneous

// Applies the clearfix fix to `thread' iff it has no child threads.
function clearfix(thread) {
  if (!thread.find(':has(.dw-thread)').length) {
    thread.addClass('ui-helper-clearfix');
  }
}

// Finds all tags with an id attribute, and (hopefully) makes
// the ids unique by appending `suffix' to the ids.
// Updates any  `for' attributes to match the new ids.
function makeIdsUniqueUpdateLabels(jqueryObj, suffix) {
  jqueryObj.find("*[id]").each(function(ix) {
      $(this).attr('id', $(this).attr('id') + suffix);
    });
  jqueryObj.find('label').each(function(ix) {
      $(this).attr('for', $(this).attr('for') + suffix);
    });
}

// Highlight the parent post when hovering over a reference.
$(".dw-parent-ref").hover(
  function(event){
    $(this).closest(".dw-thread").parent().closest(".dw-thread").
            children(".dw-post").addClass("dw-highlight");
  },
  function(event){
    $(this).closest(".dw-thread").parent().closest(".dw-thread").
            children(".dw-post").removeClass("dw-highlight");
  });

});

<commit_msg>Fix bug: Action menu disappears.<commit_after>// vim: fdm=marker et ts=2 sw=2

jQuery.noConflict()(function($){

var threadHovered = null;
var didResize = false;
var posts = $(".debiki .dw-post");
var rateFormTemplate = $("#dw-hidden-templates .dw-rat-template form");
var debateId = $('.debiki').attr('id');

$(".dw-post, .dw-thread-info").hover(
  function(event){
    var nextThread = $(this).closest('.dw-thread');

    if ($(this).hasClass('dw-post')) {
      // Show the #action-menu, unless the thread is closing (A)
      // and unless the thread doesn't already have a reply-form (B)
      // or a rating-form (C) or an edit form child (D).
      // (B, C and D: Better not open many action forms at once.)
      if (!nextThread.hasClass('dw-collapsed') &&  // A
          !nextThread.children().filter( // B, C, D
            '.dw-reply-form, .dw-rat-form, .dw-edit-form').length) {
        $(this).after($('#dw-action-menu'))
      }
    }
    else if (nextThread.find('#dw-action-menu').length) {
      // This is a .thread-info and it has the #action-menu inside.
      // This is not a safe place for the menu! If this
      // .thread-info is clicked, the thread will
      // collapsed itself, and the #action-menu will be hidden inside
      // the collapsed thread -- the menu becomes buggy gone -- unless
      // moved to somewhere safe.
      $('#dw-hidden-templates').append($('#dw-action-menu'));
    }

    if (threadHovered && threadHovered[0] == nextThread[0])
      return;

    if ($(this).hasClass('dw-thread-info') &&
        !threadHovered.hasClass('dw-collapsed')) {
      // This .thread-info is not visible (it's only visible by default
      // if the thread is collapsed). Don't fade it in, because the user
      // might be navigating the #action-menu, and then we don't want
      // this .thread-info to appear below that menu. Instead,
      // only open the .thread-info if the threaad's *post* is hovered.
      return;
    }

    // Fade last thread-info, unless thread collapsed.
    if (threadHovered && !threadHovered.hasClass('dw-collapsed')) {
      threadHovered.children('.dw-thread-info')
                      .stop(true, true).fadeTo(1000, 0);
      threadHovered.stop(true, true)
          .removeClass('dw-demarcated')
          .removeClass('dw-demarcated-fx', 1000);
    }
    // Show thread-info for current thread.
    nextThread.children('.dw-thread-info').stop(true, true).fadeTo(600, 1);
    nextThread.stop(true, true)
        .addClass('dw-demarcated') // gives functionality instantly
        .addClass('dw-demarcated-fx', 600); // just for class animation effects
    threadHovered = nextThread;
  },
  function(event){
  });

$(".dw-thread-info").click(function() {
  var thread = $(this).closest(".dw-thread");
  if (! thread.hasClass('dw-demarcated')) return;
  thread
    .children(":not(.dw-thread-info)").stop(true,true).slideToggle(800)
    .end()
    .stop(true, true)
    .toggleClass('dw-collapsed')
    .toggleClass('dw-collapsed-fx', 600);
});

// Outline new posts
(function(){
  var myLastVersion = $.cookie('myLastPageVersion');
  if (!myLastVersion) return;
  var newPosts = posts.filter(function(index){
    return $(this).find('.dw-date').attr('title') > myLastVersion;
  })
  newPosts.closest('.dw-thread').addClass('dw-new');
})()

// Indicate which posts are cropped.
posts.filter('.dw-cropped-s').append(
    '
. . . truncated
');

// Makes whole post visible on click.
//
posts.filter('.dw-cropped-s').click(function(){
  console.log('click: Removind cropped-s.');
  // (Some rather long posts are cropped, using max-width and -height.
  // Don't remove max-width, or some posts might end up rather wide.)
  $(this).closest('.dw-post').removeClass('dw-cropped-s');
})

// ------- Resizing

// Make posts and threads resizable.
// Fails with a TypeError on Android: Cathching it and ignoring it.
// (On Android, posts and threads won't be resizable.)
try {
  posts
  .resizable({
      autoHide: true,
      start: function(event, ui) {
        // Remove max height and width restrictions.
        $(this).closest('.dw-post').removeClass('dw-cropped-s dw-cropped-e');
        didResize = true;
      }
     })
  .find('.ui-resizable-se')
    // Make the resize grip larger.
    .removeClass('.ui-icon-gripsmall-diagonal-se')  // exchange small grip...
    .addClass('ui-icon-grip-diagonal-se')  // ...against the normal one
  .end()
  // Remove max-height and -width when mouse *up* on the resize-e handle.
  // (This is a shortcut to reveal the whole post - only triggered if
  // *clicking* the resize handle, but not dragging it.)
  .find('.ui-resizable-e')
    .mouseup(function(){
      // (Removing only max-width usually results in nothing:
      // The thread usually has a max-width.).
      var post = $(this).closest('.dw-post');
      post.removeClass('dw-cropped-s dw-cropped-e');
      // Expand post eastwards if resize handle was clicked not dragged.
      // (Also expands southwards, but browsers usually expand to east first.)
      if (!didResize) post.css('width', null).css('height', null);
    })
  .end()
  .find('.ui-resizable-s, .ui-resizable-se')
    // Expand post southwards if resize handle was clicked not dragged.
    .mouseup(function(){
      if (!didResize) $(this).closest('.dw-post').css('height', null);
    })
  .end()
  .find('.ui-resizable-handle')
    .mousedown(function(){
      didResize = false;
    })
  .end();

  // Resize threads.
  // Bug: Resizing a thread might toggle it collapsed / expanded, since
  // a click on the .thread-info might happen.
  $('.debiki .dw-thread-info').each(function(index) {
    var thread = $(this).closest('.dw-thread');
    $(this).resizable({
        alsoResize: thread,
        handles: 'e',
        stop: function(event, ui) {
          // jQuery has added `height: ...' to the thread's style attribute.
          // Unless removed, the therad won't resize itself when child
          // threads are opened/closed.
          thread.css('height', null);
        }
      })
      // Add a resize icon.
      .children('.ui-resizable-e')
      .addClass('ui-icon ui-icon-grip-solid-vertical');
  });
}
catch (e) {
  if (e.name == 'TypeError') console.log(e.name +': Failed to make '+
      'post resizable. Ignoring error (this is a smartphone?)');
  else throw e;
}

// ------- Forms and actions

// Generic cancel button
$('.debiki').delegate('.dw-cancel', 'click', function() {
  $(this).closest('form').remove();
});

// ------- Voting

$('#dw-action-menu .dw-rate').button().click(function(){
  // Warning: Some duplicated code, see .dw-reply and dw-edit click() below.
  var thread = $(this).closest('.dw-thread');
  clearfix(thread); // ensures the rating appears nested inside the thread
  var post = thread.children('.dw-post');
  var $rateForm = rateFormTemplate.clone(true);
  var postId = post.attr('id').substr(8, 999); // drop initial 'dw-post-'
  $rateForm.find("input[name='dw-fi-post']").attr('value', postId);

  // The rating-value inputs are labeled checkboxes. Hence they
  // have ids --- which right now remain the same as the ids
  // in the rateFormTemplate. Make the cloned ids unique:
  makeIdsUniqueUpdateLabels($rateForm, '-post-'+ postId);

  post.after($rateForm);
  // Dismiss action menu
  $('#dw-action-menu').appendTo($('#dw-hidden-templates'));
  // Enable submit button when ratings specified
  $rateForm.find("input[type='checkbox']").click(function(){
    $rateForm.find("input[type='submit']").button("option", "disabled", false);
  });
  //
  // Fancy GUI
  // Seems this must be done *after* the rateFormTemplate has been
  // copied --- otherwise, if the Cancel button is clicked,
  // the rateFormTemplate itself has all its jQueryUI markup removed.
  // (Is that a jQuery bug? Only the *clone* ought to be affected?)
  $rateForm.find('.dw-rat-tag-set input, .dw-submit-set input').button();
  // Disable the submit button (until any checkbox clicked)
  $rateForm.find("input[type='submit']").button("option", "disabled", true);
  $rateForm.find('.dw-show-more-rat-tags').
    button().addClass('dw-linkify-ui-state-default');
});

// Show more rating tags when clicking the "More..." button.
rateFormTemplate.find('.dw-more-rat-tags').hide();
rateFormTemplate.find('.dw-show-more-rat-tags').show().
  click(function() {
    $(this).hide().
      closest('form').find('.dw-more-rat-tags').show();
  });

// ------- Replying

$("#dw-action-menu .dw-reply").button().click(function() {
  // Warning: Some duplicated code, see .dw-rat-tag and dw-edit click() above.
  var thread = $(this).closest('.dw-thread');
  clearfix(thread); // ensures the reply appears nested inside the thread
  var post = thread.children('.dw-post');
  var reply = $("#dw-hidden-templates .dw-reply-template").
                children().clone(true);
  var postId = post.attr('id').substr(8, 999); // drop initial "dw-post-"
  reply.find("input[name='dw-fi-post']").attr('value', postId);
  makeIdsUniqueUpdateLabels(reply, '-post-'+ postId);
  reply.resizable({ alsoResize: reply.find('textarea') });
  post.after(reply);
  // Dismiss action menu
  $('#dw-action-menu').appendTo($('#dw-hidden-templates'));
  // Build fancy jQuery UI widgets
  reply.find('.dw-submit-set input').button();
  reply.find('label').addClass( // color and font that matches <input> buttons
    'dw-color-from-ui-state-default dw-font-from-ui-widget');
  // Resize the root thread (in case this reply-thread is a new child of it).
  DebikiLayout.resizeRootThread(); // see debiki-layout.js
});

// ------- Editing

$("#dw-action-menu .dw-edit").button().click(function() {
  // Warning: Some duplicated code, see .dw-rat-tag and .dw-reply click() above.
  var $thread = $(this).closest('.dw-thread');
  clearfix($thread); // makes edit area appear inside $thread
  var $post = $thread.children('.dw-post');
  //var $actions = $thread.children('.dw-post');
  var $formWrap = $("

").insertAfter($post);
  var postId = $post.attr('id').substr(8, 999); // drop initial "dw-post-"
  $formWrap.load(debateId +'/edits/proposed/post/'+ postId +'.html form',
      function(editFormHtml) {

    var $editForm = $formWrap.find('form');
    // Unwrap, since the form must be a thread child (not grandchild)
    // or the action menu will appear if hovering the post.
    $editForm.unwrap();

    var $accordion = $editForm.find('.dw-edit-suggestions');
    // (Concerning > 1, not > 0: One suggestion is a hidden template.)
    var anyEditSuggestions = $accordion.children('h4').length > 1;
    //$editForm.find("input[name='dw-fi-post']").attr('value', postId);
    makeIdsUniqueUpdateLabels($editForm, '-post-'+ postId);

    // Copy the post text to -edit-your tab.
    var curText = '';
    $post.find('.dw-text p').each(
        function(){ curText += $(this).text() + '\n\n'; });
    $editForm.find('textarea').val(curText.trim() + '\n');

    // Make form and accordion resizable
    $accordion.wrap("
");
    var $accwrap = $editForm.find('.dw-resize-accordion');
    $editForm.resizable({
        alsoResize: $accwrap,
        resize: function(){ $accordion.accordion("resize"); },
        minHeight: 100
      });

    // Adjust dimensions. (Smaller if no suggestions, or only my.)
    var initialHeight = anyEditSuggestions ? 300 : 180;
    $editForm.css('width', $post.css('width'));
    $accwrap.css('height', '' + initialHeight + 'px');
    $accordion.accordion("resize"); // parent container height changed

    // jQuery.tabs() must be invoked after above line, weird!
    // Cannot use autoHeight, since other people's edit suggestions
    // might be arbitrary long?
    $accordion.accordion({ autoHeight: false, fillSpace: true });

    // Dismiss action menu
    $('#dw-action-menu').appendTo($('#dw-hidden-templates'));

    $editForm.find('.dw-new-edit-btn').click(function(){
      $(this).remove();
      $editForm.find('.dw-hidden-new-edit').removeClass(
        'dw-hidden-new-edit').addClass(
        'dw-your-edit dw-live-edit dw-your-new-edit');
      $accordion.accordion('activate', '.dw-your-new-edit');
      $editForm.css('height', null); // otherwise it might be too high
    });

    // Build fancy jQuery UI widgets
    $editForm.find(
        "input[type='button'], input[type='submit'], input[type='radio']").
        button();
    $editForm.find('label, .dw-edit-suggestions-label').addClass(
      // color and font matching <input> buttons
      'dw-color-from-ui-state-default dw-font-from-ui-widget');
    // Resize the root thread (in case this reply-thread is a new child of it).
    DebikiLayout.resizeRootThread(); // see debiki-layout.js
  });
});

// ------- Miscellaneous

// Applies the clearfix fix to `thread' iff it has no child threads.
function clearfix(thread) {
  if (!thread.find(':has(.dw-thread)').length) {
    thread.addClass('ui-helper-clearfix');
  }
}

// Finds all tags with an id attribute, and (hopefully) makes
// the ids unique by appending `suffix' to the ids.
// Updates any  `for' attributes to match the new ids.
function makeIdsUniqueUpdateLabels(jqueryObj, suffix) {
  jqueryObj.find("*[id]").each(function(ix) {
      $(this).attr('id', $(this).attr('id') + suffix);
    });
  jqueryObj.find('label').each(function(ix) {
      $(this).attr('for', $(this).attr('for') + suffix);
    });
}

// Highlight the parent post when hovering over a reference.
$(".dw-parent-ref").hover(
  function(event){
    $(this).closest(".dw-thread").parent().closest(".dw-thread").
            children(".dw-post").addClass("dw-highlight");
  },
  function(event){
    $(this).closest(".dw-thread").parent().closest(".dw-thread").
            children(".dw-post").removeClass("dw-highlight");
  });

});

<|endoftext|>


<commit_before>/**
 * Created by JetBrains WebStorm.
 * User: saar
 * Date: 15/02/12
 * Time: 20:22
 * To change this template use File | Settings | File Templates.
 */

var resources = require('jest'),
    util = require('util'),
    models = require('../models'),
    common = require('./common');

    //Authorization
    var Authoriztion = function() {};
    util.inherits(Authoriztion,resources.Authorization);

    Authoriztion.prototype.limit_object_list = function(req, query, callback){
        if(req.session.auth.user){
                    var id = req.session.user_id;
                    query.where('users', id);
                    callback(null, query);
        }else{
            callback("Error: User Is Not Authenticated", null);
        }
    };

var ShoppingCartResource = module.exports = function()
{
    ShoppingCartResource.super_.call(this,models.InformationItem);
    this.allowed_methods = ['get','post', 'put', 'delete'];
    this.authentication = new common.SessionAuthentication();
    this.authorization = new Authoriztion();
    this.default_query = function(query)
    {
        return query.where('is_visible',true).sort('creation_date','descending');
    };
    //this.validation = new resources.Validation();
};

util.inherits(ShoppingCartResource,resources.MongooseResource);

ShoppingCartResource.prototype.update_obj = function(req,object,callback){
    var id = req.session.user_id;
    var is_exist = false;
    for(var i=0; i<object.users.length; i++){
        if (object.users[i] == id){
            is_exist = true;
            break;
        }
    }
    if(is_exist){
        callback({message:"information item is already in shoping cart", code: 401}, null);
    }else{
        object.users.push(req.session.user_id);
        object.save(callback);
    }
}

ShoppingCartResource.prototype.delete_obj = function(req,object,callback){
    for(var i=0; i<object.users.length; i++)
    {
        if(object.users[i] == req.session.user_id)
        {
            object.users.splice(i,1);
            i--;
        }
    }
    object.save(callback);
}
<commit_msg>if null<commit_after>/**
 * Created by JetBrains WebStorm.
 * User: saar
 * Date: 15/02/12
 * Time: 20:22
 * To change this template use File | Settings | File Templates.
 */

var resources = require('jest'),
    util = require('util'),
    models = require('../models'),
    common = require('./common');

    //Authorization
    var Authoriztion = function() {};
    util.inherits(Authoriztion,resources.Authorization);

    Authoriztion.prototype.limit_object_list = function(req, query, callback){
        if(req.session.auth.user){
                    var id = req.session.user_id;
                    query.where('users', id);
                    callback(null, query);
        }else{
            callback("Error: User Is Not Authenticated", null);
        }
    };

var ShoppingCartResource = module.exports = function()
{
    ShoppingCartResource.super_.call(this,models.InformationItem);
    this.allowed_methods = ['get','post', 'put', 'delete'];
    this.authentication = new common.SessionAuthentication();
    this.authorization = new Authoriztion();
    this.default_query = function(query)
    {
        return query.where('is_visible',true).sort('creation_date','descending');
    };
    //this.validation = new resources.Validation();
};

util.inherits(ShoppingCartResource,resources.MongooseResource);

ShoppingCartResource.prototype.update_obj = function(req,object,callback){
    var id = req.session.user_id;
    var is_exist = false;
//    models.InformationItem.update({_id:object._id},{$addToSet:{users:id}},function(err,n)
//    {
//        callback(err,object);
//    });
    object.users = object.users || [];
    for(var i=0; i<object.users.length; i++){
        if (object.users[i] == id){
            is_exist = true;
            break;
        }
    }
    if(is_exist){
        callback({message:"information item is already in shoping cart", code: 401}, null);
    }else{
        object.users.push(req.session.user_id);
        object.save(callback);
    }
}

ShoppingCartResource.prototype.delete_obj = function(req,object,callback){
    for(var i=0; i<object.users.length; i++)
    {
        if(object.users[i] == req.session.user_id)
        {
            object.users.splice(i,1);
            i--;
        }
    }
    object.save(callback);
}
<|endoftext|>