<commit_before>/**
 * @author mrdoob / http://mrdoob.com/ and Alex Pletzer
 */

THREE.VTKLoader = function ( manager ) {

	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;

};

THREE.VTKLoader.prototype = {

	constructor: THREE.VTKLoader,

	load: function ( url, onLoad, onProgress, onError ) {

		var scope = this;

		var loader = new THREE.XHRLoader( scope.manager );
		loader.setCrossOrigin( this.crossOrigin );
		loader.load( url, function ( text ) {

			onLoad( scope.parse( text ) );

		}, onProgress, onError );

	},

	setCrossOrigin: function ( value ) {

		this.crossOrigin = value;

	},

	parse: function ( data ) {

		// connectivity of the triangles

		var indices = [];

		// triangles vertices

		var positions = [];

		// red, green, blue colors in the range 0 to 1
		var colors = [];

		// normal vector, one per vertex
		var normals = [];

		var result;

		// pattern for reading vertices, 3 floats or integers

		var pat3Floats = /(\-?\d+\.?[\d\-\+e]*)\s+(\-?\d+\.?[\d\-\+e]*)\s+(\-?\d+\.?[\d\-\+e]*)/g;

		// pattern for connectivity, an integer followed by any number of ints
		// the first integer is the number of polygon nodes

		var patConnectivity = /^(\d+)\s+([\s\d]*)/;

		// indicates start of vertex data section

		var patPOINTS = /^POINTS /;

		// indicates start of polygon connectivity section

		var patPOLYGONS = /^POLYGONS /;

		// POINT_DATA number_of_values
		var patPOINT_DATA = /^POINT_DATA[ ]+(\d+)/;

		// CELL_DATA number_of_polys
		var patCELL_DATA = /^CELL_DATA[ ]+(\d+)/;

		// Start of color section
		var patCOLOR_SCALARS = /^COLOR_SCALARS[ ]+(\w+)[ ]+3/;

		// NORMALS Normals float
		var patNORMALS = /^NORMALS[ ]+(\w+)[ ]+(\w+)/;

		var inPointsSection = false;
		var inPolygonsSection = false;
		var inPointDataSection = false;
		var inCellDataSection = false;
		var inColorSection = false;
		var inNormalsSection = false;

		var lines = data.split('\n');

		for ( var i in lines ) {

			var line = lines[ i ];

			if ( inPointsSection ) {

				// get the vertices

				while ( ( result = pat3Floats.exec( line ) ) !== null ) {

					var x = parseFloat( result[ 1 ] );
					var y = parseFloat( result[ 2 ] );
					var z = parseFloat( result[ 3 ] );
					positions.push( x, y, z );

				}

			} else if ( inPolygonsSection ) {

				if ( ( result = patConnectivity.exec( line ) ) !== null ) {

					// numVertices i0 i1 i2 ...

					var numVertices = parseInt( result[ 1 ] );
					var inds = result[ 2 ].split(/\s+/);

					if ( numVertices >= 3 ) {

					    var i0 = parseInt( inds[ 0 ] );
					    var i1, i2;
					    var k = 1;

					    // split the polygon in numVertices - 2 triangles

						for ( var j = 0; j < numVertices - 2; ++j ) {

							i1 = parseInt( inds[ k ] );
							i2 = parseInt( inds[ k  + 1 ] );
						  	indices.push( i0, i1, i2 );
							k++;

						}

					}

				}

			} else if ( inPointDataSection && inColorSection ) {

				// get the colors

				while ( ( result = pat3Floats.exec( line ) ) !== null ) {

					var r = parseFloat( result[ 1 ] );
					var g = parseFloat( result[ 2 ] );
					var b = parseFloat( result[ 3 ] );
					colors.push( r, g, b );

				}

			} else if ( inPointDataSection && inNormalsSection ) {

				// get the colors

				while ( ( result = pat3Floats.exec( line ) ) !== null ) {

					var nx = parseFloat( result[ 1 ] );
					var ny = parseFloat( result[ 2 ] );
					var nz = parseFloat( result[ 3 ] );
					normals.push( nx, ny, nz );

				}
			}

			if ( patPOLYGONS.exec( line ) !== null ) {

				inPolygonsSection = true;
				inPointsSection = false;

			} else if ( patPOINTS.exec( line ) !== null ) {

				inPolygonsSection = false;
				inPointsSection = true;

			} else if ( patPOINT_DATA.exec( line ) !== null ) {

				inPointDataSection = true;
				inPointsSection = false;
				inPolygonsSection = false;

			} else if ( patCELL_DATA.exec( line ) !== null ) {

				inCellDataSection = true;
				inPointsSection = false;
				inPolygonsSection = false;

			} else if ( patCOLOR_SCALARS.exec( line ) !== null ) {

				inColorSection = true;
				inNormalsSection = false;
				inPointsSection = false;
				inPolygonsSection = false;

			} else if ( patNORMALS.exec( line ) !== null ) {

				inColorSection = false;
				inNormalsSection = true;
				inPointsSection = false;
				inPolygonsSection = false;

			}
		}

		var geometry = new THREE.BufferGeometry();
		geometry.addAttribute( 'index', new THREE.BufferAttribute( new ( indices.length > 65535 ? Uint32Array : Uint16Array )( indices ), 1 ) );
		geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( positions ), 3 ) );

		if ( colors.length == positions.length ) {

			geometry.addAttribute( 'color', new THREE.BufferAttribute( new Float32Array( colors ), 3 ) );

		}

		if ( normals.length == positions.length ) {

			geometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( normals ), 3 ) );

		}

		return geometry;

	}

};

THREE.EventDispatcher.prototype.apply( THREE.VTKLoader.prototype );
<commit_msg>+ support for cell centered fields<commit_after>/**
 * @author mrdoob / http://mrdoob.com/ and Alex Pletzer
 */

THREE.VTKLoader = function ( manager ) {

	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;

};

THREE.VTKLoader.prototype = {

	constructor: THREE.VTKLoader,

	load: function ( url, onLoad, onProgress, onError ) {

		var scope = this;

		var loader = new THREE.XHRLoader( scope.manager );
		loader.setCrossOrigin( this.crossOrigin );
		loader.load( url, function ( text ) {

			onLoad( scope.parse( text ) );

		}, onProgress, onError );

	},

	setCrossOrigin: function ( value ) {

		this.crossOrigin = value;

	},

	parse: function ( data ) {

		// connectivity of the triangles

		var indices = [];

		// triangles vertices

		var positions = [];

		// red, green, blue colors in the range 0 to 1
		var colors = [];

		// normal vector, one per vertex
		var normals = [];

		var result;

		// pattern for reading vertices, 3 floats or integers

		var pat3Floats = /(\-?\d+\.?[\d\-\+e]*)\s+(\-?\d+\.?[\d\-\+e]*)\s+(\-?\d+\.?[\d\-\+e]*)/g;

		// pattern for connectivity, an integer followed by any number of ints
		// the first integer is the number of polygon nodes

		var patConnectivity = /^(\d+)\s+([\s\d]*)/;

		// indicates start of vertex data section

		var patPOINTS = /^POINTS /;

		// indicates start of polygon connectivity section

		var patPOLYGONS = /^POLYGONS /;

		// POINT_DATA number_of_values
		var patPOINT_DATA = /^POINT_DATA[ ]+(\d+)/;

		// CELL_DATA number_of_polys
		var patCELL_DATA = /^CELL_DATA[ ]+(\d+)/;

		// Start of color section
		var patCOLOR_SCALARS = /^COLOR_SCALARS[ ]+(\w+)[ ]+3/;

		// NORMALS Normals float
		var patNORMALS = /^NORMALS[ ]+(\w+)[ ]+(\w+)/;

		var inPointsSection = false;
		var inPolygonsSection = false;
		var inPointDataSection = false;
		var inCellDataSection = false;
		var inColorSection = false;
		var inNormalsSection = false;

		var lines = data.split('\n');

		for ( var i in lines ) {

			var line = lines[ i ];

			if ( inPointsSection ) {

				// get the vertices

				while ( ( result = pat3Floats.exec( line ) ) !== null ) {

					var x = parseFloat( result[ 1 ] );
					var y = parseFloat( result[ 2 ] );
					var z = parseFloat( result[ 3 ] );
					positions.push( x, y, z );

				}

			} else if ( inPolygonsSection ) {

				if ( ( result = patConnectivity.exec( line ) ) !== null ) {

					// numVertices i0 i1 i2 ...

					var numVertices = parseInt( result[ 1 ] );
					var inds = result[ 2 ].split(/\s+/);

					if ( numVertices >= 3 ) {

					    var i0 = parseInt( inds[ 0 ] );
					    var i1, i2;
					    var k = 1;

					    // split the polygon in numVertices - 2 triangles

						for ( var j = 0; j < numVertices - 2; ++j ) {

							i1 = parseInt( inds[ k ] );
							i2 = parseInt( inds[ k  + 1 ] );
						  	indices.push( i0, i1, i2 );
							k++;

						}

					}

				}

			} else if ( inPointDataSection ) {

				if ( inColorSection ) {

					// get the colors

					while ( ( result = pat3Floats.exec( line ) ) !== null ) {

						var r = parseFloat( result[ 1 ] );
						var g = parseFloat( result[ 2 ] );
						var b = parseFloat( result[ 3 ] );
						colors.push( r, g, b );

					}

				} else if ( inNormalsSection ) {

					// get the normal vectors

					while ( ( result = pat3Floats.exec( line ) ) !== null ) {

						var nx = parseFloat( result[ 1 ] );
						var ny = parseFloat( result[ 2 ] );
						var nz = parseFloat( result[ 3 ] );
						normals.push( nx, ny, nz );

					}

				}

            } else if ( inCellDataSection ) {

				if ( inColorSection ) {

					// get the colors

					while ( ( result = pat3Floats.exec( line ) ) !== null ) {

						var r = parseFloat( result[ 1 ] );
						var g = parseFloat( result[ 2 ] );
						var b = parseFloat( result[ 3 ] );
						colors.push( r, g, b );

					}

				} else if ( inNormalsSection ) {

					// get the normal vectors

					while ( ( result = pat3Floats.exec( line ) ) !== null ) {

						var nx = parseFloat( result[ 1 ] );
						var ny = parseFloat( result[ 2 ] );
						var nz = parseFloat( result[ 3 ] );
						normals.push( nx, ny, nz );

					}

				}

            }

			if ( patPOLYGONS.exec( line ) !== null ) {

				inPolygonsSection = true;
				inPointsSection = false;

			} else if ( patPOINTS.exec( line ) !== null ) {

				inPolygonsSection = false;
				inPointsSection = true;

			} else if ( patPOINT_DATA.exec( line ) !== null ) {

				inPointDataSection = true;
				inPointsSection = false;
				inPolygonsSection = false;

			} else if ( patCELL_DATA.exec( line ) !== null ) {

				inCellDataSection = true;
				inPointsSection = false;
				inPolygonsSection = false;

			} else if ( patCOLOR_SCALARS.exec( line ) !== null ) {

				inColorSection = true;
				inNormalsSection = false;
				inPointsSection = false;
				inPolygonsSection = false;

			} else if ( patNORMALS.exec( line ) !== null ) {

				inColorSection = false;
				inNormalsSection = true;
				inPointsSection = false;
				inPolygonsSection = false;

			}
		}

		var geometry;
		var stagger = 'point';

		if ( colors.length == indices.length ) {

		    stagger = 'cell';

		}

		if ( stagger == 'point' ) {

			// nodal. Use BufferGeometry

		    geometry = new THREE.BufferGeometry();

		    geometry.addAttribute( 'index', new THREE.BufferAttribute( new ( indices.length > 65535 ? Uint32Array : Uint16Array )( indices ), 1 ) );
		    geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( positions ), 3 ) );

		    if ( colors.length == positions.length ) {

			    geometry.addAttribute( 'color', new THREE.BufferAttribute( new Float32Array( colors ), 3 ) );

			}

			if ( normals.length == positions.length ) {

				geometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( normals ), 3 ) );

			}

		} else {

			// cell centered colors. The only way to attach solid colors to triangles is
			// to use Geometry

			geometry = new THREE.Geometry();

			var numTriangles = indices.length / 3;
			var numPoints = positions.length / 3;
			var va, vb, vc;
			var face;
		    var colorA, colorB, colorC;
			var ia, ib, ic;
			var x, y, z;
			var r, g, b;

			for ( var j = 0; j < numPoints; ++j ) {

				x = positions[ 3*j + 0 ];
				y = positions[ 3*j + 1 ];
				z = positions[ 3*j + 2 ];
				geometry.vertices.push( new THREE.Vector3( x, y, z ) );

			}

			for ( var i = 0; i < numTriangles; ++i ) {

				ia = indices[ 3*i + 0 ];
				ib = indices[ 3*i + 1 ];
				ic = indices[ 3*i + 2 ];

				geometry.faces.push( new THREE.Face3( ia, ib, ic ) );

			}

			if ( colors.length == numTriangles * 3 ) {

				for ( var i = 0; i < numTriangles; ++i ) {

					face = geometry.faces[i];
					r = colors[ 3*i + 0 ];
					g = colors[ 3*i + 1 ];
					b = colors[ 3*i + 2 ];
					colorA = new THREE.Color();
					colorA.setRGB( r, g, b );
					colorB = new THREE.Color();
					colorB.setRGB( r, g, b );
					colorC = new THREE.Color();
					colorC.setRGB( r, g, b );
					face.vertexColors[0] = colorA;
					face.vertexColors[1] = colorB;
					face.vertexColors[2] = colorC;

				}

			}

		}

		return geometry;

	}

};

THREE.EventDispatcher.prototype.apply( THREE.VTKLoader.prototype );
<|endoftext|>


<commit_before>/**
 * todo: 对象的扩展
 * ref: http://es6.ruanyifeng.com/#docs/object
 * Created by wind on 17/5/5.
 */
import { expect } from 'chai';

describe('1. 属性的简洁表示法', function(){
  it('(1) ES6 允许直接写入变量和函数，作为对象的属性和方法。', function(){
    let foo = 'bar';
    let baz = {foo};
    expect(baz).to.deep.equal({foo:'bar'});
  });
});
describe('2. 属性名表达式', function(){});
describe('3. 方法的name属性', function(){});
describe('4. Object.is()', function(){});
describe('5. Object.assign()', function(){});
describe('6. 属性的可枚举性', function(){});
describe('7. 属性的遍历', function(){});
describe('8. __proto__属性，Object.setPrototypeOf(), Object.getPrototypeOf()', function(){});
describe('9. Object.keys(), object.values(), object.entries()', function(){});
describe('10. 对象的扩展运算符', function(){});
describe('11. Object.getOwnPropertyDescriptors()', function(){});
describe('12. Null传导运算符', function(){});
<commit_msg>chapter9<commit_after>/**
 * todo: 对象的扩展
 * ref: http://es6.ruanyifeng.com/#docs/object
 * Created by wind on 17/5/5.
 */
import { expect } from 'chai';

describe('1. 属性的简洁表示法', function(){
  it('(1) ES6 允许直接写入变量和函数，作为对象的属性和方法。', function(){
    let foo = 'bar';
    let baz = {foo};
    expect(baz).to.deep.equal({foo:'bar'});
  });

  it('(2) let name= "xxx", age=23; let c = {name,age}; 那么c=>{name: "xxx", age: 23}', function(){
    let name = 'xxx',
        age = 23,
        c = {name, age};
    expect(c).to.deep.equal({name: "xxx", age: 23});
  });
});
describe('2. 属性名表达式', function(){
  it('(1) ES6 允许字面量定义对象时，用表达式作为对象的属性名。',function(){
    let propKey = 'foo';
    let obj = {
      [propKey]: true,
      ['sec_' + propKey]: 'secVal'
    };

    expect(obj).to.deep.equal({foo:true, sec_foo: 'secVal'});
  });
  it('(2) 使用表达式定义方法名', function(){
    let obj = {
      ['h' + 'ello']() {
        return 'hi';
      }
    };

    expect(typeof obj.hello).to.equal('function');
  });
});
describe('3. 方法的name属性', function(){
  it('(1) 方法的name属性返回函数名', function(){
    function funcName(){};
    expect(funcName.name).to.equal('funcName');
  });

  it('(2) 对象的方法使用了取值函数（getter）和存值函数（setter）',  function(){
    const obj = {
      get wheel() {},
      set wheel(name){}
    };
    const descriptor = Object.getOwnPropertyDescriptor(obj, 'wheel');
    expect(descriptor.get.name).to.equal('get wheel');
    expect(descriptor.set.name).to.equal('set wheel');
  });

  it('(3) bind方法创造的函数,name返回bound加原函数的名字', function(){
    let doSomething = function(){};
    let nameVal = doSomething.bind().name;
    expect(nameVal).to.equal('bound doSomething');
  });

  it('(4) unction构造函数创造的函数，name属性返回anonymous', function(){
    let nameVal = (new Function()).name; // "anonymous"
    expect(nameVal).to.equal('anonymous');
  });
  it('(5) 如果对象的方法是一个 Symbol 值，那么name属性返回的是这个 Symbol 值的描述。', function(){
    const key1 = Symbol('description');
    const key2 = Symbol();
    let obj = {
      [key1]() {},
      [key2]() {},
    };
    // expect(obj[key1].name).to.equal('[description]'); // "[description]"
    expect(obj[key2].name).to.equal(''); // ""
  });
});
describe('4. Object.is() 同值相等算法', function(){
  it('(1) +0 === -0 为true', function(){ expect(+0 === -0).to.equal(true); });
  it('(2) NaN === NaN 为false', function(){ expect(NaN === NaN).to.equal(false); });
  it('(3) Object.is(+0, -0) 为false', function(){ expect(Object.is(+0, -0)).to.equal(false); });
  it('(4) Object.is(NaN, NaN) 为true', function(){ expect(Object.is(NaN, NaN)).to.equal(true); });
});

describe('5. Object.assign()', function(){
  it('(1) Object.assign方法用于对象的合并。', function(){
    let target = { a: 1 },
        source1 = { b: 2 },
        source2 = { c: 3 };

    Object.assign(target, source1, source2);
    expect(target).to.deep.equal({a:1, b:2, c:3});
  });
  it('(2) Object.assign方法实行的是浅拷贝，也就是说，如果源对象某个属性的值是对象，那么目标对象拷贝得到的是这个对象的引用。',function(){
    let obj1 = {a: {b: 1}};
    let obj2 = Object.assign({}, obj1);
    expect(obj2.a.b).to.equal(1);
    obj1.a.b = 2;
    expect(obj2.a.b).to.equal(2);

  });
});
describe('6. 属性的可枚举性', function(){
  it('(1) 对象的每个属性都有一个描述对象（Descriptor），用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。', function(){
    let obj = { foo: 123 };
    let fooProp = Object.getOwnPropertyDescriptor(obj, 'foo');
    expect(fooProp).to.deep.equal({
      value: 123,
      writable: true,
      enumerable: true,
      configurable: true
    });
  });
});
describe('7. 属性的遍历', function(){
  it('(1) Reflect.ownKeys返回一个数组，包含对象自身的所有属性，不管属性名是Symbol或字符串，也不管是否可枚举。', function(){
    let arr = Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 });
    expect(arr).to.deep.equal(['2', '10', 'b', 'a', Symbol()]);
  });
});
describe('8. __proto__属性，Object.setPrototypeOf(), Object.getPrototypeOf()', function(){});
describe('9. Object.keys(), object.values(), object.entries()', function(){});
describe('10. 对象的扩展运算符', function(){});
describe('11. Object.getOwnPropertyDescriptors()', function(){});
describe('12. Null传导运算符', function(){});
<|endoftext|>


<commit_before>module('lively.ide.codeeditor.Completions').requires('lively.ide.codeeditor.ace', 'lively.Network').toRun(function() {

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// "dynamic" completions, created by evaluating an expression and enumerating the
// properties of the resulting object
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

// FIXME move this stuff into a generic JS meta interface module so that it can be
// easily reused from the server

Object.subclass('lively.ide.codeeditor.Completions.ProtocolLister',
'initializing', {
    initialize: function(textMorph) {
        this.textMorph = textMorph;
    }
},
'interface', {
    getCompletions: function(code, evalFunc) {
        var err, completions
        getCompletions(evalFunc, code, function(e, c, pre) {
            err = e, completions = {prefix: pre, completions: c}; })
        if (err) { alert(err); return {error: String(err.stack || err), prefix: '', completions: []}; }
        else return completions;
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// rk 2013-10-10 I extracted the code below into a nodejs module (since this
// stuff is also useful on a server and in other contexts). Right now we have no
// good way to load nodejs modules into Lively and I inline the code here. Please
// fix soon!
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// helper
function signatureOf(name, func) {
    var source = String(func),
        match = source.match(/function\s*[a-zA-Z0-9_$]*\s*\(([^\)]*)\)/),
        params = (match && match[1]) || '';
    return name + '(' + params + ')';
}

function isClass(obj) {
    if (obj === obj
      || obj === Array
      || obj === Function
      || obj === String
      || obj === Boolean
      || obj === Date
      || obj === RegExp
      || obj === Number) return true;
    return (obj instanceof Function)
        && ((obj.superclass !== undefined)
         || (obj._superclass !== undefined));
}

function pluck(list, prop) { return list.map(function(ea) { return ea[prop]; }); }

function getObjectForCompletion(evalFunc, stringToEval, thenDo) {
    // thenDo = function(err, obj, startLetters)
    var idx = stringToEval.lastIndexOf('.'),
        startLetters = '';
    if (idx >= 0) {
        startLetters = stringToEval.slice(idx+1);
        stringToEval = stringToEval.slice(0,idx);
    } else {
        startLetters = stringToEval;
        stringToEval = 'Global';
    }
    var completions = [];
    try {
        var obj = evalFunc(stringToEval);
    } catch (e) {
        thenDo(e, null, null);
    }
    thenDo(null, obj, startLetters);
}

function propertyExtract(excludes, obj, extractor) {
    // show(''+excludes)
    return Object.getOwnPropertyNames(obj)
        .filter(function(key) { return excludes.indexOf(key) === -1; })
        .map(extractor)
        .filter(function(ea) { return !!ea; })
        .sort(function(a,b) {
            return a.name < b.name ? -1 : (a.name > b.name ? 1 : 0); });
}

function getMethodsOf(excludes, obj) {
    return propertyExtract(excludes, obj, function(key) {

        if ((obj.__lookupGetter__ && obj.__lookupGetter__(key)) || typeof obj[key] !== 'function') return null;
        return {name: key, completion: signatureOf(key, obj[key])}; })
}

function getAttributesOf(excludes, obj) {
    return propertyExtract(excludes, obj, function(key) {
        if ((obj.__lookupGetter__ && !obj.__lookupGetter__(key)) && typeof obj[key] === 'function') return null;
        return {name: key, completion: key}; })
}

function getProtoChain(obj) {
    var protos = [], proto = obj;
    while (obj) { protos.push(obj); obj = obj.__proto__ }
    return protos;
}

function getDescriptorOf(originalObj, proto) {
    function shorten(s, len) {
        if (s.length > len) s = s.slice(0,len) + '...';
        return s.replace(/\n/g, '').replace(/\s+/g, ' ');
    }

    var stringified;
    try { stringified = String(originalObj); } catch (e) { stringified = "{/*...*/}"; }

    if (originalObj === proto) {
        if (typeof originalObj !== 'function') return shorten(stringified, 50);
        var funcString = stringified,
            body = shorten(funcString.slice(funcString.indexOf('{')+1, funcString.lastIndexOf('}')), 50);
        return signatureOf(originalObj.displayName || originalObj.name || 'function', originalObj) + ' {' + body + '}';
    }

    var klass = proto.hasOwnProperty('constructor') && proto.constructor;
    if (!klass) return 'prototype';
    if (typeof klass.type === 'string' && klass.type.length) return shorten(klass.type, 50);
    if (typeof klass.name === 'string' && klass.name.length) return shorten(klass.name, 50);
    return "anonymous class";
}

function getCompletionsOfObj(obj, thenDo) {
    var err, completions;
    try {
        var excludes = [];
        completions = getProtoChain(obj).map(function(proto) {
            var descr = getDescriptorOf(obj, proto),
                methodsAndAttributes = getMethodsOf(excludes, proto)
                    .concat(getAttributesOf(excludes, proto));
            excludes = excludes.concat(pluck(methodsAndAttributes, 'name'));
            return [descr, pluck(methodsAndAttributes, 'completion')];
        });
    } catch (e) { err = e; }
    thenDo(err, completions);
}

function getCompletions(evalFunc, string, thenDo) {
    // thendo = function(err, completions/*ARRAY*/)
    // eval string and for the resulting object find attributes and methods,
    // grouped by its prototype / class chain
    // if string is something like "foo().bar.baz" then treat "baz" as start
    // letters = filter for properties of foo().bar
    // ("foo().bar.baz." for props of the result of the complete string)
    getObjectForCompletion(evalFunc, string, function(err, obj, startLetters) {
        if (err) { thenDo(err); return }
        var excludes = [];
        var completions = getProtoChain(obj).map(function(proto) {
            var descr = getDescriptorOf(obj, proto),
                methodsAndAttributes = getMethodsOf(excludes, proto)
                    .concat(getAttributesOf(excludes, proto));
            excludes = excludes.concat(pluck(methodsAndAttributes, 'name'));
            return [descr, pluck(methodsAndAttributes, 'completion')];
        });
        thenDo(err, completions, startLetters);
    })
}

/*
;(function testCompletion() {
    function assertCompletions(err, completions, prefix) {
        assert(!err, 'getCompletions error: ' + err);
        assert(prefix === '', 'prefix: ' + prefix);
        assert(completions.length === 3, 'completions does not contain 3 groups ' + completions.length)
        assert(completions[2][0] === 'Object', 'last completion group is Object')
        objectCompletions = completions.slice(0,2)
        expected = [["[object Object]", ["m1(a)","m2(x)","a"]],
                    ["prototype", ["m3(a,b,c)"]]]
        assert(Objects.equals(expected, objectCompletions), 'compl not equal');
        alertOK('all good!')

    }
    function evalFunc(string) { return eval(string); }
    var code = "obj1 = {m2: function() {}, m3:function(a,b,c) {}}\n"
             + "obj2 = {a: 3, m1: function(a) {}, m2:function(x) {}, __proto__: obj1}\n"
             + "obj2."
    getCompletions(evalFunc, code, assertCompletions)
})();
*/
    },
    evalSelectionAndOpenNarrower: function() {
        var evalIt, code, editor = this.textMorph;
        editor.saveExcursion(function(reset) {
            evalIt = editor.boundEval.bind(editor);
            code = editor.getSelectionOrLineString();
            reset();
        });
        this.openNarrower(this.getCompletions(code, evalIt));
    }
},
'accessing', {

    openNarrower: function(completionSpec) {
        var completions = completionSpec.completions;
        var prefix = completionSpec.prefix;
        var ed = this.textMorph;

        var candidates = completions.reduce(function(candidates, protoGroup) {
            var protoName = protoGroup[0], completions = protoGroup[1];
            return candidates.concat(completions.map(function(completion) {
                return {
                    isListItem: true,
                    string: '[' + protoName.truncate(50) + '] ' + completion,
                    value: complete.curry(completion)
                }
            }));
        }, []);

        lively.ide.tools.SelectionNarrowing.getNarrower({
            name: 'lively.morphic.Text.ProtocolLister.CompletionNarrower',
            setup: function(narrower) {
                lively.bindings.connect(narrower, 'confirmedSelection', narrower, 'remove');
                lively.bindings.connect(narrower, 'escapePressed', narrower, 'remove');
            },
            spec: {
                prompt: 'pattern: ',
                candidates: candidates,
                input: prefix || '',
                actions: [{name: 'insert completion', exec: function(candidate) { candidate(); }}]
            }
        });

        // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

        function complete(completion) {
          if (prefix && prefix.length) {
              var sel = ed.aceEditor.selection;
              if (sel.isBackwards()) sel.setRange(sel.getRange(), false/*reverse*/);
              sel.clearSelection();
              var range = ed.aceEditor.find({needle: prefix, backwards: true, preventScroll: true})
              ed.replace(range, '');
          }
          var id = completion.match(/^[^\(]+/)[0];
          var needsBrackets = !lively.Class.isValidIdentifier(id);
          if (needsBrackets) {
            var pos = ed.getCursorPositionAce();
            var Range = lively.ide.ace.require("ace/range").Range;
            var beforeRange = Range.fromPoints(lively.lang.obj.merge(pos, {column:pos.column-1}), pos);
            if (ed.getTextRange(beforeRange) === '.') ed.replace(beforeRange, '');
            if (!completion.match(/^[0-9]+$/)) completion = Strings.print(completion);
            completion = '[' +  completion + ']';
          }
          ed.printObject(ed.aceEditor, completion, true);
        }
    },

    createSubMenuItemForCompletion: function(completionString, optStartLetters, type) {
        var itemString = completionString;
        if (type) itemString += ' ' + type;
        var textMorph = this.textMorph;
            if (typeof(optStartLetters) !== 'undefined') {
            completionString = completionString.substring(optStartLetters.size());
        }
        return [itemString, function() {
            textMorph.focus();
            textMorph.clearSelection();
            textMorph.insertAtCursor(completionString, true);
        }];
    }

});

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// completions based on ace completer objects
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

Object.extend(lively.ide.codeeditor.Completions, {

    setupCompletions: function(thenDo) {
        console.log('setup code completions');

        [wordsFromFiles, installCompleter, done].doAndContinue(null, thenDo);

        var words;
        function wordsFromFiles(next) {
            var ideSupportServer = URL.nodejsBase.withFilename('IDESupportServer/indexedLivelyWords')
                .asWebResource().beAsync();
            ideSupportServer.get().withJSONWhenDone(function(json, s) {
                var err = (json && json.error) || (!s.isSuccess() && s);
                if (err) {
                    console.error('Error in ide code completion preparation: %s', s);
                } else { words = json; next(); }
            });
        }

        function installCompleter(next) {
            // 1) define completer
            lively.ide.WordCompleter = {wordsFromFiles: words};
            Object.extend(lively.ide.WordCompleter, {
                getCompletions: function(editor, session, pos, prefix, callback) {
                    if (prefix.length === 0) { callback(null, []); return }
                    var startLetter = prefix[0].toLowerCase(),
                        wordList = this.wordsFromFiles[startLetter], result = [];
                    for (var word in wordList) {
                        if (word.lastIndexOf(prefix, 0) !== 0) continue;
                        result.push({
                            name: word,
                            value: word,
                            score: wordList[word],
                            meta: "lively"
                        });
                    }
                    callback(null, result);
                }
            });
            // 2) register completer
            var langTools = lively.ide.ace.require('ace/ext/language_tools');
            langTools.addCompleter(lively.ide.WordCompleter);
            next();
        }

        function done(next) { alertOK('Word completion installed!'); next(); }
    }
});

(function setupCompletionsOnLoad() {
    (function() {
        if (UserAgent.isNodejs
         || UserAgent.isWorker
         || !lively.Config.get('computeCodeEditorCompletionsOnStartup'))
         return;
        lively.ide.codeeditor.Completions.setupCompletions();
    }).delay(3);
})();

}) // end of module
<commit_msg>completions: bracket exceptions<commit_after>module('lively.ide.codeeditor.Completions').requires('lively.ide.codeeditor.ace', 'lively.Network').toRun(function() {

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// "dynamic" completions, created by evaluating an expression and enumerating the
// properties of the resulting object
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

// FIXME move this stuff into a generic JS meta interface module so that it can be
// easily reused from the server

Object.subclass('lively.ide.codeeditor.Completions.ProtocolLister',
'initializing', {
    initialize: function(textMorph) {
        this.textMorph = textMorph;
    }
},
'interface', {
    getCompletions: function(code, evalFunc) {
        var err, completions
        getCompletions(evalFunc, code, function(e, c, pre) {
            err = e, completions = {prefix: pre, completions: c}; })
        if (err) { alert(err); return {error: String(err.stack || err), prefix: '', completions: []}; }
        else return completions;
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// rk 2013-10-10 I extracted the code below into a nodejs module (since this
// stuff is also useful on a server and in other contexts). Right now we have no
// good way to load nodejs modules into Lively and I inline the code here. Please
// fix soon!
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// helper
function signatureOf(name, func) {
    var source = String(func),
        match = source.match(/function\s*[a-zA-Z0-9_$]*\s*\(([^\)]*)\)/),
        params = (match && match[1]) || '';
    return name + '(' + params + ')';
}

function isClass(obj) {
    if (obj === obj
      || obj === Array
      || obj === Function
      || obj === String
      || obj === Boolean
      || obj === Date
      || obj === RegExp
      || obj === Number) return true;
    return (obj instanceof Function)
        && ((obj.superclass !== undefined)
         || (obj._superclass !== undefined));
}

function pluck(list, prop) { return list.map(function(ea) { return ea[prop]; }); }

function getObjectForCompletion(evalFunc, stringToEval, thenDo) {
    // thenDo = function(err, obj, startLetters)
    var idx = stringToEval.lastIndexOf('.'),
        startLetters = '';
    if (idx >= 0) {
        startLetters = stringToEval.slice(idx+1);
        stringToEval = stringToEval.slice(0,idx);
    } else {
        startLetters = stringToEval;
        stringToEval = 'Global';
    }
    var completions = [];
    try {
        var obj = evalFunc(stringToEval);
    } catch (e) {
        thenDo(e, null, null);
    }
    thenDo(null, obj, startLetters);
}

function propertyExtract(excludes, obj, extractor) {
    // show(''+excludes)
    return Object.getOwnPropertyNames(obj)
        .filter(function(key) { return excludes.indexOf(key) === -1; })
        .map(extractor)
        .filter(function(ea) { return !!ea; })
        .sort(function(a,b) {
            return a.name < b.name ? -1 : (a.name > b.name ? 1 : 0); });
}

function getMethodsOf(excludes, obj) {
    return propertyExtract(excludes, obj, function(key) {

        if ((obj.__lookupGetter__ && obj.__lookupGetter__(key)) || typeof obj[key] !== 'function') return null;
        return {name: key, completion: signatureOf(key, obj[key])}; })
}

function getAttributesOf(excludes, obj) {
    return propertyExtract(excludes, obj, function(key) {
        if ((obj.__lookupGetter__ && !obj.__lookupGetter__(key)) && typeof obj[key] === 'function') return null;
        return {name: key, completion: key}; })
}

function getProtoChain(obj) {
    var protos = [], proto = obj;
    while (obj) { protos.push(obj); obj = obj.__proto__ }
    return protos;
}

function getDescriptorOf(originalObj, proto) {
    function shorten(s, len) {
        if (s.length > len) s = s.slice(0,len) + '...';
        return s.replace(/\n/g, '').replace(/\s+/g, ' ');
    }

    var stringified;
    try { stringified = String(originalObj); } catch (e) { stringified = "{/*...*/}"; }

    if (originalObj === proto) {
        if (typeof originalObj !== 'function') return shorten(stringified, 50);
        var funcString = stringified,
            body = shorten(funcString.slice(funcString.indexOf('{')+1, funcString.lastIndexOf('}')), 50);
        return signatureOf(originalObj.displayName || originalObj.name || 'function', originalObj) + ' {' + body + '}';
    }

    var klass = proto.hasOwnProperty('constructor') && proto.constructor;
    if (!klass) return 'prototype';
    if (typeof klass.type === 'string' && klass.type.length) return shorten(klass.type, 50);
    if (typeof klass.name === 'string' && klass.name.length) return shorten(klass.name, 50);
    return "anonymous class";
}

function getCompletionsOfObj(obj, thenDo) {
    var err, completions;
    try {
        var excludes = [];
        completions = getProtoChain(obj).map(function(proto) {
            var descr = getDescriptorOf(obj, proto),
                methodsAndAttributes = getMethodsOf(excludes, proto)
                    .concat(getAttributesOf(excludes, proto));
            excludes = excludes.concat(pluck(methodsAndAttributes, 'name'));
            return [descr, pluck(methodsAndAttributes, 'completion')];
        });
    } catch (e) { err = e; }
    thenDo(err, completions);
}

function getCompletions(evalFunc, string, thenDo) {
    // thendo = function(err, completions/*ARRAY*/)
    // eval string and for the resulting object find attributes and methods,
    // grouped by its prototype / class chain
    // if string is something like "foo().bar.baz" then treat "baz" as start
    // letters = filter for properties of foo().bar
    // ("foo().bar.baz." for props of the result of the complete string)
    getObjectForCompletion(evalFunc, string, function(err, obj, startLetters) {
        if (err) { thenDo(err); return }
        var excludes = [];
        var completions = getProtoChain(obj).map(function(proto) {
            var descr = getDescriptorOf(obj, proto),
                methodsAndAttributes = getMethodsOf(excludes, proto)
                    .concat(getAttributesOf(excludes, proto));
            excludes = excludes.concat(pluck(methodsAndAttributes, 'name'));
            return [descr, pluck(methodsAndAttributes, 'completion')];
        });
        thenDo(err, completions, startLetters);
    })
}

/*
;(function testCompletion() {
    function assertCompletions(err, completions, prefix) {
        assert(!err, 'getCompletions error: ' + err);
        assert(prefix === '', 'prefix: ' + prefix);
        assert(completions.length === 3, 'completions does not contain 3 groups ' + completions.length)
        assert(completions[2][0] === 'Object', 'last completion group is Object')
        objectCompletions = completions.slice(0,2)
        expected = [["[object Object]", ["m1(a)","m2(x)","a"]],
                    ["prototype", ["m3(a,b,c)"]]]
        assert(Objects.equals(expected, objectCompletions), 'compl not equal');
        alertOK('all good!')

    }
    function evalFunc(string) { return eval(string); }
    var code = "obj1 = {m2: function() {}, m3:function(a,b,c) {}}\n"
             + "obj2 = {a: 3, m1: function(a) {}, m2:function(x) {}, __proto__: obj1}\n"
             + "obj2."
    getCompletions(evalFunc, code, assertCompletions)
})();
*/
    },
    evalSelectionAndOpenNarrower: function() {
        var evalIt, code, editor = this.textMorph;
        editor.saveExcursion(function(reset) {
            evalIt = editor.boundEval.bind(editor);
            code = editor.getSelectionOrLineString();
            reset();
        });
        this.openNarrower(this.getCompletions(code, evalIt));
    }
},
'accessing', {

    openNarrower: function(completionSpec) {
        var completions = completionSpec.completions;
        var prefix = completionSpec.prefix;
        var ed = this.textMorph;

        var candidates = completions.reduce(function(candidates, protoGroup) {
            var protoName = protoGroup[0], completions = protoGroup[1];
            return candidates.concat(completions.map(function(completion) {
                return {
                    isListItem: true,
                    string: '[' + protoName.truncate(50) + '] ' + completion,
                    value: complete.curry(completion)
                }
            }));
        }, []);

        lively.ide.tools.SelectionNarrowing.getNarrower({
            name: 'lively.morphic.Text.ProtocolLister.CompletionNarrower',
            setup: function(narrower) {
                lively.bindings.connect(narrower, 'confirmedSelection', narrower, 'remove');
                lively.bindings.connect(narrower, 'escapePressed', narrower, 'remove');
            },
            spec: {
                prompt: 'pattern: ',
                candidates: candidates,
                input: prefix || '',
                actions: [{name: 'insert completion', exec: function(candidate) { candidate(); }}]
            }
        });

        // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

        function complete(completion) {
          if (prefix && prefix.length) {
              var sel = ed.aceEditor.selection;
              if (sel.isBackwards()) sel.setRange(sel.getRange(), false/*reverse*/);
              sel.clearSelection();
              var range = ed.aceEditor.find({needle: prefix, backwards: true, preventScroll: true})
              ed.replace(range, '');
          }
          var id = completion.match(/^[^\(]+/)[0],
              bracketExceptions = ["delete", "import", "export"],
              needsBrackets = !bracketExceptions.include(id) && !lively.Class.isValidIdentifier(id);
          if (needsBrackets) {
            var pos = ed.getCursorPositionAce();
            var Range = lively.ide.ace.require("ace/range").Range;
            var beforeRange = Range.fromPoints(lively.lang.obj.merge(pos, {column:pos.column-1}), pos);
            if (ed.getTextRange(beforeRange) === '.') ed.replace(beforeRange, '');
            if (!completion.match(/^[0-9]+$/)) completion = Strings.print(completion);
            completion = '[' +  completion + ']';
            completion = completion.replace(/(\([^\)]*\))(.*)/, "$2$1");
          }
          ed.printObject(ed.aceEditor, completion, true);
        }
    },

    createSubMenuItemForCompletion: function(completionString, optStartLetters, type) {
        var itemString = completionString;
        if (type) itemString += ' ' + type;
        var textMorph = this.textMorph;
            if (typeof(optStartLetters) !== 'undefined') {
            completionString = completionString.substring(optStartLetters.size());
        }
        return [itemString, function() {
            textMorph.focus();
            textMorph.clearSelection();
            textMorph.insertAtCursor(completionString, true);
        }];
    }

});

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// completions based on ace completer objects
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

Object.extend(lively.ide.codeeditor.Completions, {

    setupCompletions: function(thenDo) {
        console.log('setup code completions');

        [wordsFromFiles, installCompleter, done].doAndContinue(null, thenDo);

        var words;
        function wordsFromFiles(next) {
            var ideSupportServer = URL.nodejsBase.withFilename('IDESupportServer/indexedLivelyWords')
                .asWebResource().beAsync();
            ideSupportServer.get().withJSONWhenDone(function(json, s) {
                var err = (json && json.error) || (!s.isSuccess() && s);
                if (err) {
                    console.error('Error in ide code completion preparation: %s', s);
                } else { words = json; next(); }
            });
        }

        function installCompleter(next) {
            // 1) define completer
            lively.ide.WordCompleter = {wordsFromFiles: words};
            Object.extend(lively.ide.WordCompleter, {
                getCompletions: function(editor, session, pos, prefix, callback) {
                    if (prefix.length === 0) { callback(null, []); return }
                    var startLetter = prefix[0].toLowerCase(),
                        wordList = this.wordsFromFiles[startLetter], result = [];
                    for (var word in wordList) {
                        if (word.lastIndexOf(prefix, 0) !== 0) continue;
                        result.push({
                            name: word,
                            value: word,
                            score: wordList[word],
                            meta: "lively"
                        });
                    }
                    callback(null, result);
                }
            });
            // 2) register completer
            var langTools = lively.ide.ace.require('ace/ext/language_tools');
            langTools.addCompleter(lively.ide.WordCompleter);
            next();
        }

        function done(next) { alertOK('Word completion installed!'); next(); }
    }
});

(function setupCompletionsOnLoad() {
    (function() {
        if (UserAgent.isNodejs
         || UserAgent.isWorker
         || !lively.Config.get('computeCodeEditorCompletionsOnStartup'))
         return;
        lively.ide.codeeditor.Completions.setupCompletions();
    }).delay(3);
})();

}) // end of module
<|endoftext|>


<commit_before>/**
 * @file Contain the description of the next classes:
 *

 *
[PageToolbar]{@link PageToolbar}

 *

 *
 * @requires Toolbar
 * @requires ModalBox
 * @requires LayoutManager
 *
 * @author Pavel Dubenko
 *
 * @version 1.0.0
 */

ScriptLoader.load('Toolbar', 'ModalBox', 'LayoutManager');

// TODO: This constructor is more general as the parents constructor, or something is wrong with parent constructor and/or parent class.
/**
 * PageToolbar
 *
 * @augments Toolbar
 *
 * @constructor
 * @param componentPath
 * @param documentId
 * @param {string} toolbarName Toolbar name.
 * @param controlsDesc
 */
var PageToolbar = new Class(/** @lends PageToolbar# */{
    Extends: Toolbar,

    // constructor
    initialize: function(componentPath, documentId, toolbarName, controlsDesc) {
        this.parent(toolbarName);

        // todo: Strange, to override parent CSS.
        Asset.css('pagetoolbar.css');

        /**
         *
         * @type {*}
         */
        this.componentPath = componentPath;

        /**
         *
         * @type {*}
         */
        this.documentId = documentId;

        /**
         *
         * @type {boolean}
         */
        this.layoutManager = false;

        this.dock();
        this.bindTo(this);
        if (controlsDesc) {
            controlsDesc.each(function(controlDesc) {
                this.appendControl(controlDesc);
            }, this);
        }

        this.setupLayout();
    },

    /**
     * Setup the layout.
     * @function
     * @public
     */
    setupLayout: function() {
        var html = $$('html')[0];
        if (!html.hasClass('e-has-topframe1')) {
            html.addClass('e-has-topframe1');
        }
        if (/*(Cookie.read('sidebar') == null) || */(Cookie.read('sidebar') == 1)) {
            $$('html')[0].addClass('e-has-sideframe');
        }

        var currentBody = $(document.body).getChildren().filter(function(element) {
                return !(element.hasClass('e-overlay'));
            }),
            mainFrame = new Element('div', {'class': 'e-mainframe'}),
            topFrame = new Element('div', {'class':'e-topframe'}),
            sidebarFrame = new Element('div', {'class':'e-sideframe'}),
            sidebarFrameContent = new Element('div', {'class':'e-sideframe-content'}),
            sidebarFrameBorder = new Element('div', {'class':'e-sideframe-border'});

        $(document.body).adopt([topFrame, mainFrame, sidebarFrame]);
        mainFrame.adopt(currentBody);
        sidebarFrame.adopt([sidebarFrameContent, sidebarFrameBorder]);
        topFrame.grab(this.element);

        new Element('iframe').setProperties({
            'src': this.componentPath + 'show/'/* + this.documentId + '/'*/,
            'frameBorder': '0'
        }).inject(sidebarFrameContent);

        var editBlocksButton = this.getControlById('editBlocks');
        if(this.getControlById('editMode').getState() && editBlocksButton) {
            editBlocksButton.disable();
        }

    },

    // Actions:
    /**
     * Edit mode action.
     * @function
     * @public
     */
    editMode: function() {
        if (this.getControlById('editMode')
            && this.getControlById('editMode').getState() == 0)
        {
            this._reloadWindowInEditMode();
        } else {
            //todo: What is it?
            window.location = window.location;
        }
    },

    /**
     * Add action.
     * @function
     * @public
     */
    add: function() {
        ModalBox.open({ 'url': this.componentPath + 'add/' + this.documentId });
    },

    /**
     * Edit action.
     * @function
     * @public
     */
    edit: function() {
        ModalBox.open({ 'url': this.componentPath + this.documentId +
            '/edit' });
    },

    /**
     * Toggle sidebar.
     * @function
     * @public
     */
    toggleSidebar: function() {
        $$('html')[0].toggleClass('e-has-sideframe');
        var url = new URI(Energine.base),
            domainChunks = url.get('host').split('.'),
            domain;

        if(domainChunks.length > 2){
            domainChunks.shift();
        }

        domain = '.' + domainChunks.join('.');
        Cookie.write('sidebar',
            $$('html')[0].hasClass('e-has-sideframe') ? 1 : 0,
            {
                domain: domain,
                path: url.get('directory'),
                duration: 30
            });
    },

    /**
     * Show template editor.
     * @function
     * @public
     */
    showTmplEditor: function() {
        ModalBox.open({ 'url': this.componentPath + 'template' });
    },

    /**
     * Show translation editor.
     * @function
     * @public
     */
    showTransEditor: function() {
        ModalBox.open({ 'url': this.componentPath + 'translation' });
    },

    /**
     * Show user editor.
     * @function
     * @public
     */
    showUserEditor: function() {
        ModalBox.open({ 'url': this.componentPath + 'user' });
    },

    /**
     * Show role editor.
     * @function
     * @public
     */
    showRoleEditor: function() {
        ModalBox.open({ 'url': this.componentPath + 'role' });
    },

    /**
     * Show language editor.
     * @function
     * @public
     */
    showLangEditor: function() {
        ModalBox.open({ 'url': this.componentPath + 'languages' });
    },

    /**
     * Show file repository.
     * @function
     * @public
     */
    showFileRepository: function() {
        ModalBox.open({ 'url': this.componentPath + 'file-library' });
    },

    /**
     * Show site editor.
     * @function
     * @public
     */
    showSiteEditor: function() {
        ModalBox.open({ 'url': this.componentPath + 'sites' });
    },

    /**
     * Edit blocks.
     * @function
     * @public
     */
    editBlocks: function() {
        if (!this.getControlById('editBlocks').getState()) {
            /**
             * Layout manager.
             * @type {LayoutManager}
             */
            this.layoutManager = new LayoutManager(this.componentPath);
        } else {
            if (this.layoutManager && LayoutManager.changed){
                if(!confirm('The page has unsaved changes. Are you sure you want to quit and lost all changes?'))   {
                    return;
                }
            }

            document.location = document.location.href;
        }
    },

    //todo: Why not to inject this to the editMode() method?
    /**
     * Reload window in the edit mode.
     * @function
     * @private
     */
    _reloadWindowInEditMode: function() {
        new Element('form', {styles: {display: 'none'}})
            .setProperties({
                action: '',
                method: 'post'
            })
            .grab(new Element('input')
                .setProperty('name', 'editMode')
                .setProperties({
                    type: 'hidden',
                    value: '1'
                }))
            .inject(document.body).submit();
    }
});
<commit_msg>если наличествует svg елементы<commit_after>/**
 * @file Contain the description of the next classes:
 *

 *
[PageToolbar]{@link PageToolbar}

 *

 *
 * @requires Toolbar
 * @requires ModalBox
 * @requires LayoutManager
 *
 * @author Pavel Dubenko
 *
 * @version 1.0.0
 */

ScriptLoader.load('Toolbar', 'ModalBox', 'LayoutManager');

// TODO: This constructor is more general as the parents constructor, or something is wrong with parent constructor and/or parent class.
/**
 * PageToolbar
 *
 * @augments Toolbar
 *
 * @constructor
 * @param componentPath
 * @param documentId
 * @param {string} toolbarName Toolbar name.
 * @param controlsDesc
 */
var PageToolbar = new Class(/** @lends PageToolbar# */{
    Extends: Toolbar,

    // constructor
    initialize: function(componentPath, documentId, toolbarName, controlsDesc) {
        this.parent(toolbarName);

        // todo: Strange, to override parent CSS.
        Asset.css('pagetoolbar.css');

        /**
         *
         * @type {*}
         */
        this.componentPath = componentPath;

        /**
         *
         * @type {*}
         */
        this.documentId = documentId;

        /**
         *
         * @type {boolean}
         */
        this.layoutManager = false;

        this.dock();
        this.bindTo(this);
        if (controlsDesc) {
            controlsDesc.each(function(controlDesc) {
                this.appendControl(controlDesc);
            }, this);
        }

        this.setupLayout();
    },

    /**
     * Setup the layout.
     * @function
     * @public
     */
    setupLayout: function() {
        var html = $$('html')[0];
        if (!html.hasClass('e-has-topframe1')) {
            html.addClass('e-has-topframe1');
        }
        if (/*(Cookie.read('sidebar') == null) || */(Cookie.read('sidebar') == 1)) {
            $$('html')[0].addClass('e-has-sideframe');
        }

        var currentBody = $(document.body).getChildren().filter(function(element) {
                return !((element.get('tag') !== 'svg') && element.hasClass('e-overlay'));
            }),
            mainFrame = new Element('div', {'class': 'e-mainframe'}),
            topFrame = new Element('div', {'class':'e-topframe'}),
            sidebarFrame = new Element('div', {'class':'e-sideframe'}),
            sidebarFrameContent = new Element('div', {'class':'e-sideframe-content'}),
            sidebarFrameBorder = new Element('div', {'class':'e-sideframe-border'});

        $(document.body).adopt([topFrame, mainFrame, sidebarFrame]);
        mainFrame.adopt(currentBody);
        sidebarFrame.adopt([sidebarFrameContent, sidebarFrameBorder]);
        topFrame.grab(this.element);

        new Element('iframe').setProperties({
            'src': this.componentPath + 'show/'/* + this.documentId + '/'*/,
            'frameBorder': '0'
        }).inject(sidebarFrameContent);

        var editBlocksButton = this.getControlById('editBlocks');
        if(this.getControlById('editMode').getState() && editBlocksButton) {
            editBlocksButton.disable();
        }

    },

    // Actions:
    /**
     * Edit mode action.
     * @function
     * @public
     */
    editMode: function() {
        if (this.getControlById('editMode')
            && this.getControlById('editMode').getState() == 0)
        {
            this._reloadWindowInEditMode();
        } else {
            //todo: What is it?
            window.location = window.location;
        }
    },

    /**
     * Add action.
     * @function
     * @public
     */
    add: function() {
        ModalBox.open({ 'url': this.componentPath + 'add/' + this.documentId });
    },

    /**
     * Edit action.
     * @function
     * @public
     */
    edit: function() {
        ModalBox.open({ 'url': this.componentPath + this.documentId +
            '/edit' });
    },

    /**
     * Toggle sidebar.
     * @function
     * @public
     */
    toggleSidebar: function() {
        $$('html')[0].toggleClass('e-has-sideframe');
        var url = new URI(Energine.base),
            domainChunks = url.get('host').split('.'),
            domain;

        if(domainChunks.length > 2){
            domainChunks.shift();
        }

        domain = '.' + domainChunks.join('.');
        Cookie.write('sidebar',
            $$('html')[0].hasClass('e-has-sideframe') ? 1 : 0,
            {
                domain: domain,
                path: url.get('directory'),
                duration: 30
            });
    },

    /**
     * Show template editor.
     * @function
     * @public
     */
    showTmplEditor: function() {
        ModalBox.open({ 'url': this.componentPath + 'template' });
    },

    /**
     * Show translation editor.
     * @function
     * @public
     */
    showTransEditor: function() {
        ModalBox.open({ 'url': this.componentPath + 'translation' });
    },

    /**
     * Show user editor.
     * @function
     * @public
     */
    showUserEditor: function() {
        ModalBox.open({ 'url': this.componentPath + 'user' });
    },

    /**
     * Show role editor.
     * @function
     * @public
     */
    showRoleEditor: function() {
        ModalBox.open({ 'url': this.componentPath + 'role' });
    },

    /**
     * Show language editor.
     * @function
     * @public
     */
    showLangEditor: function() {
        ModalBox.open({ 'url': this.componentPath + 'languages' });
    },

    /**
     * Show file repository.
     * @function
     * @public
     */
    showFileRepository: function() {
        ModalBox.open({ 'url': this.componentPath + 'file-library' });
    },

    /**
     * Show site editor.
     * @function
     * @public
     */
    showSiteEditor: function() {
        ModalBox.open({ 'url': this.componentPath + 'sites' });
    },

    /**
     * Edit blocks.
     * @function
     * @public
     */
    editBlocks: function() {
        if (!this.getControlById('editBlocks').getState()) {
            /**
             * Layout manager.
             * @type {LayoutManager}
             */
            this.layoutManager = new LayoutManager(this.componentPath);
        } else {
            if (this.layoutManager && LayoutManager.changed){
                if(!confirm('The page has unsaved changes. Are you sure you want to quit and lost all changes?'))   {
                    return;
                }
            }

            document.location = document.location.href;
        }
    },

    //todo: Why not to inject this to the editMode() method?
    /**
     * Reload window in the edit mode.
     * @function
     * @private
     */
    _reloadWindowInEditMode: function() {
        new Element('form', {styles: {display: 'none'}})
            .setProperties({
                action: '',
                method: 'post'
            })
            .grab(new Element('input')
                .setProperty('name', 'editMode')
                .setProperties({
                    type: 'hidden',
                    value: '1'
                }))
            .inject(document.body).submit();
    }
});
<|endoftext|>


<commit_before>var COMMCAREHQ = {};

COMMCAREHQ.icons = {
    GRIP:   'icon-resize-vertical icon-blue fa fa-arrows-v',
    ADD:    'icon-plus icon-blue fa fa-plus',
    COPY:   'icon-copy icon-blue fa fa-copy',
    DELETE: 'icon-remove icon-blue fa fa-remove',
    PAPERCLIP: 'icon-paper-clip fa fa-paperclip'
};

var eventize = function (that) {
    'use strict';
    var events = {};
    that.on = function (tag, callback) {
        if (events[tag] === undefined) {
            events[tag] = [];
        }
        events[tag].push(callback);
        return that;
    };
    that.fire = function (tag, e) {
        var i;
        if (events[tag] !== undefined) {
            for (i = 0; i < events[tag].length; i += 1) {
                events[tag][i].apply(that, [e]);
            }
        }
        return that;
    };
    return that;
};

COMMCAREHQ.makeHqHelp = function (opts, wrap) {
    'use strict';
    wrap = wrap === undefined ? true : wrap;
    var el = $(
        '
' +
            '' +
                '
'
    );
    _.each(['content', 'title', 'html', 'placement'], function(attr) {
        $('a', el).data(attr, opts[attr]);
    });
    if (wrap) {
        el.hqHelp();
    }
    return el;
};

COMMCAREHQ.transformHelpTemplate = function ($template, wrap) {
    'use strict';
    if ($template.data()) {
        var $help = COMMCAREHQ.makeHqHelp($template.data(), wrap);
        $help.insertAfter($template);
        $template.remove();
    }
};

COMMCAREHQ.initBlock = function ($elem) {
    'use strict';
    $('.submit_on_click', $elem).on("click", function (e) {
        e.preventDefault();
        if (!$(this).data('clicked')) {
            $(this).prev('form').submit();
            $(this).data('clicked', 'true').children('i').removeClass().addClass("icon-refresh icon-spin fa fa-refresh fa-spin");
        }
    });

    $('.submit').click(function (e) {
        var $form = $(this).closest('.form, form'),
            data = $form.my_serialize(),
            action = $form.attr('action') || $form.data('action');

        e.preventDefault();
        $.postGo(action, $.unparam(data));
    });
    $('.post-link').click(function (e) {
        e.preventDefault();
        $.postGo($(this).attr('href'), {});
    });

    $(".button", $elem).button().wrap('');
    $("input[type='submit']", $elem).button();
    $("input[type='text'], input[type='password'], textarea", $elem);
    $('.container', $elem).addClass('ui-widget ui-widget-content');
    $('.config', $elem).wrap('
').parent().addClass('container block ui-corner-all');
};

COMMCAREHQ.updateDOM = function (update) {
    'use strict';
    var key;
    for (key in update) {
        if (update.hasOwnProperty(key)) {
            $(key).text(update[key]).val(update[key]);
        }
    }
};

COMMCAREHQ.makeSaveButton = function(messageStrings, cssClass) {
    'use strict';
    var SaveButton = {
        /*
         options: {
         save: "Function to call when the user clicks Save",
         unsavedMessage: "Message to display when there are unsaved changes and the user leaves the page"
         }
         */
        init: function (options) {
            var button = {
                $save: $('
').text(SaveButton.message.SAVE).click(function () {
                    button.fire('save');
                }).addClass(cssClass),
                $retry: $('
').text(SaveButton.message.RETRY).click(function () {
                    button.fire('save');
                }).addClass(cssClass),
                $saving: $('
').text(SaveButton.message.SAVING).addClass('btn btn-default disabled'),
                $saved: $('
').text(SaveButton.message.SAVED).addClass('btn btn-default disabled'),
                ui: $('
').addClass('pull-right'),
                setStateWhenReady: function (state) {
                    if (this.state === 'saving') {
                        this.nextState = state;
                    } else {
                        this.setState(state);
                    }
                },
                setState: function (state) {
                    if (this.state === state) {
                        return;
                    }
                    this.state = state;
                    this.$save.detach();
                    this.$saving.detach();
                    this.$saved.detach();
                    this.$retry.detach();
                    if (state === 'save') {
                        this.ui.append(this.$save);
                    } else if (state === 'saving') {
                        this.ui.append(this.$saving);
                    } else if (state === 'saved') {
                        this.ui.append(this.$saved);
                    } else if (state === 'retry') {
                        this.ui.append(this.$retry);
                    }
                },
                ajax: function (options) {
                    var beforeSend = options.beforeSend || function () {},
                        success = options.success || function () {},
                        error = options.error || function () {},
                        that = this;
                    options.beforeSend = function (jqXHR, settings) {
                        that.setState('saving');
                        that.nextState = 'saved';
                        $.ajaxSettings.beforeSend(jqXHR, settings);
                        beforeSend.apply(this, arguments);
                    };
                    options.success = function (data) {
                        that.setState(that.nextState);
                        success.apply(this, arguments);
                    };
                    options.error = function (data) {
                        that.nextState = null;
                        that.setState('retry');
                        var customError = ((data.responseJSON && data.responseJSON.message) ? data.responseJSON.message : data.responseText);
                        if (customError.indexOf('') > -1) {
                            // this is sending back a full html page, likely login, so no error message.
                            customError = null;
                        }
                        alert_user(customError || SaveButton.message.ERROR_SAVING, 'danger');
                        error.apply(this, arguments);
                    };
                    var jqXHR = $.ajax(options);
                    if (!jqXHR) {
                        // request was aborted
                        that.setState('save');
                    }
                }
            };
            eventize(button);
            button.setState('saved');
            button.on('change', function () {
                this.setStateWhenReady('save');
            });
            if (options.save) {
                button.on('save', options.save);
            }
            $(window).bind('beforeunload', function () {
                var stillAttached = button.ui.parents()[button.ui.parents().length - 1].tagName.toLowerCase() == 'html';
                if (button.state !== 'saved' && stillAttached) {
                    return options.unsavedMessage || "";
                }
            });
            return button;
        },
        initForm: function ($form, options) {
            var url = $form.attr('action'),
                button = SaveButton.init({
                    unsavedMessage: options.unsavedMessage,
                    save: function () {
                        this.ajax({
                            url: url,
                            type: 'POST',
                            dataType: 'json',
                            data: $form.serialize(),
                            success: options.success
                        });
                    }
                }),
                fireChange = function () {
                    button.fire('change');
                };
            _.defer(function () {
                $form.find('*').change(fireChange);
                $form.find('input, textarea').bind('textchange', fireChange);
            });
            return button;
        },
        message: messageStrings
    };

    return SaveButton;
};

COMMCAREHQ.SaveButton = COMMCAREHQ.makeSaveButton({
    SAVE: django.gettext("Save"),
    SAVING: django.gettext("Saving..."),
    SAVED: django.gettext("Saved"),
    RETRY: django.gettext("Try Again"),
    ERROR_SAVING: django.gettext("There was an error saving")
}, 'btn btn-success');

COMMCAREHQ.DeleteButton = COMMCAREHQ.makeSaveButton({
    SAVE: django.gettext("Delete"),
    SAVING: django.gettext("Deleting..."),
    SAVED: django.gettext("Deleted"),
    RETRY: django.gettext("Try Again"),
    ERROR_SAVING: django.gettext("There was an error deleting")
}, 'btn btn-danger');

COMMCAREHQ.beforeUnload = [];

COMMCAREHQ.bindBeforeUnload = function (callback) {
    COMMCAREHQ.beforeUnload.push(callback);
};

COMMCAREHQ.beforeUnloadCallback = function () {
    for (var i = 0; i < COMMCAREHQ.beforeUnload.length; i++) {
        var message = COMMCAREHQ.beforeUnload[i]();
        if (message !== null && message !== undefined) {
            return message;
        }
    }
};

$(function () {
    'use strict';
    COMMCAREHQ.initBlock($("body"));
    $(window).bind('beforeunload', COMMCAREHQ.beforeUnloadCallback);
});

COMMCAREHQ.toggleEnabled = hqImport('hqwebapp/js/toggles.js').toggleEnabled;
COMMCAREHQ.previewEnabled = hqImport('hqwebapp/js/toggles.js').previewEnabled;
<commit_msg>Stop undefined error on page unload<commit_after>var COMMCAREHQ = {};

COMMCAREHQ.icons = {
    GRIP:   'icon-resize-vertical icon-blue fa fa-arrows-v',
    ADD:    'icon-plus icon-blue fa fa-plus',
    COPY:   'icon-copy icon-blue fa fa-copy',
    DELETE: 'icon-remove icon-blue fa fa-remove',
    PAPERCLIP: 'icon-paper-clip fa fa-paperclip'
};

var eventize = function (that) {
    'use strict';
    var events = {};
    that.on = function (tag, callback) {
        if (events[tag] === undefined) {
            events[tag] = [];
        }
        events[tag].push(callback);
        return that;
    };
    that.fire = function (tag, e) {
        var i;
        if (events[tag] !== undefined) {
            for (i = 0; i < events[tag].length; i += 1) {
                events[tag][i].apply(that, [e]);
            }
        }
        return that;
    };
    return that;
};

COMMCAREHQ.makeHqHelp = function (opts, wrap) {
    'use strict';
    wrap = wrap === undefined ? true : wrap;
    var el = $(
        '
' +
            '' +
                '
'
    );
    _.each(['content', 'title', 'html', 'placement'], function(attr) {
        $('a', el).data(attr, opts[attr]);
    });
    if (wrap) {
        el.hqHelp();
    }
    return el;
};

COMMCAREHQ.transformHelpTemplate = function ($template, wrap) {
    'use strict';
    if ($template.data()) {
        var $help = COMMCAREHQ.makeHqHelp($template.data(), wrap);
        $help.insertAfter($template);
        $template.remove();
    }
};

COMMCAREHQ.initBlock = function ($elem) {
    'use strict';
    $('.submit_on_click', $elem).on("click", function (e) {
        e.preventDefault();
        if (!$(this).data('clicked')) {
            $(this).prev('form').submit();
            $(this).data('clicked', 'true').children('i').removeClass().addClass("icon-refresh icon-spin fa fa-refresh fa-spin");
        }
    });

    $('.submit').click(function (e) {
        var $form = $(this).closest('.form, form'),
            data = $form.my_serialize(),
            action = $form.attr('action') || $form.data('action');

        e.preventDefault();
        $.postGo(action, $.unparam(data));
    });
    $('.post-link').click(function (e) {
        e.preventDefault();
        $.postGo($(this).attr('href'), {});
    });

    $(".button", $elem).button().wrap('');
    $("input[type='submit']", $elem).button();
    $("input[type='text'], input[type='password'], textarea", $elem);
    $('.container', $elem).addClass('ui-widget ui-widget-content');
    $('.config', $elem).wrap('
').parent().addClass('container block ui-corner-all');
};

COMMCAREHQ.updateDOM = function (update) {
    'use strict';
    var key;
    for (key in update) {
        if (update.hasOwnProperty(key)) {
            $(key).text(update[key]).val(update[key]);
        }
    }
};

COMMCAREHQ.makeSaveButton = function(messageStrings, cssClass) {
    'use strict';
    var SaveButton = {
        /*
         options: {
         save: "Function to call when the user clicks Save",
         unsavedMessage: "Message to display when there are unsaved changes and the user leaves the page"
         }
         */
        init: function (options) {
            var button = {
                $save: $('
').text(SaveButton.message.SAVE).click(function () {
                    button.fire('save');
                }).addClass(cssClass),
                $retry: $('
').text(SaveButton.message.RETRY).click(function () {
                    button.fire('save');
                }).addClass(cssClass),
                $saving: $('
').text(SaveButton.message.SAVING).addClass('btn btn-default disabled'),
                $saved: $('
').text(SaveButton.message.SAVED).addClass('btn btn-default disabled'),
                ui: $('
').addClass('pull-right'),
                setStateWhenReady: function (state) {
                    if (this.state === 'saving') {
                        this.nextState = state;
                    } else {
                        this.setState(state);
                    }
                },
                setState: function (state) {
                    if (this.state === state) {
                        return;
                    }
                    this.state = state;
                    this.$save.detach();
                    this.$saving.detach();
                    this.$saved.detach();
                    this.$retry.detach();
                    if (state === 'save') {
                        this.ui.append(this.$save);
                    } else if (state === 'saving') {
                        this.ui.append(this.$saving);
                    } else if (state === 'saved') {
                        this.ui.append(this.$saved);
                    } else if (state === 'retry') {
                        this.ui.append(this.$retry);
                    }
                },
                ajax: function (options) {
                    var beforeSend = options.beforeSend || function () {},
                        success = options.success || function () {},
                        error = options.error || function () {},
                        that = this;
                    options.beforeSend = function (jqXHR, settings) {
                        that.setState('saving');
                        that.nextState = 'saved';
                        $.ajaxSettings.beforeSend(jqXHR, settings);
                        beforeSend.apply(this, arguments);
                    };
                    options.success = function (data) {
                        that.setState(that.nextState);
                        success.apply(this, arguments);
                    };
                    options.error = function (data) {
                        that.nextState = null;
                        that.setState('retry');
                        var customError = ((data.responseJSON && data.responseJSON.message) ? data.responseJSON.message : data.responseText);
                        if (customError.indexOf('') > -1) {
                            // this is sending back a full html page, likely login, so no error message.
                            customError = null;
                        }
                        alert_user(customError || SaveButton.message.ERROR_SAVING, 'danger');
                        error.apply(this, arguments);
                    };
                    var jqXHR = $.ajax(options);
                    if (!jqXHR) {
                        // request was aborted
                        that.setState('save');
                    }
                }
            };
            eventize(button);
            button.setState('saved');
            button.on('change', function () {
                this.setStateWhenReady('save');
            });
            if (options.save) {
                button.on('save', options.save);
            }
            $(window).bind('beforeunload', function () {
                var lastParent = button.ui.parents()[button.ui.parents().length - 1];
                if (lastParent) {
                    var stillAttached = lastParent.tagName.toLowerCase() == 'html';
                    if (button.state !== 'saved' && stillAttached) {
                        return options.unsavedMessage || "";
                    }
                }
            });
            return button;
        },
        initForm: function ($form, options) {
            var url = $form.attr('action'),
                button = SaveButton.init({
                    unsavedMessage: options.unsavedMessage,
                    save: function () {
                        this.ajax({
                            url: url,
                            type: 'POST',
                            dataType: 'json',
                            data: $form.serialize(),
                            success: options.success
                        });
                    }
                }),
                fireChange = function () {
                    button.fire('change');
                };
            _.defer(function () {
                $form.find('*').change(fireChange);
                $form.find('input, textarea').bind('textchange', fireChange);
            });
            return button;
        },
        message: messageStrings
    };

    return SaveButton;
};

COMMCAREHQ.SaveButton = COMMCAREHQ.makeSaveButton({
    SAVE: django.gettext("Save"),
    SAVING: django.gettext("Saving..."),
    SAVED: django.gettext("Saved"),
    RETRY: django.gettext("Try Again"),
    ERROR_SAVING: django.gettext("There was an error saving")
}, 'btn btn-success');

COMMCAREHQ.DeleteButton = COMMCAREHQ.makeSaveButton({
    SAVE: django.gettext("Delete"),
    SAVING: django.gettext("Deleting..."),
    SAVED: django.gettext("Deleted"),
    RETRY: django.gettext("Try Again"),
    ERROR_SAVING: django.gettext("There was an error deleting")
}, 'btn btn-danger');

COMMCAREHQ.beforeUnload = [];

COMMCAREHQ.bindBeforeUnload = function (callback) {
    COMMCAREHQ.beforeUnload.push(callback);
};

COMMCAREHQ.beforeUnloadCallback = function () {
    for (var i = 0; i < COMMCAREHQ.beforeUnload.length; i++) {
        var message = COMMCAREHQ.beforeUnload[i]();
        if (message !== null && message !== undefined) {
            return message;
        }
    }
};

$(function () {
    'use strict';
    COMMCAREHQ.initBlock($("body"));
    $(window).bind('beforeunload', COMMCAREHQ.beforeUnloadCallback);
});

COMMCAREHQ.toggleEnabled = hqImport('hqwebapp/js/toggles.js').toggleEnabled;
COMMCAREHQ.previewEnabled = hqImport('hqwebapp/js/toggles.js').previewEnabled;
<|endoftext|>


<commit_before>/*global define*/

define(['jquery', 'underscore', 'backbone', 'templates', 'helpers/gauge-helper', 'kinetic', 'humanize', 'marionette'], function($, _, Backbone, JST, gaugeHelper, Kinetic, humanize) {
    'use strict';

    var PgmapView = Backbone.Marionette.ItemView.extend({
        className: 'col-lg-9 col-md-9 col-sm-12 col-xs-12 custom-gutter',
        template: JST['app/scripts/templates/pgmap.ejs'],
        headlineTemplate: _.template('<%- active %>/<%- total %>'),
        subtextTemplate: _.template('<%- value %> <%- key %>'),
        ui: {
            container: '.pgcanvas',
            headline: '.headline',
            subtext: '.subtext'
        },
        initialize: function() {
            _.bindAll(this);
            this.App = Backbone.Marionette.getOption(this, 'App');
            this.listenToOnce(this, 'show', this.postRender);
            this.listenTo(this, 'renderMap', this.renderMap);
            this.collection = new Backbone.Collection();
            if (this.App) {
                this.ReqRes = Backbone.Marionette.getOption(this.App, 'ReqRes');
                this.listenTo(this.App.vent, 'filter:update', this.fetchOSDPGCount);
                this.listenTo(this.App.vent, 'status:update', this.statusUpdate);
            }
            gaugeHelper(this);
        },
        count: 15000,
        statusUpdate: function(model) {
            this.total = _.reduce(model.get('pg'), function(memo, obj) {
                return memo + obj.count;
            }, 0);
            this.ui.headline.text(this.headlineTemplate({
                active: this.format(model.get('pg').ok.count),
                total: this.format(this.total)
            }));
        },
        getLayout: function(count) {
            var width = 400,
                height = 250,
                x = 25,
                y = 10,
                scale = 1;
            if (count <= 15000) {
                width = 155;
                height = 95;
                scale = 2.58;
            } else if (count <= 30000) {
                width = 220;
                height = 138;
                scale = 1.8;
            } else if (count <= 60000) {
                width = 310;
                height = 194;
                scale = 1.29;
            }
            x = x / scale;
            y = y / scale;
            return {
                x: Math.round(x),
                y: Math.round(y),
                width: width,
                height: height,
                scale: scale
            };
        },
        legendColors: [
            ['#0f0', 0.305],
            ['#ff0', 1],
            ['#f00', 1]
        ],
        legendText: [
                'Clean',
                'Working',
                'Dirty'
        ],
        makeText: function(index, x, y) {
            var t = new Kinetic.Text({
                fontFamily: 'ApexSansMedium',
                fontSize: '12',
                text: this.legendText[index],
                fill: '#37424a',
                x: x,
                y: y + 3
            });
            x += t.getWidth() + 10;
            return [t, x];
        },
        makeSwatch: function(index, x, y) {
            var o = [];
            var boxWidth = 16;
            var border = new Kinetic.Rect({
                x: x,
                y: y,
                width: boxWidth + 2,
                height: boxWidth + 2,
                stroke: '#5e6a71'
            });
            o.push(border);
            o.push(new Kinetic.Rect({
                x: x + 1,
                y: y + 1,
                width: boxWidth,
                height: boxWidth,
                fill: _.first(this.legendColors[index]),
                opacity: _.last(this.legendColors[index])
            }));
            x += border.getWidth() + 4;
            return [o, x];
        },
        makeLegend: function() {
            var x = 230,
                y = 229;
            var self = this;
            var legend = _.reduce(_.range(0, 3), function(memo, i) {
                var swatch = self.makeSwatch(i, x, y);
                memo = memo.concat(_.first(swatch));
                x = _.last(swatch);
                var text = self.makeText(i, x, y);
                x = _.last(text);
                memo.push(_.first(text));
                return memo;
            }, []);
            return _.flatten(legend);
        },
        postRender: function() {
            var layout = this.getLayout(this.count);
            var width = this.ui.container.width();
            var height = this.ui.container.height();
            this.stage = new Kinetic.Stage({
                container: this.ui.container[0],
                height: height,
                width: width
            });

            this.layer = new Kinetic.Layer();
            this.tlayer = new Kinetic.Layer();

            this.background = new Kinetic.Rect({
                x: layout.x,
                y: layout.y,
                width: layout.width,
                height: layout.height,
                fill: '#e6e8e8'
            });

            this.layer.add(this.background);

            var legend = this.makeLegend();
            (function(tlayer) {
                _.each(legend, function(o) {
                    tlayer.add(o);
                });
            })(this.tlayer);

            this.stage.add(this.layer);
            this.stage.add(this.tlayer);
            this.layer.getContext().scale(layout.scale, layout.scale);
            this.layer.drawScene();

            this.frag = document.createDocumentFragment();
            this.backstage = new Kinetic.Stage({
                container: this.frag,
                height: height,
                width: width
            });

            this._background = new Kinetic.Rect({
                x: layout.x,
                y: layout.y,
                width: layout.width,
                height: layout.height,
                fill: '#e6e8e8'
            });
            this._layer = new Kinetic.Layer();
            this._layer.add(this._background);
            this.backstage.add(this._layer);
            this._layer.drawScene();

        },
        countPGs: function() {
            return this.collection.reduce(function(memo, m) {
                var pgs = m.get('pg_states');
                return memo + _.reduce(pgs, function(memo, v, k) {
                    if (k === 'active') {
                        return memo;
                    }
                    return memo + v;
                }, 0);
            }, 0);
        },
        fetchOSDPGCount: function() {
            var self = this;
            setTimeout(function() {
                self.collection.set(self.ReqRes.request('get:osdpgcounts'));
                var count = self.countPGs();
                if (count !== self.count) {
                    self.count = count;
                    self.stage.destroy();
                    self.backstage.destroy();
                    self.postRender();
                }
                self.trigger('renderMap');
            }, 0);
        },
        countAttributes: function(attr, list) {
            return _.reduce(list, function(memo, key) {
                var value = attr[key];
                return memo + (_.isNumber(value) ? value : 0);
            }, 0);
        },
        setPixel: function(imageData, x, y, r, g, b, a) {
            var index = (x + y * imageData.width) * 4;
            imageData.data[index + 0] = r;
            imageData.data[index + 1] = g;
            imageData.data[index + 2] = b;
            imageData.data[index + 3] = a;
        },
        total: 0,
        activeclean: 0,
        format: function(v) {
            return humanize.filesize(v, 1000, 1).replace(' ', '').replace('b', '').toLowerCase();
        },
        priorityOrder: [
                'stale',
                'incomplete',
                'peering',
                'down',
                'inconsistent',
                'remapped',
                'wait-backfill',
                'backfill',
                'recovering',
                'repair',
                'degraded',
                'scrubbing',
                'splitting',
                'replaying',
                'creating'
        ],
        redStates: {
            'stale': true,
            'incomplete': true,
            'peering': true,
            'down': true,
            'inconsistent': true
        },
        displayWarnings: function(found) {
            if (found in this.redStates) {
                this.trigger('status:warn');
            } else {
                this.trigger('status:ok');
            }
        },
        renderMap: function() {
            var self = this;
            this.pgReplicaTotal = 0;
            this.activeclean = 0;
            var r, g, b, a, y = 0;
            var l = this.getLayout(this.count);
            var ctx = this._background.getContext();
            ctx.clear();
            var imageData = this._background.getContext().getImageData(l.x, l.y, l.width, l.height);
            this.stats = {};
            this.collection.each(function(m) {
                var pgStates = m.get('pg_states');
                self.pgReplicaTotal += _.reduce(pgStates, function(memo, value, key) {
                    if (self.stats[key] === undefined) {
                        self.stats[key] = 0;
                    }
                    self.stats[key] += value;

                    if (key === 'active') {
                        return memo;
                    }
                    if (key === 'clean') {
                        self.activeclean += value;
                        r = 0;
                        g = 255;
                        b = 0;
                        a = 78;
                    } else if (key in self.redStates) {
                        r = 255;
                        g = 0;
                        b = 0;
                        a = 255;
                    } else {
                        r = 255;
                        g = 255;
                        b = 0;
                        a = 255;
                    }
                    var x = memo,
                        xo = memo;
                    for (; x < xo + value; x++) {
                        self.setPixel(imageData, x + self.pgReplicaTotal, y, r, g, b, a); // 255 opaque
                    }
                    return xo + value;
                }, 0);
            });
            var fctx = this.background.getContext();
            fctx.clear();
            ctx.putImageData(imageData, 0, 0);
            fctx.drawImage(this._background.getCanvas()._canvas, l.x, l.y);
            var found = _.find(this.priorityOrder, function(key) {
                return self.stats[key] > 0;
            });
            if (found) {
                this.ui.subtext.text(this.subtextTemplate({
                    key: found,
                    value: this.stats[found]
                }));
                this.displayWarnings(found);
            } else {
                // clear states when there's no interesting states
                // to display
                this.ui.subtext.text('');
                this.displayWarnings('');
            }
        }
    });

    return PgmapView;
});
<commit_msg>Delay canvas rendering to after osd map update<commit_after>/*global define*/

define(['jquery', 'underscore', 'backbone', 'templates', 'helpers/gauge-helper', 'kinetic', 'humanize', 'marionette'], function($, _, Backbone, JST, gaugeHelper, Kinetic, humanize) {
    'use strict';

    var PgmapView = Backbone.Marionette.ItemView.extend({
        className: 'col-lg-9 col-md-9 col-sm-12 col-xs-12 custom-gutter',
        template: JST['app/scripts/templates/pgmap.ejs'],
        headlineTemplate: _.template('<%- active %>/<%- total %>'),
        subtextTemplate: _.template('<%- value %> <%- key %>'),
        ui: {
            container: '.pgcanvas',
            headline: '.headline',
            subtext: '.subtext'
        },
        initialize: function() {
            _.bindAll(this);
            this.App = Backbone.Marionette.getOption(this, 'App');
            this.listenTo(this, 'renderMap', this.renderMap);
            this.collection = new Backbone.Collection();
            if (this.App) {
                this.ReqRes = Backbone.Marionette.getOption(this.App, 'ReqRes');
                this.listenTo(this.App.vent, 'filter:update', this.fetchOSDPGCount);
                this.listenTo(this.App.vent, 'status:update', this.statusUpdate);
            }
            gaugeHelper(this);
        },
        count: 15000,
        statusUpdate: function(model) {
            this.total = _.reduce(model.get('pg'), function(memo, obj) {
                return memo + obj.count;
            }, 0);
            this.ui.headline.text(this.headlineTemplate({
                active: this.format(model.get('pg').ok.count),
                total: this.format(this.total)
            }));
        },
        getLayout: function(count) {
            var width = 400,
                height = 250,
                x = 25,
                y = 10,
                scale = 1;
            if (count <= 15000) {
                width = 155;
                height = 95;
                scale = 2.58;
            } else if (count <= 30000) {
                width = 220;
                height = 138;
                scale = 1.8;
            } else if (count <= 60000) {
                width = 310;
                height = 194;
                scale = 1.29;
            }
            x = x / scale;
            y = y / scale;
            return {
                x: Math.round(x),
                y: Math.round(y),
                width: width,
                height: height,
                scale: scale
            };
        },
        legendColors: [
            ['#0f0', 0.305],
            ['#ff0', 1],
            ['#f00', 1]
        ],
        legendText: [
                'Clean',
                'Working',
                'Dirty'
        ],
        makeText: function(index, x, y) {
            var t = new Kinetic.Text({
                fontFamily: 'ApexSansMedium',
                fontSize: '12',
                text: this.legendText[index],
                fill: '#37424a',
                x: x,
                y: y + 3
            });
            x += t.getWidth() + 10;
            return [t, x];
        },
        makeSwatch: function(index, x, y) {
            var o = [];
            var boxWidth = 16;
            var border = new Kinetic.Rect({
                x: x,
                y: y,
                width: boxWidth + 2,
                height: boxWidth + 2,
                stroke: '#5e6a71'
            });
            o.push(border);
            o.push(new Kinetic.Rect({
                x: x + 1,
                y: y + 1,
                width: boxWidth,
                height: boxWidth,
                fill: _.first(this.legendColors[index]),
                opacity: _.last(this.legendColors[index])
            }));
            x += border.getWidth() + 4;
            return [o, x];
        },
        makeLegend: function() {
            var x = 230,
                y = 229;
            var self = this;
            var legend = _.reduce(_.range(0, 3), function(memo, i) {
                var swatch = self.makeSwatch(i, x, y);
                memo = memo.concat(_.first(swatch));
                x = _.last(swatch);
                var text = self.makeText(i, x, y);
                x = _.last(text);
                memo.push(_.first(text));
                return memo;
            }, []);
            return _.flatten(legend);
        },
        initCanvas: function() {
            var layout = this.getLayout(this.count);
            var width = this.ui.container.width();
            var height = this.ui.container.height();
            this.stage = new Kinetic.Stage({
                container: this.ui.container[0],
                height: height,
                width: width
            });

            this.layer = new Kinetic.Layer();
            this.tlayer = new Kinetic.Layer();

            this.background = new Kinetic.Rect({
                x: layout.x,
                y: layout.y,
                width: layout.width,
                height: layout.height,
                fill: '#e6e8e8'
            });

            this.layer.add(this.background);

            var legend = this.makeLegend();
            (function(tlayer) {
                _.each(legend, function(o) {
                    tlayer.add(o);
                });
            })(this.tlayer);

            this.stage.add(this.layer);
            this.stage.add(this.tlayer);
            this.layer.getContext().scale(layout.scale, layout.scale);
            this.layer.drawScene();

            this.frag = document.createDocumentFragment();
            this.backstage = new Kinetic.Stage({
                container: this.frag,
                height: height,
                width: width
            });

            this._background = new Kinetic.Rect({
                x: layout.x,
                y: layout.y,
                width: layout.width,
                height: layout.height,
                fill: '#e6e8e8'
            });
            this._layer = new Kinetic.Layer();
            this._layer.add(this._background);
            this.backstage.add(this._layer);
            this._layer.drawScene();

        },
        countPGs: function() {
            return this.collection.reduce(function(memo, m) {
                var pgs = m.get('pg_states');
                return memo + _.reduce(pgs, function(memo, v, k) {
                    if (k === 'active') {
                        return memo;
                    }
                    return memo + v;
                }, 0);
            }, 0);
        },
        fetchOSDPGCount: function() {
            var self = this;
            setTimeout(function() {
                self.collection.set(self.ReqRes.request('get:osdpgcounts'));
                var count = self.countPGs();
                if (count !== self.count) {
                    // PG Replica Count changed, re-render the canvas
                    self.count = count;
                    if (self.stage) {
                        self.stage.destroy();
                        self.backstage.destroy();
                    }
                    self.initCanvas();
                }
                self.trigger('renderMap');
            }, 0);
        },
        countAttributes: function(attr, list) {
            return _.reduce(list, function(memo, key) {
                var value = attr[key];
                return memo + (_.isNumber(value) ? value : 0);
            }, 0);
        },
        setPixel: function(imageData, x, y, r, g, b, a) {
            var index = (x + y * imageData.width) * 4;
            imageData.data[index + 0] = r;
            imageData.data[index + 1] = g;
            imageData.data[index + 2] = b;
            imageData.data[index + 3] = a;
        },
        total: 0,
        activeclean: 0,
        format: function(v) {
            return humanize.filesize(v, 1000, 1).replace(' ', '').replace('b', '').toLowerCase();
        },
        priorityOrder: [
                'stale',
                'incomplete',
                'peering',
                'down',
                'inconsistent',
                'remapped',
                'wait-backfill',
                'backfill',
                'recovering',
                'repair',
                'degraded',
                'scrubbing',
                'splitting',
                'replaying',
                'creating'
        ],
        redStates: {
            'stale': true,
            'incomplete': true,
            'peering': true,
            'down': true,
            'inconsistent': true
        },
        displayWarnings: function(found) {
            if (found in this.redStates) {
                this.trigger('status:warn');
            } else {
                this.trigger('status:ok');
            }
        },
        renderMap: function() {
            var self = this;
            this.pgReplicaTotal = 0;
            this.activeclean = 0;
            var r, g, b, a, y = 0;
            var l = this.getLayout(this.count);
            var ctx = this._background.getContext();
            ctx.clear();
            var imageData = this._background.getContext().getImageData(l.x, l.y, l.width, l.height);
            this.stats = {};
            this.collection.each(function(m) {
                var pgStates = m.get('pg_states');
                self.pgReplicaTotal += _.reduce(pgStates, function(memo, value, key) {
                    if (self.stats[key] === undefined) {
                        self.stats[key] = 0;
                    }
                    self.stats[key] += value;

                    if (key === 'active') {
                        return memo;
                    }
                    if (key === 'clean') {
                        self.activeclean += value;
                        r = 0;
                        g = 255;
                        b = 0;
                        a = 78;
                    } else if (key in self.redStates) {
                        r = 255;
                        g = 0;
                        b = 0;
                        a = 255;
                    } else {
                        r = 255;
                        g = 255;
                        b = 0;
                        a = 255;
                    }
                    var x = memo,
                        xo = memo;
                    for (; x < xo + value; x++) {
                        self.setPixel(imageData, x + self.pgReplicaTotal, y, r, g, b, a); // 255 opaque
                    }
                    return xo + value;
                }, 0);
            });
            var fctx = this.background.getContext();
            fctx.clear();
            ctx.putImageData(imageData, 0, 0);
            fctx.drawImage(this._background.getCanvas()._canvas, l.x, l.y);
            var found = _.find(this.priorityOrder, function(key) {
                return self.stats[key] > 0;
            });
            if (found) {
                this.ui.subtext.text(this.subtextTemplate({
                    key: found,
                    value: this.stats[found]
                }));
                this.displayWarnings(found);
            } else {
                // clear states when there's no interesting states
                // to display
                this.ui.subtext.text('');
                this.displayWarnings('');
            }
        }
    });

    return PgmapView;
});
<|endoftext|>