<commit_before>import React from 'react'
import { Link } from 'react-router'
import DocumentTitle from 'react-document-title'
import { config } from 'config'

import { prefixLink } from '../utils/urls.js'

import {
  Row,
  Splash,
  Split,
  ImgGallery,
} from './_sharedComponents'

import CoverImg from '../static/alumni-cover.jpg';

export default function (props) {
  return (
      <DocumentTitle title={"Alumni | " + config.siteTitle}>

          <Splash id="alumni" imageUrl={CoverImg}/>

Alumni

      </DocumentTitle>
  );
}
<commit_msg>text from old alumni page<commit_after>import React from 'react'
import { Link } from 'react-router'
import DocumentTitle from 'react-document-title'
import { config } from 'config'

import { prefixLink } from '../utils/urls.js'

import {
  Row,
  Splash,
  Split,
  ImgGallery,
} from './_sharedComponents'

import CoverImg from '../static/alumni-cover.jpg';

export default function (props) {
  return (
      <DocumentTitle title={"Alumni | " + config.siteTitle}>

          <Splash id="alumni" imageUrl={CoverImg}/>

Alumni

              Once you move beyond your undergrad experience as a zebe, the bonds of brotherhood remain just as strong as ever. While our lives and careers lead us to a variety of locations and situations, MIT ZBT continues to thrive and continue the excellence that we were all a part of. As fellow alumni, we encourage you to come back to the house for alumni weekend, tech reunion, or whenever you're in the area. We also encourage you to give back to help ensure the continued success of the house (you can click on the "donate" button on the left of this page).

Alumni Weekend

The undergraduates host an alumni weekend every year, usually during the spring term. This has always been a great opportunity for alumni to connect with one another and current brothers. Look out for an email for the next alumni weekend or email zbt-president@mit.edu.

Tech Reunion

ZBT hosts an open house every year during MIT's Tech Reunion. All alumni are welcomed and encouraged to stop by. You can email the trustees for more details.

      </DocumentTitle>
  );
}
<|endoftext|>


<commit_before>// dependencies
var fs = require('fs');
var express = require('express');
var routes = require('./routes');
var path = require('path');
var mongoose = require('mongoose');
var path = require('path');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var app = express();
var http = require('http').createServer(app);
var io = require('socket.io').listen(http);
// global config

app.set('port', process.env.PORT || 3001);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.set('view options', { layout: false });
app.use(express.logger());
app.use(express.bodyParser());
app.use(express.favicon());
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
app.use(express.session());
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

app.configure('development', function(){
        app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
        app.use(express.errorHandler());
});

// passport config
var Account = require('./models/account');
passport.use(new LocalStrategy(Account.authenticate()));
passport.serializeUser(Account.serializeUser());
passport.deserializeUser(Account.deserializeUser());
// // mongoose
mongoose.connect('mongodb://localhost/passport_local_mongoose');
// // routes
require('./routes')(app);

/******************************************************************
 * 프로그램 메인 코드
 ******************************************************************/

var ideacards = []; //키워드(문장)의 내용이 저장될 변수.
var key_id = 0;

/******************************************************************
 * keyword의 클래스.
 * 부모 오브젝트, 값, 칼라, id, 깊이값을 속성으로 갖는다.
 ******************************************************************/
function ideacard(p,v,c){
    this.parentKey = p;
    this.keyValue = v;
    this.keyColor = c;
    this.keyId = get_key_id();
    this.diff = get_key_diff();
    /****************************************************************
     * keyword의 깊이를 구해주는 메소드.
     * 재귀적으로 부모 오브젝트를 따라 올라가면서 갯수를 체크함.
     ****************************************************************/
    function get_key_diff(){
        if (this.parentKey == null){
            return 1;
        }
        else {
            return 1 + get_key_diff(this.parentKey);
        }
    }
    /******************************************************************
     * keyword의 id를 지정해주는 메소드.
     * 실질적으로는 데이터베이스와의 연동을 통해서 값을 받아와야함.
     * 현재는 데모의 구현을 위해 임시로 전역변수의 값을 변동시킴.
     ******************************************************************/
    function get_key_id(){
        return (++key_id)-1;
    }
}

/******************************************************************
 * 다이얼로그에 작성된 내용을 바탕으로 카드를 생성해주는 함수.
 * ideacards배열에 새 오브젝트를 생성하여 추가한다.
 * 이후에는 만들어진 카드의 값들을 다시 클라이언트로 넘겨준다.
 ******************************************************************/
function create_card(parent,content,color,x,y){
    ideacards.push(new ideacard(null,content,color));
    var ib = 'ib'+ideacards[ideacards.length-1].keyId;
    io.emit('card created', ideacards[ideacards.length-1].keyValue,ib,color,x,y);
}

/******************************************************************
 * Socket 통신
 *******************************************************************/
io.on('connection', function(socket){
    io.sockets.connected[socket.id].emit('motd',"Welcome to chatroom");
    //접속이 되었을 때 환영 메시지를보냄.

    socket.on('send msg', function(msg){
        console.log(msg);
        io.emit('receive msg', msg);
    });
    //메시지전송요청을 받으면, 해당 메시지를 전체에 브로드캐스팅.

    socket.on('request create card',function(parent,content,color,x,y){
        create_card(parent, content, color);
    });
});

app.listen(app.get('port'), function(){
   console.log(("Express server listening on port " + app.get('port')))
 });

<commit_msg>resolving socket.io.js 404 error<commit_after>// dependencies
var fs = require('fs');
var express = require('express');
var routes = require('./routes');
var path = require('path');
var mongoose = require('mongoose');
var path = require('path');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io').listen(server);
// global config

app.set('port', process.env.PORT || 3001);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.set('view options', { layout: false });
app.use(express.logger());
app.use(express.bodyParser());
app.use(express.favicon());
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
app.use(express.session());
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

app.configure('development', function(){
        app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
        app.use(express.errorHandler());
});

// passport config
var Account = require('./models/account');
passport.use(new LocalStrategy(Account.authenticate()));
passport.serializeUser(Account.serializeUser());
passport.deserializeUser(Account.deserializeUser());
// // mongoose
mongoose.connect('mongodb://localhost/passport_local_mongoose');
// // routes
require('./routes')(app);

/******************************************************************
 * 프로그램 메인 코드
 ******************************************************************/

var ideacards = []; //키워드(문장)의 내용이 저장될 변수.
var key_id = 0;

/******************************************************************
 * keyword의 클래스.
 * 부모 오브젝트, 값, 칼라, id, 깊이값을 속성으로 갖는다.
 ******************************************************************/
function ideacard(p,v,c){
    this.parentKey = p;
    this.keyValue = v;
    this.keyColor = c;
    this.keyId = get_key_id();
    this.diff = get_key_diff();
    /****************************************************************
     * keyword의 깊이를 구해주는 메소드.
     * 재귀적으로 부모 오브젝트를 따라 올라가면서 갯수를 체크함.
     ****************************************************************/
    function get_key_diff(){
        if (this.parentKey == null){
            return 1;
        }
        else {
            return 1 + get_key_diff(this.parentKey);
        }
    }
    /******************************************************************
     * keyword의 id를 지정해주는 메소드.
     * 실질적으로는 데이터베이스와의 연동을 통해서 값을 받아와야함.
     * 현재는 데모의 구현을 위해 임시로 전역변수의 값을 변동시킴.
     ******************************************************************/
    function get_key_id(){
        return (++key_id)-1;
    }
}

/******************************************************************
 * 다이얼로그에 작성된 내용을 바탕으로 카드를 생성해주는 함수.
 * ideacards배열에 새 오브젝트를 생성하여 추가한다.
 * 이후에는 만들어진 카드의 값들을 다시 클라이언트로 넘겨준다.
 ******************************************************************/
function create_card(parent,content,color,x,y){
    ideacards.push(new ideacard(null,content,color));
    var ib = 'ib'+ideacards[ideacards.length-1].keyId;
    io.emit('card created', ideacards[ideacards.length-1].keyValue,ib,color,x,y);
}

/******************************************************************
 * Socket 통신
 *******************************************************************/
io.on('connection', function(socket){
    io.sockets.connected[socket.id].emit('motd',"Welcome to chatroom");
    //접속이 되었을 때 환영 메시지를보냄.

    socket.on('send msg', function(msg){
        console.log(msg);
        io.emit('receive msg', msg);
    });
    //메시지전송요청을 받으면, 해당 메시지를 전체에 브로드캐스팅.

    socket.on('request create card',function(parent,content,color,x,y){
        create_card(parent, content, color);
    });
});

app.listen(app.get('port'), function(){
   console.log(("Express server listening on port " + app.get('port')))
 });

<|endoftext|>


<commit_before>/*
Copyright 2014 Newcastle University

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

/*
 This extension adds a JME data type to represent permutations.
*/

Numbas.queueScript('extensions/permutations/permutations.js',['jme','jme-display'],function() {
	var permutations = Numbas.extensions.permutations = {};

	var Permutation = permutations.Permutation = function(to) {
		if(to==undefined) {
			to = [];
		}
		for(var i=0;i<to.length;i++) {
			if(to[i]===undefined) {
				to[i] = i;
			}
		}
		this.to = to;
		this.n = this.to.length;

		var l = this.to.slice().sort();
		var old = null;
		for(var i=0;i<l.length;i++) {
			if(l[i]==old) {
				throw(new Numbas.Error("Permutation map is not a bijection: "+this.to));
			}
			old = l[i];
		}
	}
	Permutation.prototype = {
		map: function(n) {
			if(n<0) {
				throw(new Numbas.Error("Permutations only work on natural numbers, not "+(n+1)+"."));
			} else if(n<this.n) {
				return this.to[n];
			} else {
				return n;
			}
		},
		inverse: function() {
			var out = [];
			for(var i=0;i<this.n;i++) {
				out[this.to[i]] = i;
			}
			return new Permutation(out);
		},
		compose: function(p2) {
			var out = this.to.slice();
			var l = Math.max(this.n,p2.n);
			for(var i=0;i<l;i++) {
				out[i] = p2.map(this.map(i));
			}
			return new Permutation(out);
		},
		equal: function(p2) {
			var e = this.compose(p2.inverse());
			for(var i=0;i<e.n;i++) {
				if(e.to[i]!=i) {
					return false;
				}
			}
			return true;
		},
		cycles: function() {
			if(this._cycles) {
				return this._cycles;
			}
			var tmp = [];
			var cycles = [];
			for(var i=0;i<this.n;i++) {
				if(!tmp[i]) {
					var cycle = [i];
					i = this.map(i);
					var acc = 0;
					while(i!==cycle[0]) {
						cycle.push(i);
						var oi = i;
						i = this.map(i);
						if(i===undefined) {
							throw(new Numbas.Error("Map goes to undefined"));
						}
						tmp[oi] = true;

						acc++;
						if(acc==10000) {
							throw(new Numbas.Error("Not a cycle!"));
						}
					}
					cycles.push(cycle);
				}
			}
			this._cycles = cycles;
			return cycles;
		},
		order: function() {
			var cycles = this.cycles().map(function(c){return c.length});
			if(cycles.length==1) {
				return cycles[0];
			} else {
				return lcm.apply(this,cycles);
			}
		},
		even: function() {
			return !((this.n - this.cycles().length)%2);
		},

		toString: function() {
			var cycles = this.cycles();
			var out = '';
			for(var i = 0;i<cycles.length;i++) {
				if(cycles[i].length>1) {
					out += '('+cycles[i].map(function(n){return n+1}).join(',')+')';
				}
			}
			return out;
		},
		twoLineLaTeX: function() {
			var out = '\\left[\\begin{array}{c} ';
			for(var i=0;i<this.n;i++) {
				if(i>0) {
					out +=' & ';
				}
				out += (i+1);
			}
			out += ' \\\\ ';
			for(var i=0;i<this.n;i++) {
				if(i>0) {
					out +=' & ';
				}
				out += (this.map(i)+1);
			}
			out += ' \\end{array} \\right]';
			return out;
		},
		asTranspositions: function() {
			var cycles = this.cycles();
			var out = '';
			for(var i=0;i<cycles.length;i++) {
				var cycle = cycles[i];
				if(cycle.length>1) {
					for(var j=cycle.length-1;j>0;j--) {
						out += '('+(cycle[0]+1)+','+(cycle[j]+1)+')';
					}
				}
			}
			return out;
		},

		is_rotation: function(p) {
			var start = p.to[n-1];
			var od = p.to[0]-start;
			var d;
			if(od<-p.n/2) {
				od += p.n;
			}
			if(od!=1) {
				return false;
			}
			for(var i=0;i<p.n;i++) {
				d = p.to[i]-start;
				if(d<-n/2) {
					d += p.n;
				}
				if(d!=1) {
					return false;
				}
				od = d;
				start = p.to[i];
			}
			return true;
		},
		is_flip: function(p) {
			return is_rotation(p.compose(dihedral(p.n).flip));
		}

	}

	var re_cycle_commas = /\(\s*(\d+(?:\s*,\s*\d+)*)\s*\)/g;
	var re_cycle_spaces = /\(\s*(\d+(?:\s+\d+)*)\s*\)/g;
	var re_cycle_elements = /\s*[,\s]\s*/g;
	var re_cycles = new RegExp('^\\s*(('+re_cycle_commas.source+')*|('+re_cycle_spaces.source+')*)\\s*$');
	Permutation.parse = function(str) {
		if(!re_cycles.test(str)) {
			throw(new Error('Invalid disjoint cycle notation: '+str));
		}
		var r = str.indexOf(',')>=0 ? re_cycle_commas : re_cycle_spaces;
		r.index = 0;
		var m;
		var cycles = [];
		while(m=r.exec(str)) {
			var numbers = m[1].trim().split(re_cycle_elements);
			numbers = numbers.map(function(n){ return parseInt(n)-1; });
			cycles.push(numbers);
		}
		return cycles;
	}
	Permutation.fromString = function(str) {
		var bits = Permutation.parse(str);
		var cycles = [];
		for(var i=0;i<bits.length;i++) {
			var numbers = bits[i];
			var to = [];
			for(var j=0;j<numbers.length-1;j++) {
				to[numbers[j]] = numbers[j+1];
			}
			to[numbers[numbers.length-1]] = numbers[0];
			cycles.push(to);
		}
		var out;
		if(cycles.length) {
			out = cycles[cycles.length-1];
			if(cycles.length>1) {
				for(var i=cycles.length-2;i>=0;i--) {
					var cycle = cycles[i];
					var l = Math.max(cycle.length,out.length);
					var nout = [];
					for(var j=0;j<l;j++) {
						var ot = out[j]===undefined ? j : out[j];
						out[j] = cycle[ot]===undefined ? ot : cycle[ot];
					}
				}
			}
		} else {
			out = [];
		}
		return new Permutation(out);
	}

	Permutation.is_disjoint = function(str) {
		var seen = {};
		var cycles = Permutation.parse(str);
		for(var i=0;i<cycles.length;i++) {
			var cycle = cycles[i];
			for(var j=0;j<cycle.length;j++) {
				var n = cycle[j];
				if(n!==undefined) {
					if(seen[n]) {
						return false;
					} else {
						seen[n] = true;
					}
				}
			}
		}
		return true;
	}

	Permutation.is_transpositions = function(s) {
		var cycles = Permutation.parse(s);
		for(var i=0;i<cycles.length;i++) {
			if(cycles[i].length!=2) {
				return false;
			}
		}
		return true;
	}

	function gcd(a,b) {
		var c;
		if(a<b) {
			c = a;
			a = b;
			b = c;
		}
		var m;
		while(m=a%b) {
			a=b;
			b=m;
		}
		return b;
	}
	function lcm(a,b) {
		if(arguments.length>2) {
			for(var i=1;i<arguments.length;i++) {
				b = arguments[i];
				a = a*b/gcd(a,b);
			}
			return a;
		}
		return a*b/gcd(a,b);
	}

	permutations.dihedral = function(n) {
		var rotate = [];
		for(var i=1;i<n;i++) {
			rotate.push(i);
		}
		rotate.push(0);
		var flip = [];
		for(var i=0;i<n;i++) {
			flip.push(n-1-i);
		}
		rotate = new Permutation(rotate);
		flip = new Permutation(flip);
		return {flip: flip, rotate: rotate};
	}

	var jme = Numbas.jme;
	var funcObj = jme.funcObj;
	var types = jme.types;
	var TList = jme.types.TList;

	var scope = permutations.scope = new Numbas.jme.Scope();

	var TPerm = Numbas.jme.types.permutation = Numbas.jme.types.TPerm = function(p) {
		this.value = p;
	};
	TPerm.prototype.type = 'permutation';

	function jme_perm(l) {
		l = l.map(function(i){ return i.value; });
		return new Permutation(l);
	}
	function jme_perm_fromString(s) {
		return Permutation.fromString(s);
	}
	scope.addFunction(new funcObj('permutation',[TList],TPerm,jme_perm));
	scope.addFunction(new funcObj('permutation',[types.TString],TPerm,jme_perm_fromString));
	scope.addFunction(new funcObj('perm',[TList],TPerm,jme_perm));
	scope.addFunction(new funcObj('perm',[types.TString],TPerm,jme_perm_fromString));
	scope.addFunction(new funcObj('*',[TPerm,TPerm],TPerm,function(a,b) {
		return b.compose(a);
	}));
	scope.addFunction(new funcObj('compose',[TPerm,'*TPerm'],TPerm,function() {
		var p = arguments[arguments.length-1];
		for(var i=arguments.length-2;i>=0;i--) {
			p = p.compose(arguments[i]);
		}
		return p;
	}));
	scope.addFunction(new funcObj('listval',[TPerm,types.TNum],types.TNum,function(p,n) {
		return p.map(n-1)+1;
	}));
	scope.addFunction(new funcObj('inverse',[TPerm],TPerm,function(p) {
		return p.inverse();
	}));
	scope.addFunction(new funcObj('even',[TPerm],types.TBool,function(p) {
		return p.even();
	}));
	scope.addFunction(new funcObj('order',[TPerm],types.TNum,function(p) {
		return p.order();
	}));
	scope.addFunction(new funcObj('cycles',[TPerm],TList,function(p) {
		return jme.wrapValue(p.cycles()).value;
	}));
	scope.addFunction(new funcObj('nontrivial_cycles',[TPerm],TList,function(p) {
		return jme.wrapValue(p.cycles().filter(function(c){return c.length>1})).value;
	}));
	scope.addFunction(new funcObj('show',[TPerm],types.TString,null,{
		evaluate: function(args,scope) {
			var p = args[0].value;
			var s = new types.TString(p.toString());
			s.latex = true;
			return s;
		}
	}));
	scope.addFunction(new funcObj('twoline',[TPerm],types.TString,null,{
		evaluate: function(args,scope) {
			var p = args[0].value;
			var s = new types.TString(p.twoLineLaTeX());
			s.latex = true;
			return s;
		}
	}));
	scope.addFunction(new funcObj('as_transpositions',[TPerm],types.TString,null,{
		evaluate: function(args,scope) {
			var p = args[0].value;
			var s = new types.TString(p.asTranspositions());
			s.latex = true;
			return s;
		}
	}));

	Numbas.util.equalityTests.permutation = function(a,b) {
		return a.value.equal(b.value);
	};

	Numbas.jme.display.typeToTeX.permutation = function(thing,tok,texArgs,settings) {
		return tok.value.toString();
	};

	Numbas.jme.display.typeToJME.permutation = function(thing,tok,bits,settings) {
		return 'perm("'+tok.value.toString()+'")';
	};

});
<commit_msg>use addExtension<commit_after>/*
Copyright 2014 Newcastle University

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

/*
 This extension adds a JME data type to represent permutations.
*/

Numbas.addExtension('permutations',['jme','jme-display'],function(permutations) {
	var Permutation = permutations.Permutation = function(to) {
		if(to==undefined) {
			to = [];
		}
		for(var i=0;i<to.length;i++) {
			if(to[i]===undefined) {
				to[i] = i;
			}
		}
		this.to = to;
		this.n = this.to.length;

		var l = this.to.slice().sort();
		var old = null;
		for(var i=0;i<l.length;i++) {
			if(l[i]==old) {
				throw(new Numbas.Error("Permutation map is not a bijection: "+this.to));
			}
			old = l[i];
		}
	}
	Permutation.prototype = {
		map: function(n) {
			if(n<0) {
				throw(new Numbas.Error("Permutations only work on natural numbers, not "+(n+1)+"."));
			} else if(n<this.n) {
				return this.to[n];
			} else {
				return n;
			}
		},
		inverse: function() {
			var out = [];
			for(var i=0;i<this.n;i++) {
				out[this.to[i]] = i;
			}
			return new Permutation(out);
		},
		compose: function(p2) {
			var out = this.to.slice();
			var l = Math.max(this.n,p2.n);
			for(var i=0;i<l;i++) {
				out[i] = p2.map(this.map(i));
			}
			return new Permutation(out);
		},
		equal: function(p2) {
			var e = this.compose(p2.inverse());
			for(var i=0;i<e.n;i++) {
				if(e.to[i]!=i) {
					return false;
				}
			}
			return true;
		},
		cycles: function() {
			if(this._cycles) {
				return this._cycles;
			}
			var tmp = [];
			var cycles = [];
			for(var i=0;i<this.n;i++) {
				if(!tmp[i]) {
					var cycle = [i];
					i = this.map(i);
					var acc = 0;
					while(i!==cycle[0]) {
						cycle.push(i);
						var oi = i;
						i = this.map(i);
						if(i===undefined) {
							throw(new Numbas.Error("Map goes to undefined"));
						}
						tmp[oi] = true;

						acc++;
						if(acc==10000) {
							throw(new Numbas.Error("Not a cycle!"));
						}
					}
					cycles.push(cycle);
				}
			}
			this._cycles = cycles;
			return cycles;
		},
		order: function() {
			var cycles = this.cycles().map(function(c){return c.length});
			if(cycles.length==1) {
				return cycles[0];
			} else {
				return lcm.apply(this,cycles);
			}
		},
		even: function() {
			return !((this.n - this.cycles().length)%2);
		},

		toString: function() {
			var cycles = this.cycles();
			var out = '';
			for(var i = 0;i<cycles.length;i++) {
				if(cycles[i].length>1) {
					out += '('+cycles[i].map(function(n){return n+1}).join(',')+')';
				}
			}
			return out;
		},
		twoLineLaTeX: function() {
			var out = '\\left[\\begin{array}{c} ';
			for(var i=0;i<this.n;i++) {
				if(i>0) {
					out +=' & ';
				}
				out += (i+1);
			}
			out += ' \\\\ ';
			for(var i=0;i<this.n;i++) {
				if(i>0) {
					out +=' & ';
				}
				out += (this.map(i)+1);
			}
			out += ' \\end{array} \\right]';
			return out;
		},
		asTranspositions: function() {
			var cycles = this.cycles();
			var out = '';
			for(var i=0;i<cycles.length;i++) {
				var cycle = cycles[i];
				if(cycle.length>1) {
					for(var j=cycle.length-1;j>0;j--) {
						out += '('+(cycle[0]+1)+','+(cycle[j]+1)+')';
					}
				}
			}
			return out;
		},

		is_rotation: function(p) {
			var start = p.to[n-1];
			var od = p.to[0]-start;
			var d;
			if(od<-p.n/2) {
				od += p.n;
			}
			if(od!=1) {
				return false;
			}
			for(var i=0;i<p.n;i++) {
				d = p.to[i]-start;
				if(d<-n/2) {
					d += p.n;
				}
				if(d!=1) {
					return false;
				}
				od = d;
				start = p.to[i];
			}
			return true;
		},
		is_flip: function(p) {
			return is_rotation(p.compose(dihedral(p.n).flip));
		}

	}

	var re_cycle_commas = /\(\s*(\d+(?:\s*,\s*\d+)*)\s*\)/g;
	var re_cycle_spaces = /\(\s*(\d+(?:\s+\d+)*)\s*\)/g;
	var re_cycle_elements = /\s*[,\s]\s*/g;
	var re_cycles = new RegExp('^\\s*(('+re_cycle_commas.source+')*|('+re_cycle_spaces.source+')*)\\s*$');
	Permutation.parse = function(str) {
		if(!re_cycles.test(str)) {
			throw(new Error('Invalid disjoint cycle notation: '+str));
		}
		var r = str.indexOf(',')>=0 ? re_cycle_commas : re_cycle_spaces;
		r.index = 0;
		var m;
		var cycles = [];
		while(m=r.exec(str)) {
			var numbers = m[1].trim().split(re_cycle_elements);
			numbers = numbers.map(function(n){ return parseInt(n)-1; });
			cycles.push(numbers);
		}
		return cycles;
	}
	Permutation.fromString = function(str) {
		var bits = Permutation.parse(str);
		var cycles = [];
		for(var i=0;i<bits.length;i++) {
			var numbers = bits[i];
			var to = [];
			for(var j=0;j<numbers.length-1;j++) {
				to[numbers[j]] = numbers[j+1];
			}
			to[numbers[numbers.length-1]] = numbers[0];
			cycles.push(to);
		}
		var out;
		if(cycles.length) {
			out = cycles[cycles.length-1];
			if(cycles.length>1) {
				for(var i=cycles.length-2;i>=0;i--) {
					var cycle = cycles[i];
					var l = Math.max(cycle.length,out.length);
					var nout = [];
					for(var j=0;j<l;j++) {
						var ot = out[j]===undefined ? j : out[j];
						out[j] = cycle[ot]===undefined ? ot : cycle[ot];
					}
				}
			}
		} else {
			out = [];
		}
		return new Permutation(out);
	}

	Permutation.is_disjoint = function(str) {
		var seen = {};
		var cycles = Permutation.parse(str);
		for(var i=0;i<cycles.length;i++) {
			var cycle = cycles[i];
			for(var j=0;j<cycle.length;j++) {
				var n = cycle[j];
				if(n!==undefined) {
					if(seen[n]) {
						return false;
					} else {
						seen[n] = true;
					}
				}
			}
		}
		return true;
	}

	Permutation.is_transpositions = function(s) {
		var cycles = Permutation.parse(s);
		for(var i=0;i<cycles.length;i++) {
			if(cycles[i].length!=2) {
				return false;
			}
		}
		return true;
	}

	function gcd(a,b) {
		var c;
		if(a<b) {
			c = a;
			a = b;
			b = c;
		}
		var m;
		while(m=a%b) {
			a=b;
			b=m;
		}
		return b;
	}
	function lcm(a,b) {
		if(arguments.length>2) {
			for(var i=1;i<arguments.length;i++) {
				b = arguments[i];
				a = a*b/gcd(a,b);
			}
			return a;
		}
		return a*b/gcd(a,b);
	}

	permutations.dihedral = function(n) {
		var rotate = [];
		for(var i=1;i<n;i++) {
			rotate.push(i);
		}
		rotate.push(0);
		var flip = [];
		for(var i=0;i<n;i++) {
			flip.push(n-1-i);
		}
		rotate = new Permutation(rotate);
		flip = new Permutation(flip);
		return {flip: flip, rotate: rotate};
	}

	var jme = Numbas.jme;
	var funcObj = jme.funcObj;
	var types = jme.types;
	var TList = jme.types.TList;

	var scope = permutations.scope = new Numbas.jme.Scope();

	var TPerm = Numbas.jme.types.permutation = Numbas.jme.types.TPerm = function(p) {
		this.value = p;
	};
	TPerm.prototype.type = 'permutation';

	function jme_perm(l) {
		l = l.map(function(i){ return i.value; });
		return new Permutation(l);
	}
	function jme_perm_fromString(s) {
		return Permutation.fromString(s);
	}
	scope.addFunction(new funcObj('permutation',[TList],TPerm,jme_perm));
	scope.addFunction(new funcObj('permutation',[types.TString],TPerm,jme_perm_fromString));
	scope.addFunction(new funcObj('perm',[TList],TPerm,jme_perm));
	scope.addFunction(new funcObj('perm',[types.TString],TPerm,jme_perm_fromString));
	scope.addFunction(new funcObj('*',[TPerm,TPerm],TPerm,function(a,b) {
		return b.compose(a);
	}));
	scope.addFunction(new funcObj('compose',[TPerm,'*TPerm'],TPerm,function() {
		var p = arguments[arguments.length-1];
		for(var i=arguments.length-2;i>=0;i--) {
			p = p.compose(arguments[i]);
		}
		return p;
	}));
	scope.addFunction(new funcObj('listval',[TPerm,types.TNum],types.TNum,function(p,n) {
		return p.map(n-1)+1;
	}));
	scope.addFunction(new funcObj('inverse',[TPerm],TPerm,function(p) {
		return p.inverse();
	}));
	scope.addFunction(new funcObj('even',[TPerm],types.TBool,function(p) {
		return p.even();
	}));
	scope.addFunction(new funcObj('order',[TPerm],types.TNum,function(p) {
		return p.order();
	}));
	scope.addFunction(new funcObj('cycles',[TPerm],TList,function(p) {
		return jme.wrapValue(p.cycles()).value;
	}));
	scope.addFunction(new funcObj('nontrivial_cycles',[TPerm],TList,function(p) {
		return jme.wrapValue(p.cycles().filter(function(c){return c.length>1})).value;
	}));
	scope.addFunction(new funcObj('show',[TPerm],types.TString,null,{
		evaluate: function(args,scope) {
			var p = args[0].value;
			var s = new types.TString(p.toString());
			s.latex = true;
			return s;
		}
	}));
	scope.addFunction(new funcObj('twoline',[TPerm],types.TString,null,{
		evaluate: function(args,scope) {
			var p = args[0].value;
			var s = new types.TString(p.twoLineLaTeX());
			s.latex = true;
			return s;
		}
	}));
	scope.addFunction(new funcObj('as_transpositions',[TPerm],types.TString,null,{
		evaluate: function(args,scope) {
			var p = args[0].value;
			var s = new types.TString(p.asTranspositions());
			s.latex = true;
			return s;
		}
	}));

	Numbas.util.equalityTests.permutation = function(a,b) {
		return a.value.equal(b.value);
	};

	Numbas.jme.display.typeToTeX.permutation = function(thing,tok,texArgs,settings) {
		return tok.value.toString();
	};

	Numbas.jme.display.typeToJME.permutation = function(thing,tok,bits,settings) {
		return 'perm("'+tok.value.toString()+'")';
	};

});
<|endoftext|>


<commit_before>;(function($){

  function arrayIndexOf(r, num){
    if( Array.prototype.indexOf ){
      return r.indexOf(num);
    }else{
      for(var i=0, len=r.length; i<len; i++){
        if( r[i] === num ) return i;
      }

      return -1;
    }
  }//end arrayIndexOf

  function setStyle(elem, obj){
    for(var i in obj){
      if( obj.hasOwnProperty(i) ){
        elem["style"][i] = obj[i];
      }
    }
  }//end setStyle

  /**
    @x   xֵ
    @y   yֵ
    @ret ңϵ£ĳΧֵ鼯

    ʼַΧִ 0 ʼ磺
    0,  1,  2,  3,  4,
    5,  6,  7,  8,  9,
    10, 11, 12, 13, 14

    0ĸ[-1, 5, -1, 1]
    6ĸ[1,  11, 5, 7]
  */
  function getRangeNum(x, y){
    var ret = [],
        cur = 0,
        i, j, temp;

    for(i=0; i<y; i++){
      for(j=0; j<x; j++){

        temp = [
          i > 0     ? cur - x : -1,   //
          i < y - 1 ? cur + x : -1,   //
          j > 0     ? cur - 1 : -1,   //
          j < x - 1 ? cur + 1 : -1    //
        ];

        ret.push(temp);

        cur++;

      }//for
    }//for

    return ret;
  }//end getRangeNum

  var direction = ["up", "down", "left", "right"];

  var emptyFun = function(){};

  function pintu(option){

    var imgSrc = option.imgSrc;
    var imgWidth = option.imgWidth;
    var imgHeight = option.imgHeight;
    var block = option.block || 100;
    var id = option.id || "J_paper_pintu";
    var begin = option.begin || emptyFun;
    var success = option.success || emptyFun;

    var x = parseInt( imgWidth / block, 10);
    var y = parseInt( imgHeight / block, 10);

    var num = 0;
    var beginEmpty = 0;
    var empty = 0;

    $(function(){
      var $id = $("#" + id);
      var frag = document.createDocumentFragment();
      var i, j, div, span;

      for(i = 0; i < y; i++){     //
        for(j = 0; j < x; j++){   //

          //һ
          if( i == y-1 && j == x-1 ){
            empty = num;
            beginEmpty = num;
            break;
          }

          div = document.createElement("div");

          var imgx = block * j * -1 + "px";
          var imgy = block * i * -1 + "px";

          setStyle(div, {
            width : block + "px",
            height : block + "px",
            left : block * j + "px",
            top : block * i + "px",
            background : "url("+ imgSrc +") "+ imgx +" "+ imgy +" no-repeat"
          });

          div.setAttribute("data-num", num);
          num++;

          frag.appendChild(div);
        }//for
      }//for

      $id.css({
        width : x * block + "px",
        height : y * block + "px",
      }).append(frag);

      var $blocks = $id.find("div");
      var blocksLength = $blocks.length;
      var ret = getRangeNum(x, y);

      function moveBlock($elem, isHuman){
        if( isHuman && begin ){
          begin();
          begin = null;
        }

        var num = +$elem.attr("data-num"),
            rangeNum = ret[num],
            i = arrayIndexOf(rangeNum, empty);

        if( i > -1 ){
          $elem.attr("data-num", empty);
          empty = num;

          //ƶ
          switch( direction[i] ){
            case "up"    : $elem.css("top",  parseInt($elem.css("top"),  10) - block + "px"); break;
            case "down"  : $elem.css("top",  parseInt($elem.css("top"),  10) + block + "px"); break;
            case "left"  : $elem.css("left", parseInt($elem.css("left"), 10) - block + "px"); break;
            case "right" : $elem.css("left", parseInt($elem.css("left"), 10) + block + "px"); break;
          }

          isHuman && checkSuccess();
        }
      }//end moveBlock

      //ͼƬ
      function randomBlocks(){
        var max = 1000;
        var a = 0;

        function fn(){
          if( a++ > max ) return;

          var r = ret[empty];
          var n = parseInt( Math.random()*4, 10); // 0 - 3
          var m = r[n];

          if( m != -1 && m < blocksLength ){
            moveBlock( $blocks.eq(m) );
          }

          fn();
        }

        fn();
      }//end randomBlocks

      //жǷɹ
      function checkSuccess(){
        if( empty == beginEmpty){

          for(var i = 0; i < blocksLength; i++){
            if( +$blocks.eq(i).attr("data-num") != i ){
              return;
            }
          }

          success();
        }
      }//end checkSuccess

      randomBlocks();

      $id.on("click", "div", function(){

        moveBlock($(this), true);

      });

    });//dom ready

  }//end pintu

  window.pintu = pintu;

})(jQuery);

<commit_msg>pinttu2 v1.0<commit_after>;
(function ($) {
  function arrayIndexOf(r, num) {
    if (Array.prototype.indexOf) {
      //console.log(r);
      return r.indexOf(num);
    } else {
      for (var i = 0, len = r.length; i < len; i++) {
        if (r[i] === num)
          return i;
      }
      return -1;
    }
  } //end arrayIndexOf

  function setStyle(elem, obj) {
    for (var i in obj) {
      if (obj.hasOwnProperty(i)) {
        elem["style"][i] = obj[i];
      }
    }
  } //end setStyle

  /**
  @x x轴最大值
  @y y轴最大值
  @ret 从左到右，从上到下，某个数字周围数字的数组集合
  初始化数字范围。数字从 0 开始填，比如：
  0, 1, 2, 3, 4,
  5, 6, 7, 8, 9,
  10, 11, 12, 13, 14
  0的附近是[-1, 5, -1, 1]
  6的附近是[1, 11, 5, 7]
   */
  function getRangeNum(x, y) {
    var ret = [],
    cur = 0,
    i,
    j,
    temp;
    for (i = 0; i < y; i++) {
      for (j = 0; j < x; j++) {
        temp = [
          i > 0 ? cur - x : -1, //上
          i < y - 1 ? cur + x : -1, //下
          j > 0 ? cur - 1 : -1, //左
          j < x - 1 ? cur + 1 : -1 //右
        ];
        ret.push(temp);
        cur++;
      } //for
    } //for
    return ret;
  } //end getRangeNum

  var direction = ["up", "down", "left", "right"];
  var emptyFun = function () {};

  function pintu(option) {
    var imgSrc = option.imgSrc;
    var imgWidth = option.imgWidth;
    var imgHeight = option.imgHeight;
    var block = option.block || 100;
    var id = option.id || "J_paper_pintu";
    var begin = option.begin || emptyFun;
    var success = option.success || emptyFun;
    var x = parseInt(imgWidth / block, 10);
    var y = parseInt(imgHeight / block, 10);
    var num = 0;
    var beginEmpty = 0;
    var empty = 0;

    $(function () {
      var $id = $("#" + id);
      var frag = document.createDocumentFragment();
      var i, j, div, span;

      for (i = 0; i < y; i++) { //行
        for (j = 0; j < x; j++) { //列
          div = document.createElement("div");
          div.className = "paper-pintu-block";
          var imgx = block * j * -1 + "px";
          var imgy = block * i * -1 + "px";
          setStyle(div, {
            width : block + "px",
            height : block + "px",
            left : block * j + "px",
            top : block * i + "px",
            background : "url(" + imgSrc + ") " + imgx + " " + imgy + " no-repeat"
          });
          // 最后一格
          if (i == y - 1 && j == x - 1) {
            empty = num;
            beginEmpty = num;
            // 先设置隐藏，success时再显示出来
            setStyle(div, {
              display : "none"
            });
            div.className = "paper-pintu-last-block";
          } else {
            div.setAttribute("data-num", num);
            num++;
          }
          frag.appendChild(div);
        } //for
      } //for

      $id.css({
        width : x * block + "px",
        height : y * block + "px",
      }).append(frag);

      var $blocks = $id.find(".paper-pintu-block");
      var blocksLength = $blocks.length;
      var ret = getRangeNum(x, y);
      var $lastBlock = $id.find(".paper-pintu-last-block");

      // 方块移动
      function moveBlock($elem, isHuman) {
        if (isHuman && begin) {
          begin();
          begin = null;
        }
        var num = +$elem.attr("data-num"),
        rangeNum = ret[num],
        i = arrayIndexOf(rangeNum, empty);
        if (i > -1) {
          $elem.attr("data-num", empty);
          empty = num;
          //移动方块
          switch (direction[i]) {
          case "up":
            $elem.css("top", parseInt($elem.css("top"), 10) - block + "px");
            break;
          case "down":
            $elem.css("top", parseInt($elem.css("top"), 10) + block + "px");
            break;
          case "left":
            $elem.css("left", parseInt($elem.css("left"), 10) - block + "px");
            break;
          case "right":
            $elem.css("left", parseInt($elem.css("left"), 10) + block + "px");
            break;
          }
          isHuman && checkSuccess();
        }
      } //end moveBlock

      //随机打乱图片
      function randomBlocks() {
        var max = 1000;
        var a = 0;
        function fn() {
          if (a++ > max)
            return;
          var r = ret[empty];
          var n = parseInt(Math.random() * 4, 10); // 0 - 3
          var m = r[n];
          if (m != -1 && m < blocksLength) {
            moveBlock($blocks.eq(m));
          }
          fn();
        }
        fn();
      } //end randomBlocks

      //判断是否成功
      function checkSuccess() {
        if (empty == beginEmpty) {
          for (var i = 0; i < blocksLength; i++) {
            if (+$blocks.eq(i).attr("data-num") != i) {
              return;
            }
          }
          $lastBlock.show();
          success();
        } else {
          $lastBlock.hide();
        }
      } //end checkSuccess

      randomBlocks();

      $id.on("click", ".paper-pintu-block", function () {
        moveBlock($(this), true);
      });

    }); //dom ready
  } //end pintu

  window.pintu = pintu;
})(jQuery);<|endoftext|>


<commit_before>(function ($) {

  $.fn.pixelPicker = function(options) {
    var settings,
        rows,
        currentColor,
        isErasing = false,
        isDragging = false,
        palette = [],
        map = [];

    // Core functions
    var updateHandler,
        applyColor,
        cycleColor;

    // Helper functions
    var parseColor,
        parseHex,
        parseRgb,
        arrayToRgb,
        arrayEqual;

    // Takes the passed in cell, finds its current background color within
    // the color palette, and updates the currentColor to the next
    // (or previous if reverse is true) color in the palette
    cycleColor = function(cell, reverse) {
      var cellColor = parseColor(cell.css('background-color').length ? cell.css('background-color') : '#ffffff');

      // If we're in eraser mode, return early
      if (isErasing) {
        return currentColor = parseColor(settings.eraserColor);
      };

      // Locate our position in the palette based on our
      // current cell's background color
      var currentIndex = (function() {
        var matchingIndex;

        palette.forEach(function(color, index) {
          if (arrayEqual(color, cellColor)) {
            matchingIndex = index;
          };
        });

        return matchingIndex;
      })();

      var nextIndex = (function() {
        if (reverse) {
          // Go back in the array, or to the end if we've reached the beginning
          return (currentIndex - 1) === -1 ? palette.length - 1 : (currentIndex - 1);
        } else {
          // Go forward in the array, or the beginning if we've reached the end
          return (currentIndex + 1) in palette ? (currentIndex + 1) : 0;
        };
      })();

      // Set the new global current color!
      return currentColor = palette[nextIndex];
    };

    // Apply the global current color as the cell's background
    applyColor = function(cell) {
      return cell.css('background-color', arrayToRgb(currentColor));
    };

    // Update whatever is handling the updated map of colors
    updateHandler = function(rowIndex, cellIndex, cell, dontHandle) {
      var handler = settings.update;
      var newColor = (cell != null && cell.css('background-color').length ? parseColor(cell.css('background-color')) : currentColor);

      map[rowIndex][cellIndex] = newColor;

      if (dontHandle) return;

      if (typeof handler === 'function') {
        // We can either pass off the updated map to a function
        handler(map);
      } else if (handler instanceof jQuery) {
        // Or, we can update the value="" of a jQuery input
        handler.val(JSON.stringify(map));
      }
    };

    // Determine if we need to parse a hex or rgb value
    parseColor = function(color) {
      // If the color is already an RGB array, return
      if (Object.prototype.toString.call(color) === '[object Array]') {
        return color;
      };

      return color.charAt(0) === '#' ? parseHex(color) : parseRgb(color);
    };

    // Parse a hex value to an RGB array (i.e. [255,255,255])
    parseHex = function(hexValue) {
      var rgb = parseInt(hexValue.substring(1), 16);
      return [(rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF];
    };

    // Parse an RGB value to an RGB array (i.e. [255,255,255])
    parseRgb = function(rgbValue) {
      return rgbValue.replace(/[^\d,]/g, '').split(',').map(function(value) {
        return parseInt(value, 10);
      });
    };

    // Convert an RGB array back to a CSS RGB color
    arrayToRgb = function(inArray) {
      return 'rgb(' + inArray[0] + ', ' + inArray[1] + ', ' + inArray[2] + ')';
    };

    // Check if two arrays are exacty the same
    arrayEqual = function(a, b) {
      return a.length === b.length && a.every(function(elem, i) {
        return elem === b[i];
      });
    };

    // Woo settings!
    settings = $.extend({
      update: null,
      ready: null,
      rowSelector: '.pixel-picker-row',
      cellSelector: '.pixel-picker-cell',
      eraserColor: '#ffffff',
      palette: [
        '#ffffff', '#000000',
        '#ff0000', '#0000ff',
        '#ffff00', '#008000'
      ]
    }, options);

    // Convert palette to array of RGB arrays
    settings.palette.forEach(function(color) {
      palette.push(parseColor(color));
    });

    $(window)
      // Prevent context menu from showing up over top of cells
      .on('contextmenu', function(event) {
        if ($(event.target).hasClass(settings.cellSelector.substring(1))) return event.preventDefault();
      })
      // When CTRL (Mac) or CMD (Windows) key is down, eraser is active
      .on('keydown', function(event) {
        if (event.metaKey || event.ctrlKey) isErasing = true;
      })
      // When CTRL (Mac) or CMD (Windows) key is released, eraser is inactive
      .on('keyup', function(event) {
        if (!event.metaKey && !event.ctrlKey) isErasing = false;
      });

    // Find all the rows
    rows = this.find(settings.rowSelector);
    rowCount = rows.length;

    // Set up our initial color
    currentColor = settings.palette[0];

    rows.each(function(rowIndex, row) {
      row = $(row);
      map.push([]);
      var cellCollection = map[rowIndex];
      var cells = row.find(settings.cellSelector);
      var cellCount = cells.length;

      cells.each(function(cellIndex, cell) {
        cell = $(cell);
        cellCollection.push([]);

        // When a cell is clicked in to...
        cell.on('mousedown', function(event) {
          // First, was it a right click?
          var isRightClick = ('which' in event && event.which === 3) || ('button' in event && event.button === 2);

          // By default, turn dragging on so the mouse can move to
          // another cell and have continuity
          isDragging = true;

          // Now we do all the work
          cycleColor(cell, isRightClick);
          applyColor(cell);
          updateHandler(rowIndex, cellIndex);
        });

        // Turn dragging off when we mouse up
        cell.on('mouseup', function() {
          isDragging = false;
        });

        // When the mouse enters a cell, if dragging is on
        // (turned on in the mousedown event), apply our current
        // global color to the cell, no cycling
        cell.on('mouseenter', function() {
          if (!isDragging) return;

          applyColor(cell);
          updateHandler(rowIndex, cellIndex);
        });

        // Update the handler when we've finished the initial cell processing
        if (rowIndex + 1 === rowCount && cellIndex + 1 === cellCount) {
          updateHandler(rowIndex, cellIndex, cell);

          // Trigger the ready handler if there is one
          if (settings.ready) {
            settings.ready();
          }
        } else {
          updateHandler(rowIndex, cellIndex, cell, true);
        };
      });
    });

    return this;

  };

}(jQuery));
<commit_msg>Add eraser color to palette<commit_after>(function ($) {

  $.fn.pixelPicker = function(options) {
    var settings,
        rows,
        currentColor,
        isErasing = false,
        isDragging = false,
        palette = [],
        map = [];

    // Core functions
    var updateHandler,
        applyColor,
        cycleColor;

    // Helper functions
    var parseColor,
        parseHex,
        parseRgb,
        arrayToRgb,
        arrayEqual;

    // Takes the passed in cell, finds its current background color within
    // the color palette, and updates the currentColor to the next
    // (or previous if reverse is true) color in the palette
    cycleColor = function(cell, reverse) {
      var cellColor = parseColor(cell.css('background-color').length ? cell.css('background-color') : '#ffffff');

      // If we're in eraser mode, return early
      if (isErasing) {
        return currentColor = parseColor(settings.eraserColor);
      };

      // Locate our position in the palette based on our
      // current cell's background color
      var currentIndex = (function() {
        var matchingIndex;

        palette.forEach(function(color, index) {
          if (arrayEqual(color, cellColor)) {
            matchingIndex = index;
          };
        });

        return matchingIndex;
      })();

      var nextIndex = (function() {
        if (reverse) {
          // Go back in the array, or to the end if we've reached the beginning
          return (currentIndex - 1) === -1 ? palette.length - 1 : (currentIndex - 1);
        } else {
          // Go forward in the array, or the beginning if we've reached the end
          return (currentIndex + 1) in palette ? (currentIndex + 1) : 0;
        };
      })();

      // Set the new global current color!
      return currentColor = palette[nextIndex];
    };

    // Apply the global current color as the cell's background
    applyColor = function(cell) {
      return cell.css('background-color', arrayToRgb(currentColor));
    };

    // Update whatever is handling the updated map of colors
    updateHandler = function(rowIndex, cellIndex, cell, dontHandle) {
      var handler = settings.update;
      var newColor = (cell != null && cell.css('background-color').length ? parseColor(cell.css('background-color')) : currentColor);

      map[rowIndex][cellIndex] = newColor;

      if (dontHandle) return;

      if (typeof handler === 'function') {
        // We can either pass off the updated map to a function
        handler(map);
      } else if (handler instanceof jQuery) {
        // Or, we can update the value="" of a jQuery input
        handler.val(JSON.stringify(map));
      }
    };

    // Determine if we need to parse a hex or rgb value
    parseColor = function(color) {
      // If the color is already an RGB array, return
      if (Object.prototype.toString.call(color) === '[object Array]') {
        return color;
      };

      return color.charAt(0) === '#' ? parseHex(color) : parseRgb(color);
    };

    // Parse a hex value to an RGB array (i.e. [255,255,255])
    parseHex = function(hexValue) {
      var rgb = parseInt(hexValue.substring(1), 16);
      return [(rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF];
    };

    // Parse an RGB value to an RGB array (i.e. [255,255,255])
    parseRgb = function(rgbValue) {
      return rgbValue.replace(/[^\d,]/g, '').split(',').map(function(value) {
        return parseInt(value, 10);
      });
    };

    // Convert an RGB array back to a CSS RGB color
    arrayToRgb = function(inArray) {
      return 'rgb(' + inArray[0] + ', ' + inArray[1] + ', ' + inArray[2] + ')';
    };

    // Check if two arrays are exacty the same
    arrayEqual = function(a, b) {
      return a.length === b.length && a.every(function(elem, i) {
        return elem === b[i];
      });
    };

    // Woo settings!
    settings = $.extend({
      update: null,
      ready: null,
      rowSelector: '.pixel-picker-row',
      cellSelector: '.pixel-picker-cell',
      eraserColor: '#ffffff',
      palette: [
        '#ffffff', '#000000',
        '#ff0000', '#0000ff',
        '#ffff00', '#008000'
      ]
    }, options);

    // Convert palette to array of RGB arrays
    settings.palette.forEach(function(color) {
      palette.push(parseColor(color));
    });

    // Add the eraser color as the first color in
    // the palette. Required to make color cycling work.
    palette.unshift(parseColor(settings.eraserColor))

    $(window)
      // Prevent context menu from showing up over top of cells
      .on('contextmenu', function(event) {
        if ($(event.target).hasClass(settings.cellSelector.substring(1))) return event.preventDefault();
      })
      // When CTRL (Mac) or CMD (Windows) key is down, eraser is active
      .on('keydown', function(event) {
        if (event.metaKey || event.ctrlKey) isErasing = true;
      })
      // When CTRL (Mac) or CMD (Windows) key is released, eraser is inactive
      .on('keyup', function(event) {
        if (!event.metaKey && !event.ctrlKey) isErasing = false;
      });

    // Find all the rows
    rows = this.find(settings.rowSelector);
    rowCount = rows.length;

    // Set up our initial color
    currentColor = settings.palette[0];

    rows.each(function(rowIndex, row) {
      row = $(row);
      map.push([]);
      var cellCollection = map[rowIndex];
      var cells = row.find(settings.cellSelector);
      var cellCount = cells.length;

      cells.each(function(cellIndex, cell) {
        cell = $(cell);
        cellCollection.push([]);

        // When a cell is clicked in to...
        cell.on('mousedown', function(event) {
          // First, was it a right click?
          var isRightClick = ('which' in event && event.which === 3) || ('button' in event && event.button === 2);

          // By default, turn dragging on so the mouse can move to
          // another cell and have continuity
          isDragging = true;

          // Now we do all the work
          cycleColor(cell, isRightClick);
          applyColor(cell);
          updateHandler(rowIndex, cellIndex);
        });

        // Turn dragging off when we mouse up
        cell.on('mouseup', function() {
          isDragging = false;
        });

        // When the mouse enters a cell, if dragging is on
        // (turned on in the mousedown event), apply our current
        // global color to the cell, no cycling
        cell.on('mouseenter', function() {
          if (!isDragging) return;

          applyColor(cell);
          updateHandler(rowIndex, cellIndex);
        });

        // Update the handler when we've finished the initial cell processing
        if (rowIndex + 1 === rowCount && cellIndex + 1 === cellCount) {
          updateHandler(rowIndex, cellIndex, cell);

          // Trigger the ready handler if there is one
          if (settings.ready) {
            settings.ready();
          }
        } else {
          updateHandler(rowIndex, cellIndex, cell, true);
        };
      });
    });

    return this;

  };

}(jQuery));
<|endoftext|>


<commit_before>var http = require('http');
var port = parseInt(process.argv[2] || '3000');
var defaults = { width: 500, height: 100, colour: '663399' };

// Choose black or white text to contrast with the background colour
function defaultTextColour(bgColour) {
  return (
          (
           parseInt(bgColour.substr(0,2), 16) +
           parseInt(bgColour.substr(2,2), 16) +
           parseInt(bgColour.substr(4,2), 16)
          ) / (3 * 0xFF)
         ) >= 0.5 ? '000000' : 'FFFFFF';
}

http.createServer(function (req, res) {
  var params = req.url.split('/');
  console.log(req.connection.remoteAddress, params);

  var size = params[1].split('x');
  var width = size[0] || defaults.width;
  var height = size[1] || defaults.height;
  var colour = params[2] || defaults.colour;
  var textColour = params[3] || defaultTextColour(colour);
  var textSize = Math.ceil(Math.min((width / params[1].length), (height / 2)));

  var svg = '<svg xmlns="http://www.w3.org/2000/svg" width="'+width+'" height="'+height+'"><g>'+
              '<rect width="100%" height="100%" style="fill:#'+colour+'"/>'+
              '<text y="50%" x="50%" text-anchor="middle" dominant-baseline="central" style="font-family: sans-serif; font-size: '+textSize+'px; fill:#'+textColour+'">'+width+'x'+height+'</text>' +
            '</g></svg>';

  res.writeHead(200, {'Content-Type': 'image/svg+xml', 'Content-Length': svg.length + 1});
  res.end(svg + '\n');
}).listen(port);

console.log("Serving on port:", port);

<commit_msg>Use a pseudo random colour if none specified<commit_after>var http = require('http');
var port = parseInt(process.argv[2] || '3000');
var defaults = { width: 500, height: 100 };

// Generate a randomish colour based on the given width and height
function defaultBackgroundColour(width, height) {
  var col = (width * height * 0x13579 % 0xFFFFFF).toString(16);
  return '000000'.substr(0, (6 - col.length)) + col;
};

// Choose black or white text to contrast with the background colour
function defaultTextColour(bgColour) {
  return (
          (
           parseInt(bgColour.substr(0,2), 16) +
           parseInt(bgColour.substr(2,2), 16) +
           parseInt(bgColour.substr(4,2), 16)
          ) / (3 * 0xFF)
         ) >= 0.5 ? '000000' : 'FFFFFF';
}

http.createServer(function (req, res) {
  var params = req.url.split('/');
  console.log(req.connection.remoteAddress, params);

  var size = params[1].split('x');
  var width = size[0] || defaults.width;
  var height = size[1] || defaults.height;
  var colour = params[2] || defaultBackgroundColour(width, height);
  var textColour = params[3] || defaultTextColour(colour);
  var textSize = Math.ceil(Math.min((width / params[1].length), (height / 2)));

  var svg = '<svg xmlns="http://www.w3.org/2000/svg" width="'+width+'" height="'+height+'"><g>'+
              '<rect width="100%" height="100%" style="fill:#'+colour+'"/>'+
              '<text y="50%" x="50%" text-anchor="middle" dominant-baseline="central" style="font-family: sans-serif; font-size: '+textSize+'px; fill:#'+textColour+'">'+width+'x'+height+'</text>' +
            '</g></svg>';

  res.writeHead(200, {'Content-Type': 'image/svg+xml', 'Content-Length': svg.length + 1});
  res.end(svg + '\n');
}).listen(port);

console.log("Serving on port:", port);

<|endoftext|>


<commit_before>// See the file COPYRIGHT for copyright information.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//
// HTML encoding
//

// **** BEGIN WEAKNESS ***

// It seems ridiculous that this isn't standard in JavaScript
// It is certainly ridiculous to involve the DOM, but on the other hand, the
// browser will implement this correctly, and any solution using .replace()
// will be buggy.  And this will be fast.  But still, this is weak.

var _domTextAreaForHaxxors = document.createElement("textarea")

function textAsHTML(text) {
  _domTextAreaForHaxxors.textContent = text;
  return _domTextAreaForHaxxors.innerHTML;
}

function htmlAsText(html) {
  _domTextAreaForHaxxors.innerHTML = html;
  return _domTextAreaForHaxxors.textContent;
}

// **** END WEAKNESS ***

//
// Generic string formatting
//

function padTwo(segment) {
  if (segment == undefined) {
    return "?";
  }

  segment = segment.toString();

  if (segment.length == 1) {
    return "0" + segment;
  }

  return "" + segment;
}

function shortFormatDate(date) {
  return moment(date).format("M/D@h:mm z");
}

//
// Incident data
//

function priorityNameFromNumber(priorityNumber) {
  // priorityNumber should be an int, 1-5.

  switch (priorityNumber) {
    case 1: return "High";
    case 2: return "High";
    case 3: return "Medium";
    case 4: return "Low";
    case 5: return "Low";
    default:
      console.log("Unknown incident priority number: " + priorityNumber);
      return undefined;
  }
}

function priorityIconFromNumber(priorityNumber) {
  // priorityNumber should be an int, 1-5.

  switch (priorityNumber) {
    case 1: return '';
    case 2: return '';
    case 3: return '';
    case 4: return '';
    case 5: return '';
    default:
      console.log("Unknown incident priority number: " + priorityNumber);
      return undefined;
  }
}

function stateNameFromID(stateID) {
  // stateID should be a string key.

  switch (stateID) {
    case "new"       : return "New";
    case "on_hold"   : return "On Hold";
    case "dispatched": return "Dispatched";
    case "on_scene"  : return "On Scene";
    case "closed"    : return "Closed";
    default:
      console.log("Unknown incident state ID: " + stateID);
      return undefined;
  }
}

function stateSortKeyFromID(stateID) {
  // stateID should be a string key.

  switch (stateID) {
    case "new"       : return 1;
    case "on_hold"   : return 2;
    case "dispatched": return 3;
    case "on_scene"  : return 4;
    case "closed"    : return 5;
    default:
      console.log("Unknown incident state ID: " + stateID);
      return undefined;
  }
}

function concentricStreetFromID(streetID) {
  // streetID should be an int

  if (streetID == undefined) {
    return undefined;
  }

  var name = concentricStreetNameByNumber[streetID];
  if (name == undefined) {
    console.log("Unknown street ID: " + streetID);
    name = undefined;
  }
  return name;
}

function stateForIncident(incident) {
  // Data from 2014+ should have incident.state set.
  if (incident.state != undefined) {
    return incident.state;
  }

  // 2013 data had multiple overloaded timestamps instead.
  if (incident.closed != undefined) {
    return "closed";
  }
  if (incident.on_scene != undefined) {
    return "on_scene";
  }
  if (incident.dispatched != undefined) {
    return "dispatched";
  }
  if (incident.created != undefined) {
    return "new";
  }

  console.log("Unknown state for incident: " + incident);
  return undefined;
}

function summarizeIncident(incident) {
  var summary = incident.summary;
  var reportEntries = incident.report_entries;

  if (summary == undefined) {
    if (reportEntries == undefined) {
      console.log("No summary provided.");
      return "";
    }
    else {
      // Get the first line of the first report entry.
      for (var i in reportEntries) {
        var lines = reportEntries[i].text.split("\n");

        for (var j in lines) {
          var line = lines[j];
          if (line != undefined && line != "") {
            return summary;
          }
        }
      }
    }

    console.log("No summary provided and no report entry text.");
    return "";
  }

  return summary;
}

function reportTextFromIncident(incident) {
  var texts = [];

  if (incident.summary != undefined) {
    texts.push(incident.summary);
  }

  var reportEntries = incident.report_entries;

  for (var i in reportEntries) {
    var reportEntry = reportEntries[i];

    // Skip system entries
    if (reportEntry.system_entry) {
      continue;
    }

    var text = reportEntry.text;

    if (text != undefined) {
      texts.push(text);
    }
  }

  var text = texts.join("");

  return text;
}

function shortDescribeLocation(location) {
  if (location == undefined) {
    return undefined;
  }

  var locationBits = [];

  if (location.name != undefined) {
    locationBits.push(location.name);
  }

  switch (location.type) {
    case undefined:
      // Fall through to "text" case
    case "text":
      break;
    case "garett":
      locationBits.push(" (");
      locationBits.push(padTwo(location.radial_hour));
      locationBits.push(":");
      locationBits.push(padTwo(location.radial_minute));
      locationBits.push("@");
      locationBits.push(concentricStreetFromID(location.concentric));
      locationBits.push(")");
      break;
    default:
      locationBits.push(
        "Unknown location type:" + location.type
      );
      break;
  }

  return locationBits.join("");
}

//
// DataTables rendering
//

function renderPriority(priorityNumber, type, incident) {
  switch (type) {
    case "display":
      return priorityIconFromNumber(priorityNumber);
    case "filter":
      return priorityNameFromNumber(priorityNumber);
    case "type":
    case "sort":
      return priorityNumber;
  }
  return undefined;
}

function renderDate(date, type, incident) {
  switch (type) {
    case "display":
      return textAsHTML(shortFormatDate(date));
    case "filter":
      return shortFormatDate(date);
    case "type":
    case "sort":
      return moment(date);
  }
  return undefined;
}

function renderState(state, type, incident) {
  if (state == undefined) {
    state = stateForIncident(incident);
  }

  switch (type) {
    case "display":
      return textAsHTML(stateNameFromID(state));
    case "filter":
      return stateNameFromID(state);
    case "type":
      return state;
    case "sort":
      return stateSortKeyFromID(state);
  }
  return undefined;
}

function renderLocation(data, type, incident) {
  if (data == undefined) {
    data = "";
  }
  switch (type) {
    case "display":
      return textAsHTML(shortDescribeLocation(data));
    case "filter":
    case "sort":
      return shortDescribeLocation(data);
    case "type":
      return "";
  }
  return undefined;
}

function renderSummary(data, type, incident) {
  switch (type) {
    case "display":
      return textAsHTML(summarizeIncident(incident));
    case "sort":
      return summarizeIncident(incident);
    case "filter":
      return reportTextFromIncident(incident);
    case "type":
      return "";
  }
  return undefined;
}
<commit_msg>console.log -> console.warn<commit_after>// See the file COPYRIGHT for copyright information.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//
// HTML encoding
//

// **** BEGIN WEAKNESS ***

// It seems ridiculous that this isn't standard in JavaScript
// It is certainly ridiculous to involve the DOM, but on the other hand, the
// browser will implement this correctly, and any solution using .replace()
// will be buggy.  And this will be fast.  But still, this is weak.

var _domTextAreaForHaxxors = document.createElement("textarea")

function textAsHTML(text) {
  _domTextAreaForHaxxors.textContent = text;
  return _domTextAreaForHaxxors.innerHTML;
}

function htmlAsText(html) {
  _domTextAreaForHaxxors.innerHTML = html;
  return _domTextAreaForHaxxors.textContent;
}

// **** END WEAKNESS ***

//
// Generic string formatting
//

function padTwo(segment) {
  if (segment == undefined) {
    return "?";
  }

  segment = segment.toString();

  if (segment.length == 1) {
    return "0" + segment;
  }

  return "" + segment;
}

function shortFormatDate(date) {
  return moment(date).format("M/D@h:mm z");
}

//
// Incident data
//

function priorityNameFromNumber(priorityNumber) {
  // priorityNumber should be an int, 1-5.

  switch (priorityNumber) {
    case 1: return "High";
    case 2: return "High";
    case 3: return "Medium";
    case 4: return "Low";
    case 5: return "Low";
    default:
      console.warn("Unknown incident priority number: " + priorityNumber);
      return undefined;
  }
}

function priorityIconFromNumber(priorityNumber) {
  // priorityNumber should be an int, 1-5.

  switch (priorityNumber) {
    case 1: return '';
    case 2: return '';
    case 3: return '';
    case 4: return '';
    case 5: return '';
    default:
      console.warn("Unknown incident priority number: " + priorityNumber);
      return undefined;
  }
}

function stateNameFromID(stateID) {
  // stateID should be a string key.

  switch (stateID) {
    case "new"       : return "New";
    case "on_hold"   : return "On Hold";
    case "dispatched": return "Dispatched";
    case "on_scene"  : return "On Scene";
    case "closed"    : return "Closed";
    default:
      console.warn("Unknown incident state ID: " + stateID);
      return undefined;
  }
}

function stateSortKeyFromID(stateID) {
  // stateID should be a string key.

  switch (stateID) {
    case "new"       : return 1;
    case "on_hold"   : return 2;
    case "dispatched": return 3;
    case "on_scene"  : return 4;
    case "closed"    : return 5;
    default:
      console.warn("Unknown incident state ID: " + stateID);
      return undefined;
  }
}

function concentricStreetFromID(streetID) {
  // streetID should be an int

  if (streetID == undefined) {
    return undefined;
  }

  var name = concentricStreetNameByNumber[streetID];
  if (name == undefined) {
    console.warn("Unknown street ID: " + streetID);
    name = undefined;
  }
  return name;
}

function stateForIncident(incident) {
  // Data from 2014+ should have incident.state set.
  if (incident.state != undefined) {
    return incident.state;
  }

  // 2013 data had multiple overloaded timestamps instead.
  if (incident.closed != undefined) {
    return "closed";
  }
  if (incident.on_scene != undefined) {
    return "on_scene";
  }
  if (incident.dispatched != undefined) {
    return "dispatched";
  }
  if (incident.created != undefined) {
    return "new";
  }

  console.warn("Unknown state for incident: " + incident);
  return undefined;
}

function summarizeIncident(incident) {
  var summary = incident.summary;
  var reportEntries = incident.report_entries;

  if (summary == undefined) {
    if (reportEntries == undefined) {
      console.warn("No summary provided.");
      return "";
    }
    else {
      // Get the first line of the first report entry.
      for (var i in reportEntries) {
        var lines = reportEntries[i].text.split("\n");

        for (var j in lines) {
          var line = lines[j];
          if (line != undefined && line != "") {
            return summary;
          }
        }
      }
    }

    console.warn("No summary provided and no report entry text.");
    return "";
  }

  return summary;
}

function reportTextFromIncident(incident) {
  var texts = [];

  if (incident.summary != undefined) {
    texts.push(incident.summary);
  }

  var reportEntries = incident.report_entries;

  for (var i in reportEntries) {
    var reportEntry = reportEntries[i];

    // Skip system entries
    if (reportEntry.system_entry) {
      continue;
    }

    var text = reportEntry.text;

    if (text != undefined) {
      texts.push(text);
    }
  }

  var text = texts.join("");

  return text;
}

function shortDescribeLocation(location) {
  if (location == undefined) {
    return undefined;
  }

  var locationBits = [];

  if (location.name != undefined) {
    locationBits.push(location.name);
  }

  switch (location.type) {
    case undefined:
      // Fall through to "text" case
    case "text":
      break;
    case "garett":
      locationBits.push(" (");
      locationBits.push(padTwo(location.radial_hour));
      locationBits.push(":");
      locationBits.push(padTwo(location.radial_minute));
      locationBits.push("@");
      locationBits.push(concentricStreetFromID(location.concentric));
      locationBits.push(")");
      break;
    default:
      locationBits.push(
        "Unknown location type:" + location.type
      );
      break;
  }

  return locationBits.join("");
}

//
// DataTables rendering
//

function renderPriority(priorityNumber, type, incident) {
  switch (type) {
    case "display":
      return priorityIconFromNumber(priorityNumber);
    case "filter":
      return priorityNameFromNumber(priorityNumber);
    case "type":
    case "sort":
      return priorityNumber;
  }
  return undefined;
}

function renderDate(date, type, incident) {
  switch (type) {
    case "display":
      return textAsHTML(shortFormatDate(date));
    case "filter":
      return shortFormatDate(date);
    case "type":
    case "sort":
      return moment(date);
  }
  return undefined;
}

function renderState(state, type, incident) {
  if (state == undefined) {
    state = stateForIncident(incident);
  }

  switch (type) {
    case "display":
      return textAsHTML(stateNameFromID(state));
    case "filter":
      return stateNameFromID(state);
    case "type":
      return state;
    case "sort":
      return stateSortKeyFromID(state);
  }
  return undefined;
}

function renderLocation(data, type, incident) {
  if (data == undefined) {
    data = "";
  }
  switch (type) {
    case "display":
      return textAsHTML(shortDescribeLocation(data));
    case "filter":
    case "sort":
      return shortDescribeLocation(data);
    case "type":
      return "";
  }
  return undefined;
}

function renderSummary(data, type, incident) {
  switch (type) {
    case "display":
      return textAsHTML(summarizeIncident(incident));
    case "sort":
      return summarizeIncident(incident);
    case "filter":
      return reportTextFromIncident(incident);
    case "type":
      return "";
  }
  return undefined;
}
<|endoftext|>


<commit_before>/**
 * @type jquery.layout Global variable so layout state management
 * can pick it up.
 */
var ss_MainLayout;

(function($) {

	/**
	 * @class Layout handling for the main CMS interface,
	 * with tree on left and main edit form on the right.
	 * @name ss.CMSMain
	 */
	$('body.CMSMain').concrete('ss', function($){
		return/** @lends ss.CMSMain */{

		/**
		 * Reference to jQuery.layout element
		 * @type Object
		 */
		MainLayout: null,

		onmatch: function() {
			var self = this;

			// Layout
			ss_MainLayout = this._setupLayout();
			this.setMainLayout(ss_MainLayout);
			layoutState.options.keys = "west.size,west.isClosed";
			$(window).unload(function(){ layoutState.save('ss_MainLayout') });

			// artificially delay the resize event 200ms
			// to avoid overlapping height changes in different onresize() methods
			$(window).resize(function () {
				var timerID = "timerCMSMainResize";
				if (window[timerID]) clearTimeout(window[timerID]);
				window[timerID] = setTimeout(function() {self._resizeChildren();}, 200);
			});

			// If tab has no nested tabs, set overflow to auto
			$(this).find('.tab').not(':has(.tab)').css('overflow', 'auto');

			this._resizeChildren();

			this._super();
		},

		_resizeChildren: function() {
			$("#treepanes", this).accordion("resize");
			$('#sitetree_and_tools', this).fitHeightToParent();
			$('#contentPanel form', this).fitHeightToParent();
			$('#contentPanel form fieldset', this).fitHeightToParent();
			$('#contentPanel form fieldset .content', this).fitHeightToParent();

			this._super();
		},

		/**
		 * Initialize jQuery layout manager with the following panes:
		 * - east: Tree, Page Version History, Site Reports
		 * - center: Form
		 * - west: "Insert Image", "Insert Link", "Insert Flash" panes
		 * - north: CMS area menu bar
		 * - south: "Page view", "profile" and "logout" links
		 */
		_setupLayout: function() {
			var self = this;

			// layout containing the tree, CMS menu, the main form etc.
			var savedLayoutSettings = layoutState.load('ss_MainLayout');
			var layoutSettings = jQuery.extend({
				defaults: {
					// TODO Reactivate once we have localized values
					togglerTip_open: '',
					togglerTip_closed: '',
					resizerTip: '',
					sliderTip: '',
					onresize: function() {self._resizeChildren();},
					onopen: function() {self._resizeChildren();}
				},
				north: {
					slidable: false,
					resizable: false,
					size: 35,
					togglerLength_open: 0
				},
				south: {
					slidable: false,
					resizable: false,
					size: 23,
					togglerLength_open: 0
				},
				west: {
					size: 250,
					fxName: "none"
				},
				east: {
					initClosed: true,
					// multiple panels which are triggered through tinymce buttons,
					// so a user shouldn't be able to toggle this panel manually
					initHidden: true,
					fxName: "none",
					size: 250
				},
				center: {}
			}, savedLayoutSettings);
			var layout = $('body').layout(layoutSettings);

			// Adjust tree accordion etc. in left panel to work correctly
			// with jQuery.layout (see http://layout.jquery-dev.net/tips.html#Widget_Accordion)
			this.find("#treepanes").accordion({
				fillSpace: true,
				animated: false
			});

			return layout;
		}
	}});

	/**
	 * @class CMS-specific form behaviour
	 * @name ss.EditForm
	 */
	$('#Form_EditForm').concrete('ss', function($){
		return/** @lends ss.EditForm */{
		onmatch: function() {
			// Alert the user on change of page-type - this might have implications
			// on the available form fields etc.
			this.find(':input[name=ClassName]').bind('change',
				function() {
					alert('The page type will be updated after the page is saved');
				}
			);

			this.find(':input[name=ParentID]')
		}
	}});

	/**
	 * @class ParentID field combination - mostly toggling between
	 * the two radiobuttons and setting the hidden "ParentID" field
	 * @name ss.EditForm.ParentType
	 */
	$('#Form_EditForm_ParentType').concrete('ss', function($){
		return/** @lends ss.EditForm.ParentType */{
		onmatch : function() {
			var parentTypeRootEl = $('#Form_EditForm_ParentType_root');
			var parentTypeSubpageEl = $('#Form_EditForm_ParentType_subpage');
			if(parentTypeRootEl) {
				parentTypeRootEl.onclick = this._rootClick.bind(this);
			}
			if(parentTypeSubpageEl) {
				parentTypeSubpageEl.onclick = this.showHide;
			}
			this.showHide();
		},

		_rootClick : function() {
			$('#Form_EditForm_ParentID').val(0);
			this.showHide();
		},

		showHide : function() {
			var parentTypeRootEl = $('#Form_EditForm_ParentType_root');
			if(parentTypeRootEl && parentTypeRootEl.checked) {
				$('#ParentID').hide();
			} else {
				$('#ParentID').show();
			}
		}
	}});

	/**
	 * @class Toggle display of group dropdown in "access" tab,
	 * based on selection of radiobuttons.
	 * @name ss.Form_EditForm.Access
	 */
	$('#Form_EditForm #CanViewType, #Form_EditForm #CanEditType').concrete('ss', function($){
		return/** @lends ss.Form_EditForm.Access */{
			onmatch: function() {
				// TODO Decouple
				var dropdown;
				if(this.attr('id') == 'CanViewType') dropdown = $('#ViewerGroups');
				else if(this.attr('id') == 'CanEditType') dropdown = $('#EditorGroups');

				this.find('.optionset :input').bind('change', function(e) {
					dropdown.toggle(e.target.value == 'OnlyTheseUsers');
				});

				// initial state
				var currentVal = this.find('input[name=' + this.attr('id') + ']:checked').val();
				dropdown.toggle(currentVal == 'OnlyTheseUsers');
			}
		}
	});

	/**
	 * @class Email containing the link to the archived version of the page.
	 * Visible on readonly older versions of a specific page at the moment.
	 * @name ss.Form_EditForm_action_email
	 */
	$('#Form_EditForm .Actions #Form_EditForm_action_email').concrete('ss', function($){
		return/** @lends ss.Form_EditForm_action_email */{
		onclick: function(e) {
			window.open(
				'mailto:?subject='
					+ $('input[name=ArchiveEmailSubject]', this[0].form).val()
					+ '&body='
					+ $(':input[name=ArchiveEmailMessage]', this[0].form).val(),
				'archiveemail'
			);

			return false;
		}
	}});

	/**
	 * @class Open a printable representation of the form in a new window.
	 * Used for readonly older versions of a specific page.
	 * @name ss.Form_EditForm_action_print
	 */
	$('#Form_EditForm .Actions #Form_EditForm_action_print').concrete('ss', function($){
		return/** @lends ss.Form_EditForm_action_print */{
		onclick: function(e) {
			var printURL = $(this[0].form).attr('action').replace(/\?.*$/,'')
				+ '/printable/'
				+ $(':input[name=ID]',this[0].form).val();
			if(printURL.substr(0,7) != 'http://') printURL = $('base').attr('href') + printURL;

			window.open(printURL, 'printable');

			return false;
		}
	}});

	/**
	 * @class A "rollback" to a specific version needs user confirmation.
	 * @name ss.Form_EditForm_action_rollback
	 */
	$('#Form_EditForm .Actions #Form_EditForm_action_rollback').concrete('ss', function($){
		return/** @lends ss.Form_EditForm_action_rollback */{
		onclick: function(e) {
			// @todo i18n
			return confirm("Do you really want to copy the published content to the stage site?");
		}
	}});

	/**
	 * @class All forms in the right content panel should have closeable jQuery UI style titles.
	 * @name ss.contentPanel.form
	 */
	$('#contentPanel form').concrete('ss', function($){
		return/** @lends ss.contentPanel.form */{
		onmatch: function() {
		  // Style as title bar
			this.find(':header:first').titlebar({
				closeButton:true
			});
			// The close button should close the east panel of the layout
			this.find(':header:first .ui-dialog-titlebar-close').bind('click', function(e) {
				$('body.CMSMain').concrete('ss').MainLayout().close('east');

				return false;
			});
		}
	}});

	/**
	 * @class Control the site tree filter.
	 * Toggles search form fields based on a dropdown selection,
	 * similar to "Smart Search" criteria in iTunes.
	 * @name ss.Form_SeachTreeForm
	 */
	$('#Form_SearchTreeForm').concrete('ss', function($) {
		return/** @lends ss.Form_SeachTreeForm */{

		/**
		 * @type DOMElement
		 */
		SelectEl: null,

		onmatch: function() {
			var self = this;

			// TODO Cant bind to onsubmit/onreset directly because of IE6
			this.bind('submit', function(e) {return self._submitForm(e);});
			this.bind('reset', function(e) {return self._resetForm(e);});

			// only the first field should be visible by default
			this.find('.field').not(':first').hide();

			// generate the field dropdown
			this.setSelectEl($('<select name="options" class="options"></select>')
				.appendTo(this.find('fieldset:first'))
				.bind('change', function(e) {self._addField(e);})
			);

			this._setOptions();

		},

		_setOptions: function() {
			var self = this;

			// reset existing elements
			self.SelectEl().find('option').remove();

			// add default option
			// TODO i18n
			jQuery('<option value="0">Add Criteria</option>').appendTo(self.SelectEl())

			// populate dropdown values from existing fields
			this.find('.field').each(function() {
				$('<option />').appendTo(self.SelectEl())
					.val(this.id)
					.text($(this).find('label').text());
			});
		},

		/**
		 * Filter tree based on selected criteria.
		 */
		_submitForm: function(e) {
			var self = this;
			var data = [];

			// convert from jQuery object literals to hash map
			$(this.serializeArray()).each(function(i, el) {
				data[el.name] = el.value;
			});

			// Set new URL
			$('#sitetree')[0].setCustomURL(this.attr('action') + '&action_getfilteredsubtree=1', data);

			// Disable checkbox tree controls that currently don't work with search.
			// @todo: Make them work together
			if ($('#sitetree')[0].isDraggable) $('#sitetree')[0].stopBeingDraggable();
			this.find('.checkboxAboveTree :checkbox').val(false).attr('disabled', true);

			// disable buttons to avoid multiple submission
			//this.find(':submit').attr('disabled', true);

			this.find(':submit[name=action_getfilteredsubtree]').addClass('loading');

			this._reloadSitetree();

			return false;
		},

		_resetForm: function(e) {
			this.find('.field :input').clearFields().not(':first').hide();

			// Reset URL to default
			$('#sitetree')[0].clearCustomURL();

			// Enable checkbox tree controls
			this.find('.checkboxAboveTree :checkbox').attr('disabled', 'false');

			// reset all options, some of the might be removed
			this._setOptions();

			this._reloadSitetree();

			return false;
		},

		_addField: function(e) {
			var $select = $(e.target);
			// show formfield matching the option
			this.find('#' + $select.val()).show();

			// remove option from dropdown, each field should just exist once
			this.find('option[value=' + $select.val() + ']').remove();

			// jump back to default entry
			$select.val(0);

			return false;
		},

		_reloadSitetree: function() {
			var self = this;

			$('#sitetree')[0].reload({
				onSuccess :  function(response) {
					self.find(':submit').attr('disabled', false).removeClass('loading');
					self.find('.checkboxAboveTree :checkbox').attr('disabled', 'true');
					statusMessage('Filtered tree','good');
				},
				onFailure : function(response) {
					self.find(':submit').attr('disabled', false).removeClass('loading');
					self.find('.checkboxAboveTree :checkbox').attr('disabled', 'true');
					errorMessage('Could not filter site tree<br />' + response.responseText);
				}
			});
		}
	}});

})(jQuery);<commit_msg>MINOR Changed left panel size in CMSMain to resemble old interface<commit_after>/**
 * @type jquery.layout Global variable so layout state management
 * can pick it up.
 */
var ss_MainLayout;

(function($) {

	/**
	 * @class Layout handling for the main CMS interface,
	 * with tree on left and main edit form on the right.
	 * @name ss.CMSMain
	 */
	$('body.CMSMain').concrete('ss', function($){
		return/** @lends ss.CMSMain */{

		/**
		 * Reference to jQuery.layout element
		 * @type Object
		 */
		MainLayout: null,

		onmatch: function() {
			var self = this;

			// Layout
			ss_MainLayout = this._setupLayout();
			this.setMainLayout(ss_MainLayout);
			layoutState.options.keys = "west.size,west.isClosed";
			$(window).unload(function(){ layoutState.save('ss_MainLayout') });

			// artificially delay the resize event 200ms
			// to avoid overlapping height changes in different onresize() methods
			$(window).resize(function () {
				var timerID = "timerCMSMainResize";
				if (window[timerID]) clearTimeout(window[timerID]);
				window[timerID] = setTimeout(function() {self._resizeChildren();}, 200);
			});

			// If tab has no nested tabs, set overflow to auto
			$(this).find('.tab').not(':has(.tab)').css('overflow', 'auto');

			this._resizeChildren();

			this._super();
		},

		_resizeChildren: function() {
			$("#treepanes", this).accordion("resize");
			$('#sitetree_and_tools', this).fitHeightToParent();
			$('#contentPanel form', this).fitHeightToParent();
			$('#contentPanel form fieldset', this).fitHeightToParent();
			$('#contentPanel form fieldset .content', this).fitHeightToParent();

			this._super();
		},

		/**
		 * Initialize jQuery layout manager with the following panes:
		 * - east: Tree, Page Version History, Site Reports
		 * - center: Form
		 * - west: "Insert Image", "Insert Link", "Insert Flash" panes
		 * - north: CMS area menu bar
		 * - south: "Page view", "profile" and "logout" links
		 */
		_setupLayout: function() {
			var self = this;

			// layout containing the tree, CMS menu, the main form etc.
			var savedLayoutSettings = layoutState.load('ss_MainLayout');
			var layoutSettings = jQuery.extend({
				defaults: {
					// TODO Reactivate once we have localized values
					togglerTip_open: '',
					togglerTip_closed: '',
					resizerTip: '',
					sliderTip: '',
					onresize: function() {self._resizeChildren();},
					onopen: function() {self._resizeChildren();}
				},
				north: {
					slidable: false,
					resizable: false,
					size: 35,
					togglerLength_open: 0
				},
				south: {
					slidable: false,
					resizable: false,
					size: 23,
					togglerLength_open: 0
				},
				west: {
					size: 225,
					fxName: "none"
				},
				east: {
					initClosed: true,
					// multiple panels which are triggered through tinymce buttons,
					// so a user shouldn't be able to toggle this panel manually
					initHidden: true,
					fxName: "none",
					size: 250
				},
				center: {}
			}, savedLayoutSettings);
			var layout = $('body').layout(layoutSettings);

			// Adjust tree accordion etc. in left panel to work correctly
			// with jQuery.layout (see http://layout.jquery-dev.net/tips.html#Widget_Accordion)
			this.find("#treepanes").accordion({
				fillSpace: true,
				animated: false
			});

			return layout;
		}
	}});

	/**
	 * @class CMS-specific form behaviour
	 * @name ss.EditForm
	 */
	$('#Form_EditForm').concrete('ss', function($){
		return/** @lends ss.EditForm */{
		onmatch: function() {
			// Alert the user on change of page-type - this might have implications
			// on the available form fields etc.
			this.find(':input[name=ClassName]').bind('change',
				function() {
					alert('The page type will be updated after the page is saved');
				}
			);

			this.find(':input[name=ParentID]')
		}
	}});

	/**
	 * @class ParentID field combination - mostly toggling between
	 * the two radiobuttons and setting the hidden "ParentID" field
	 * @name ss.EditForm.ParentType
	 */
	$('#Form_EditForm_ParentType').concrete('ss', function($){
		return/** @lends ss.EditForm.ParentType */{
		onmatch : function() {
			var parentTypeRootEl = $('#Form_EditForm_ParentType_root');
			var parentTypeSubpageEl = $('#Form_EditForm_ParentType_subpage');
			if(parentTypeRootEl) {
				parentTypeRootEl.onclick = this._rootClick.bind(this);
			}
			if(parentTypeSubpageEl) {
				parentTypeSubpageEl.onclick = this.showHide;
			}
			this.showHide();
		},

		_rootClick : function() {
			$('#Form_EditForm_ParentID').val(0);
			this.showHide();
		},

		showHide : function() {
			var parentTypeRootEl = $('#Form_EditForm_ParentType_root');
			if(parentTypeRootEl && parentTypeRootEl.checked) {
				$('#ParentID').hide();
			} else {
				$('#ParentID').show();
			}
		}
	}});

	/**
	 * @class Toggle display of group dropdown in "access" tab,
	 * based on selection of radiobuttons.
	 * @name ss.Form_EditForm.Access
	 */
	$('#Form_EditForm #CanViewType, #Form_EditForm #CanEditType').concrete('ss', function($){
		return/** @lends ss.Form_EditForm.Access */{
			onmatch: function() {
				// TODO Decouple
				var dropdown;
				if(this.attr('id') == 'CanViewType') dropdown = $('#ViewerGroups');
				else if(this.attr('id') == 'CanEditType') dropdown = $('#EditorGroups');

				this.find('.optionset :input').bind('change', function(e) {
					dropdown.toggle(e.target.value == 'OnlyTheseUsers');
				});

				// initial state
				var currentVal = this.find('input[name=' + this.attr('id') + ']:checked').val();
				dropdown.toggle(currentVal == 'OnlyTheseUsers');
			}
		}
	});

	/**
	 * @class Email containing the link to the archived version of the page.
	 * Visible on readonly older versions of a specific page at the moment.
	 * @name ss.Form_EditForm_action_email
	 */
	$('#Form_EditForm .Actions #Form_EditForm_action_email').concrete('ss', function($){
		return/** @lends ss.Form_EditForm_action_email */{
		onclick: function(e) {
			window.open(
				'mailto:?subject='
					+ $('input[name=ArchiveEmailSubject]', this[0].form).val()
					+ '&body='
					+ $(':input[name=ArchiveEmailMessage]', this[0].form).val(),
				'archiveemail'
			);

			return false;
		}
	}});

	/**
	 * @class Open a printable representation of the form in a new window.
	 * Used for readonly older versions of a specific page.
	 * @name ss.Form_EditForm_action_print
	 */
	$('#Form_EditForm .Actions #Form_EditForm_action_print').concrete('ss', function($){
		return/** @lends ss.Form_EditForm_action_print */{
		onclick: function(e) {
			var printURL = $(this[0].form).attr('action').replace(/\?.*$/,'')
				+ '/printable/'
				+ $(':input[name=ID]',this[0].form).val();
			if(printURL.substr(0,7) != 'http://') printURL = $('base').attr('href') + printURL;

			window.open(printURL, 'printable');

			return false;
		}
	}});

	/**
	 * @class A "rollback" to a specific version needs user confirmation.
	 * @name ss.Form_EditForm_action_rollback
	 */
	$('#Form_EditForm .Actions #Form_EditForm_action_rollback').concrete('ss', function($){
		return/** @lends ss.Form_EditForm_action_rollback */{
		onclick: function(e) {
			// @todo i18n
			return confirm("Do you really want to copy the published content to the stage site?");
		}
	}});

	/**
	 * @class All forms in the right content panel should have closeable jQuery UI style titles.
	 * @name ss.contentPanel.form
	 */
	$('#contentPanel form').concrete('ss', function($){
		return/** @lends ss.contentPanel.form */{
		onmatch: function() {
		  // Style as title bar
			this.find(':header:first').titlebar({
				closeButton:true
			});
			// The close button should close the east panel of the layout
			this.find(':header:first .ui-dialog-titlebar-close').bind('click', function(e) {
				$('body.CMSMain').concrete('ss').MainLayout().close('east');

				return false;
			});
		}
	}});

	/**
	 * @class Control the site tree filter.
	 * Toggles search form fields based on a dropdown selection,
	 * similar to "Smart Search" criteria in iTunes.
	 * @name ss.Form_SeachTreeForm
	 */
	$('#Form_SearchTreeForm').concrete('ss', function($) {
		return/** @lends ss.Form_SeachTreeForm */{

		/**
		 * @type DOMElement
		 */
		SelectEl: null,

		onmatch: function() {
			var self = this;

			// TODO Cant bind to onsubmit/onreset directly because of IE6
			this.bind('submit', function(e) {return self._submitForm(e);});
			this.bind('reset', function(e) {return self._resetForm(e);});

			// only the first field should be visible by default
			this.find('.field').not(':first').hide();

			// generate the field dropdown
			this.setSelectEl($('<select name="options" class="options"></select>')
				.appendTo(this.find('fieldset:first'))
				.bind('change', function(e) {self._addField(e);})
			);

			this._setOptions();

		},

		_setOptions: function() {
			var self = this;

			// reset existing elements
			self.SelectEl().find('option').remove();

			// add default option
			// TODO i18n
			jQuery('<option value="0">Add Criteria</option>').appendTo(self.SelectEl())

			// populate dropdown values from existing fields
			this.find('.field').each(function() {
				$('<option />').appendTo(self.SelectEl())
					.val(this.id)
					.text($(this).find('label').text());
			});
		},

		/**
		 * Filter tree based on selected criteria.
		 */
		_submitForm: function(e) {
			var self = this;
			var data = [];

			// convert from jQuery object literals to hash map
			$(this.serializeArray()).each(function(i, el) {
				data[el.name] = el.value;
			});

			// Set new URL
			$('#sitetree')[0].setCustomURL(this.attr('action') + '&action_getfilteredsubtree=1', data);

			// Disable checkbox tree controls that currently don't work with search.
			// @todo: Make them work together
			if ($('#sitetree')[0].isDraggable) $('#sitetree')[0].stopBeingDraggable();
			this.find('.checkboxAboveTree :checkbox').val(false).attr('disabled', true);

			// disable buttons to avoid multiple submission
			//this.find(':submit').attr('disabled', true);

			this.find(':submit[name=action_getfilteredsubtree]').addClass('loading');

			this._reloadSitetree();

			return false;
		},

		_resetForm: function(e) {
			this.find('.field :input').clearFields().not(':first').hide();

			// Reset URL to default
			$('#sitetree')[0].clearCustomURL();

			// Enable checkbox tree controls
			this.find('.checkboxAboveTree :checkbox').attr('disabled', 'false');

			// reset all options, some of the might be removed
			this._setOptions();

			this._reloadSitetree();

			return false;
		},

		_addField: function(e) {
			var $select = $(e.target);
			// show formfield matching the option
			this.find('#' + $select.val()).show();

			// remove option from dropdown, each field should just exist once
			this.find('option[value=' + $select.val() + ']').remove();

			// jump back to default entry
			$select.val(0);

			return false;
		},

		_reloadSitetree: function() {
			var self = this;

			$('#sitetree')[0].reload({
				onSuccess :  function(response) {
					self.find(':submit').attr('disabled', false).removeClass('loading');
					self.find('.checkboxAboveTree :checkbox').attr('disabled', 'true');
					statusMessage('Filtered tree','good');
				},
				onFailure : function(response) {
					self.find(':submit').attr('disabled', false).removeClass('loading');
					self.find('.checkboxAboveTree :checkbox').attr('disabled', 'true');
					errorMessage('Could not filter site tree<br />' + response.responseText);
				}
			});
		}
	}});

})(jQuery);<|endoftext|>