<commit_before>import * as THREE from 'three';
import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { mergeBufferGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
import Stats from 'stats.js';
import { GUI } from 'dat.gui';
import {
	MeshBVH, MeshBVHUniformStruct, FloatVertexAttributeTexture,
	shaderStructs, shaderIntersectFunction, SAH,
} from '../src/index.js';

const params = {
	enableRaytracing: true,
	smoothImageScaling: true,
	resolutionScale: Math.min( 0.5, 1 / window.devicePixelRatio ),
	bounces: 3,
	rayOffsetExponent: - 4,
	accumulate: true,
};

let renderer, camera, scene, gui, stats;
let rtQuad, finalQuad, renderTarget, mesh;
let samples = 0;
let outputContainer;

init();
render();

function init() {

	// renderer setup
	renderer = new THREE.WebGLRenderer( { antialias: false } );
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setClearColor( 0x09141a );
	renderer.setSize( window.innerWidth, window.innerHeight );
	renderer.outputEncoding = THREE.sRGBEncoding;
	document.body.appendChild( renderer.domElement );

	outputContainer = document.getElementById( 'output' );

	// scene setup
	scene = new THREE.Scene();

	const light = new THREE.DirectionalLight( 0xffffff, 1 );
	light.position.set( 1, 1, 1 );
	scene.add( light );
	scene.add( new THREE.AmbientLight( 0xb0bec5, 0.5 ) );

	// camera setup
	camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 50 );
	camera.position.set( - 2, 2, 3 );
	camera.far = 100;
	camera.updateProjectionMatrix();

	// stats setup
	stats = new Stats();
	document.body.appendChild( stats.dom );

	const rtMaterial = new THREE.ShaderMaterial( {

		defines: {
			BOUNCES: 5,
			RAY_OFFSET: '1e-4',
		},

		uniforms: {
			bvh: { value: new MeshBVHUniformStruct() },
			normalAttribute: { value: new FloatVertexAttributeTexture() },
			cameraWorldMatrix: { value: new THREE.Matrix4() },
			invProjectionMatrix: { value: new THREE.Matrix4() },
			seed: { value: 0 },
			opacity: { value: 1 },
		},

		vertexShader: /* glsl */`

			varying vec2 vUv;
			void main() {

				vec4 mvPosition = vec4( position, 1.0 );
				mvPosition = modelViewMatrix * mvPosition;
				gl_Position = projectionMatrix * mvPosition;

				vUv = uv;

			}

		`,

		fragmentShader: /* glsl */`
			precision highp usampler2D;
			${ shaderStructs }
			${ shaderIntersectFunction }
			#include <common>

			#ifndef RAY_OFFSET
			#define RAY_OFFSET 1e-4
			#endif

			uniform mat4 cameraWorldMatrix;
			uniform mat4 invProjectionMatrix;
			uniform sampler2D normalAttribute;
			uniform BVH bvh;
			uniform float seed;
			uniform float opacity;
			varying vec2 vUv;

			void main() {

				// get [-1, 1] normalized device coordinates
				vec2 ndc = 2.0 * vUv - vec2( 1.0 );
				Ray ray = ndcToCameraRay( ndc, cameraWorldMatrix, invProjectionMatrix );

				// Lambertian render
				gl_FragColor = vec4( 0.0 );

				vec3 throughputColor = vec3( 1.0 );
				vec3 randomPoint = vec3( .0 );
				BVHRayHit hit = emptyBVHRayHit();
				for ( int i = 0; i < BOUNCES; i ++ ) {

					if ( ! bvhIntersectFirstHit( bvh, ray, hit ) ) {

						float value = ( ray.direction.y + 0.5 ) / 1.5;
						vec3 skyColor = mix( vec3( 1.0 ), vec3( 0.75, 0.85, 1.0 ), value );

						gl_FragColor = vec4( skyColor * throughputColor * 2.0, 1.0 );

						break;

					}

					throughputColor *= 1.0 / PI;

					randomPoint = vec3(
						rand( vUv + float( i + 1 ) + vec2( seed, seed ) ),
						rand( - vUv * seed + float( i ) - seed ),
						rand( - vUv * float( i + 1 ) - vec2( seed, - seed ) )
					);
					randomPoint -= 0.5;
					randomPoint *= 2.0;

					// ensure the random vector is not 0,0,0 and that it won't exactly negate
					// the surface normal

					float pointLength = max( length( randomPoint ), 1e-4 );
					randomPoint /= pointLength;
					randomPoint *= 0.999;

					// fetch the interpolated smooth normal
					vec3 normal =
						hit.side *
						textureSampleBarycoord(
							normalAttribute,
							hit.barycoord,
							hit.face.a,
							hit.face.b,
							hit.face.c
						).xyz;

					ray.direction = normalize( normal + randomPoint );
					ray.origin = hit.point + hit.face.normal * RAY_OFFSET;

				}

				gl_FragColor.a = opacity;

			}

		`

	} );

	rtQuad = new FullScreenQuad( rtMaterial );
	rtMaterial.transparent = true;
	rtMaterial.depthWrite = false;

	// load mesh and set up material BVH attributes
	new GLTFLoader().load( '../models/DragonAttenuation.glb', gltf => {

		let dragonMesh;
		gltf.scene.traverse( c => {

			if ( c.isMesh && c.name === 'Dragon' ) {

				dragonMesh = c;
				c.geometry.scale( 0.25, 0.25, 0.25 ).rotateX( Math.PI / 2 );

			}

		} );

		const planeGeom = new THREE.PlaneBufferGeometry( 5, 5, 1, 1 );
		planeGeom.rotateX( - Math.PI / 2 );

		const merged = mergeBufferGeometries( [ planeGeom, dragonMesh.geometry ], false );
		merged.translate( 0, - 0.5, 0 );

		mesh = new THREE.Mesh( merged, new THREE.MeshStandardMaterial() );
		scene.add( mesh );

		const bvh = new MeshBVH( mesh.geometry, { maxLeafTris: 1, strategy: SAH } );
		rtMaterial.uniforms.bvh.value.updateFrom( bvh );
		rtMaterial.uniforms.normalAttribute.value.updateFrom( mesh.geometry.attributes.normal );

	} );

	renderTarget = new THREE.WebGLRenderTarget( 1, 1, {

		format: THREE.RGBAFormat,
		type: THREE.FloatType,

	} );

	finalQuad = new FullScreenQuad( new THREE.MeshBasicMaterial( {

		map: renderTarget.texture,

	} ) );

	const controls = new OrbitControls( camera, renderer.domElement );
	controls.addEventListener( 'change', () => {

		resetSamples();

	} );

	gui = new GUI();
	gui.add( params, 'enableRaytracing' ).name( 'enable' );
	gui.add( params, 'accumulate' );
	gui.add( params, 'smoothImageScaling' );
	gui.add( params, 'resolutionScale', 0.1, 1, 0.01 ).onChange( resize );
	gui.add( params, 'rayOffsetExponent', - 7, 0, 0.01 ).onChange( v => {

		// specifies the ray offset on subsequent bounce because some mobile devices need larger offsets
		// presumably due to precision issues.
		rtMaterial.defines.RAY_OFFSET = Math.pow( 10, parseFloat( v ) );
		rtMaterial.needsUpdate = true;
		resetSamples();

	} );
	gui.add( params, 'bounces', 1, 10, 1 ).onChange( v => {

		rtMaterial.defines.BOUNCES = parseInt( v );
		rtMaterial.needsUpdate = true;
		resetSamples();

	} );
	gui.open();

	window.addEventListener( 'resize', resize, false );
	resize();

}

function resetSamples() {

	samples = 0;

}

function resize() {

	camera.aspect = window.innerWidth / window.innerHeight;
	camera.updateProjectionMatrix();

	const w = window.innerWidth;
	const h = window.innerHeight;
	const dpr = window.devicePixelRatio * params.resolutionScale;
	renderer.setSize( w, h );
	renderer.setPixelRatio( dpr );

	renderTarget.setSize( w * dpr, h * dpr );

	resetSamples();

}

function render() {

	stats.update();
	requestAnimationFrame( render );

	renderer.domElement.style.imageRendering = params.smoothImageScaling ? 'auto' : 'pixelated';

	if ( mesh && params.enableRaytracing ) {

		// jitter camera for AA
		if ( params.accumulate ) {

			const w = renderTarget.width;
			const h = renderTarget.height;
			camera.setViewOffset(
				w, h,
				Math.random(), Math.random(),
				w, h,
			);

		} else {

			resetSamples();

		}

		camera.updateMatrixWorld();

		// update material
		// keep appending a value that doesn't divide evenly into 2 so we have a different seed every frame
		const seed = ( rtQuad.material.uniforms.seed.value + 0.11111 ) % 2;
		rtQuad.material.uniforms.seed.value = seed;
		rtQuad.material.uniforms.cameraWorldMatrix.value.copy( camera.matrixWorld );
		rtQuad.material.uniforms.invProjectionMatrix.value.copy( camera.projectionMatrixInverse );
		rtQuad.material.uniforms.opacity.value = 1 / ( samples + 1 );

		// render float target
		renderer.autoClear = samples === 0;
		renderer.setRenderTarget( renderTarget );
		rtQuad.render( renderer );

		// render to screen
		renderer.setRenderTarget( null );
		finalQuad.render( renderer );

		renderer.autoClear = true;
		samples ++;

	} else {

		resetSamples();
		camera.clearViewOffset();
		renderer.render( scene, camera );

	}

	outputContainer.innerText = `samples: ${ samples }`;

}
<commit_msg>ray offset adjustments<commit_after>import * as THREE from 'three';
import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { mergeBufferGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
import Stats from 'stats.js';
import { GUI } from 'dat.gui';
import {
	MeshBVH, MeshBVHUniformStruct, FloatVertexAttributeTexture,
	shaderStructs, shaderIntersectFunction, SAH,
} from '../src/index.js';

const params = {
	enableRaytracing: true,
	smoothImageScaling: true,
	resolutionScale: Math.min( 0.5, 1 / window.devicePixelRatio ),
	bounces: 3,
	accumulate: true,
};

let renderer, camera, scene, gui, stats;
let rtQuad, finalQuad, renderTarget, mesh;
let samples = 0;
let outputContainer;

init();
render();

function init() {

	// renderer setup
	renderer = new THREE.WebGLRenderer( { antialias: false } );
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setClearColor( 0x09141a );
	renderer.setSize( window.innerWidth, window.innerHeight );
	renderer.outputEncoding = THREE.sRGBEncoding;
	document.body.appendChild( renderer.domElement );

	outputContainer = document.getElementById( 'output' );

	// scene setup
	scene = new THREE.Scene();

	const light = new THREE.DirectionalLight( 0xffffff, 1 );
	light.position.set( 1, 1, 1 );
	scene.add( light );
	scene.add( new THREE.AmbientLight( 0xb0bec5, 0.5 ) );

	// camera setup
	camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 50 );
	camera.position.set( - 2, 2, 3 );
	camera.far = 100;
	camera.updateProjectionMatrix();

	// stats setup
	stats = new Stats();
	document.body.appendChild( stats.dom );

	// hand-tuned ray origin offset values to accommodate floating point error. Mobile offset
	// tuned from Pixel 3 device that reports as highp but seemingly has low precision.
	const isMobile = 'ontouchstart' in window && navigator.maxTouchPoints > 0;
	const rayOffsetExponent = isMobile ? - 3 : - 5;

	const rtMaterial = new THREE.ShaderMaterial( {

		defines: {
			BOUNCES: 5,
			RAY_OFFSET: Math.pow( 10, rayOffsetExponent ),
		},

		uniforms: {
			bvh: { value: new MeshBVHUniformStruct() },
			normalAttribute: { value: new FloatVertexAttributeTexture() },
			cameraWorldMatrix: { value: new THREE.Matrix4() },
			invProjectionMatrix: { value: new THREE.Matrix4() },
			seed: { value: 0 },
			opacity: { value: 1 },
		},

		vertexShader: /* glsl */`

			varying vec2 vUv;
			void main() {

				vec4 mvPosition = vec4( position, 1.0 );
				mvPosition = modelViewMatrix * mvPosition;
				gl_Position = projectionMatrix * mvPosition;

				vUv = uv;

			}

		`,

		fragmentShader: /* glsl */`
			precision highp usampler2D;
			${ shaderStructs }
			${ shaderIntersectFunction }
			#include <common>

			uniform mat4 cameraWorldMatrix;
			uniform mat4 invProjectionMatrix;
			uniform sampler2D normalAttribute;
			uniform BVH bvh;
			uniform float seed;
			uniform float opacity;
			varying vec2 vUv;

			void main() {

				// get [-1, 1] normalized device coordinates
				vec2 ndc = 2.0 * vUv - vec2( 1.0 );
				Ray ray = ndcToCameraRay( ndc, cameraWorldMatrix, invProjectionMatrix );

				// Lambertian render
				gl_FragColor = vec4( 0.0 );

				vec3 throughputColor = vec3( 1.0 );
				vec3 randomPoint = vec3( .0 );
				BVHRayHit hit = emptyBVHRayHit();
				for ( int i = 0; i < BOUNCES; i ++ ) {

					if ( ! bvhIntersectFirstHit( bvh, ray, hit ) ) {

						float value = ( ray.direction.y + 0.5 ) / 1.5;
						vec3 skyColor = mix( vec3( 1.0 ), vec3( 0.75, 0.85, 1.0 ), value );

						gl_FragColor = vec4( skyColor * throughputColor * 2.0, 1.0 );

						break;

					}

					throughputColor *= 1.0 / PI;

					randomPoint = vec3(
						rand( vUv + float( i + 1 ) + vec2( seed, seed ) ),
						rand( - vUv * seed + float( i ) - seed ),
						rand( - vUv * float( i + 1 ) - vec2( seed, - seed ) )
					);
					randomPoint -= 0.5;
					randomPoint *= 2.0;

					// ensure the random vector is not 0,0,0 and that it won't exactly negate
					// the surface normal

					float pointLength = max( length( randomPoint ), 1e-4 );
					randomPoint /= pointLength;
					randomPoint *= 0.999;

					// fetch the interpolated smooth normal
					vec3 normal =
						hit.side *
						textureSampleBarycoord(
							normalAttribute,
							hit.barycoord,
							hit.face.a,
							hit.face.b,
							hit.face.c
						).xyz;

					// adjust the hit point by the surface normal by a factor of some offset and the
					// maximum component-wise value of the current point to accommodate floating point
					// error as values increase.
					vec3 absPoint = abs( hit.point );
					float maxPoint = max( absPoint.x, max( absPoint.y, absPoint.z ) );
					ray.origin = hit.point + hit.face.normal * ( maxPoint + 1.0 ) * RAY_OFFSET;
					ray.direction = normalize( normal + randomPoint );

				}

				gl_FragColor.a = opacity;

			}

		`

	} );

	rtQuad = new FullScreenQuad( rtMaterial );
	rtMaterial.transparent = true;
	rtMaterial.depthWrite = false;

	// load mesh and set up material BVH attributes
	new GLTFLoader().load( '../models/DragonAttenuation.glb', gltf => {

		let dragonMesh;
		gltf.scene.traverse( c => {

			if ( c.isMesh && c.name === 'Dragon' ) {

				dragonMesh = c;
				c.geometry.scale( 0.25, 0.25, 0.25 ).rotateX( Math.PI / 2 );

			}

		} );

		const planeGeom = new THREE.PlaneBufferGeometry( 5, 5, 1, 1 );
		planeGeom.rotateX( - Math.PI / 2 );

		const merged = mergeBufferGeometries( [ planeGeom, dragonMesh.geometry ], false );
		merged.translate( 0, - 0.5, 0 );

		mesh = new THREE.Mesh( merged, new THREE.MeshStandardMaterial() );
		scene.add( mesh );

		const bvh = new MeshBVH( mesh.geometry, { maxLeafTris: 1, strategy: SAH } );
		rtMaterial.uniforms.bvh.value.updateFrom( bvh );
		rtMaterial.uniforms.normalAttribute.value.updateFrom( mesh.geometry.attributes.normal );

	} );

	renderTarget = new THREE.WebGLRenderTarget( 1, 1, {

		format: THREE.RGBAFormat,
		type: THREE.FloatType,

	} );

	finalQuad = new FullScreenQuad( new THREE.MeshBasicMaterial( {

		map: renderTarget.texture,

	} ) );

	const controls = new OrbitControls( camera, renderer.domElement );
	controls.addEventListener( 'change', () => {

		resetSamples();

	} );

	gui = new GUI();
	gui.add( params, 'enableRaytracing' ).name( 'enable' );
	gui.add( params, 'accumulate' );
	gui.add( params, 'smoothImageScaling' );
	gui.add( params, 'resolutionScale', 0.1, 1, 0.01 ).onChange( resize );
	gui.add( params, 'bounces', 1, 10, 1 ).onChange( v => {

		rtMaterial.defines.BOUNCES = parseInt( v );
		rtMaterial.needsUpdate = true;
		resetSamples();

	} );
	gui.open();

	window.addEventListener( 'resize', resize, false );
	resize();

}

function resetSamples() {

	samples = 0;

}

function resize() {

	camera.aspect = window.innerWidth / window.innerHeight;
	camera.updateProjectionMatrix();

	const w = window.innerWidth;
	const h = window.innerHeight;
	const dpr = window.devicePixelRatio * params.resolutionScale;
	renderer.setSize( w, h );
	renderer.setPixelRatio( dpr );

	renderTarget.setSize( w * dpr, h * dpr );

	resetSamples();

}

function render() {

	stats.update();
	requestAnimationFrame( render );

	renderer.domElement.style.imageRendering = params.smoothImageScaling ? 'auto' : 'pixelated';

	if ( mesh && params.enableRaytracing ) {

		// jitter camera for AA
		if ( params.accumulate ) {

			const w = renderTarget.width;
			const h = renderTarget.height;
			camera.setViewOffset(
				w, h,
				Math.random(), Math.random(),
				w, h,
			);

		} else {

			resetSamples();

		}

		camera.updateMatrixWorld();

		// update material
		// keep appending a value that doesn't divide evenly into 2 so we have a different seed every frame
		const seed = ( rtQuad.material.uniforms.seed.value + 0.11111 ) % 2;
		rtQuad.material.uniforms.seed.value = seed;
		rtQuad.material.uniforms.cameraWorldMatrix.value.copy( camera.matrixWorld );
		rtQuad.material.uniforms.invProjectionMatrix.value.copy( camera.projectionMatrixInverse );
		rtQuad.material.uniforms.opacity.value = 1 / ( samples + 1 );

		// render float target
		renderer.autoClear = samples === 0;
		renderer.setRenderTarget( renderTarget );
		rtQuad.render( renderer );

		// render to screen
		renderer.setRenderTarget( null );
		finalQuad.render( renderer );

		renderer.autoClear = true;
		samples ++;

	} else {

		resetSamples();
		camera.clearViewOffset();
		renderer.render( scene, camera );

	}

	outputContainer.innerText = `samples: ${ samples }`;

}
<|endoftext|>


<commit_before>var asyncLib = require('async');
var fileSystemConnector = require('../data_connection/file_system_connector');
var databaseConnector = require('../data_connection/database_connector');
var searchEngineConnector = require('../data_connection/search_engine_connector');
var config = require('../config/config');
var path = require('path');

var articleService = module.exports = {};

articleService.createArticle = function () {
    return databaseConnector.createArticle();
};

articleService.saveArticle = function (article, title, content, author) {
    return new Promise(function (resolve, reject) {
        var authorName = '';
        var authorMail = '';
        if (author) {
            authorName = author.name || '';
            authorMail = author.email || '';
        }

        content = fileSystemConnector.wrapContentInHTMLBody(content, title);
        fileSystemConnector.saveContent(article._id, content, article.isTemporary).then(function () {
            updateArticle(article, title, authorName, authorMail);
            databaseConnector.saveArticle(article).then(resolve, reject);
            searchEngineConnector.updateIndex();
        }, reject);
    });
};

articleService.findArticleById = function (id) {
    return databaseConnector.findArticleById(id);
};

articleService.saveDocument = function (article, document) {
    return new Promise(function (resolve, reject) {
        fileSystemConnector.saveDocument(document, article._id, article.isTemporary).then(function (storageInfo) {
            databaseConnector.addDocumentToArticle(article, storageInfo).then(resolve, reject);
            searchEngineConnector.updateIndex();
        }, reject);
    });
};

articleService.saveDocuments = function (article, documents) {
    var storageInfoList = [];
    return new Promise(function (resolve, reject) {
        asyncLib.each(documents, function (item, cb) {
            articleService.saveDocument(article, item).then(function (storageInfo) {
                storageInfoList.push(storageInfo);
                cb();
            }, cb);
        }, function (error) {
            if (!error) {
                return resolve(storageInfoList);
            }
            reject(error);
        });
    });
};

articleService.getArticleContent = function (articleId) {
    return new Promise(function(resolve) {
        fileSystemConnector.readArticleContent(articleId).then(function(content) {
            resolve(content);
        }, function() {
            resolve('');
        });
    });
};

articleService.getOldArticleContentAndTitle = function(articleId) {
    return new Promise(function(resolve) {
        fileSystemConnector.readOldArticleContentAndTitle(articleId).then(function(contentAndTitle) {
            resolve(contentAndTitle);
        }, function() {
            resolve('');
        });
    });
}

articleService.deleteArticle = function(articleId){
    var promiseList = [
        fileSystemConnector.deleteArticle(articleId),
        databaseConnector.deleteArticles(articleId)
    ];

    return Promise.all(promiseList).then(function () {
        searchEngineConnector.updateIndex();
    });
};

articleService.deleteDocument = function(article, filename){
    var document = removeFileFromArticle(article, filename);
    if (!document) {
        return Promise.reject(new Error('Document not found'));
    }
    var docPath = fileSystemConnector.getPathToDocumentUnsafe(article, document);

    var promiseList = [
        fileSystemConnector.deleteDocument(docPath),
        databaseConnector.saveArticle(article)
    ];

    return Promise.all(promiseList).then(function () {
        searchEngineConnector.updateIndex();
    });
};

/**
 * updateArticle sets the lastChangedBy JSON and the author iff empty.
 *
 * @param article
 * @param title
 * @param authorName
 * @param authorMail
 */
function updateArticle(article, title, authorName, authorMail) {
        article.title = title;
        if (!article.author.name && !article.author.email) {
            article.author.name = authorName;
            article.author.email = authorMail;
        }

        article.lastChangedBy.name = authorName;
        article.lastChangedBy.email = authorMail;
        if(article.isTemporary) {
            article.isTemporary = false;
            article.documents.forEach(function (document) {
                document.path = path.join(config.fileLinkPrefix, config.uploadDirPerm, article._id + '', document.name + '.' + document.filetype).replace(/[\\]/g, '/');
            });
        }
}

function removeFileFromArticle(article, filename) {
    var hasFound = false;
    article.documents.forEach(function(document, idx) {
        var tempFilename = document.name + "." + document.filetype;
        if(filename == tempFilename) {
            hasFound = document;
            var tempDocument = article.documents.pop();
            if(idx !== article.documents.length) {
                article.documents[idx] = tempDocument;
            }

            return false;
        }
    });

    if(hasFound && article.hasOwnProperty("markModified")) {
        article.markModified('documents');
    }

    return hasFound;
}

articleService.searchArticles = function (q) {
    // captures author:foobar and author:'foo bar'
    var author = q.match(/author:(['"].+?['"]|[^\s]+)/i);
    var onlyAuthor = q.match(/^author:(?:['"].+?['"]|[^\s]+)$/i);
    // remove author from search terms
    var search = q.replace(/(author:(?:['"].+?['"]|[^\s]+))/i, '');

    if (author) {
        author = author[1];
    }

    return new Promise(function (resolve, reject) {
        if (!onlyAuthor) {
            searchEngineConnector.searchArticles(search).then(function (searchResults) {
                if (searchResults.length === 0) {
                    return resolve(searchResults);
                }

                var ids = [];
                searchResults.forEach(function (searchResult) {
                    if (ids.indexOf(searchResult.id) < 0) {
                        ids.push(searchResult.id);
                    }
                });

                databaseConnector.findArticlesByIds(ids).then(function (articles) {
                    // sort articles like search results
                    articles = articles.sort(function (a, b) {
                        return ids.indexOf(a._id + '') - ids.indexOf(b._id + '');
                    });

                    // filter articles by author
                    if (author) {
                        var spliceIndexes = [];
                        articles.forEach(function (author, id) {
                            if (article.author !== author) {
                                spliceIndexes.push(id);
                            }
                        });
                        for (var i = spliceIndexes.length - 1; i >= 0; i--) {
                            articles = articles.splice(spliceIndexes[i], 1);
                        }
                    }

                    var promiseList = [];

                    // map search results to articles
                    articles.forEach(function (article) {
                        searchResults.forEach(function (searchResult) {
                            if (article._id + '' === searchResult.id) {
                                if (searchResult.filename === config.articleContentFileName) {
                                    if (searchResult.text) {
                                        article.text = searchResult.text;
                                    } else {
                                        promiseList.push(fileSystemConnector.readArticleContent(article._id).then(function (content) {
                                            article.text = content.substring(0, 200);
                                        }));
                                    }
                                } else {
                                    article.documents.forEach(function (document) {
                                        if (document.name + '.' + document.filetype === searchResult.filename) {
                                            document.containsSearchText = true;
                                            if (!article.text) {
                                                article.text = searchResult.text;
                                            }
                                        }
                                    });
                                }
                            }
                        });
                    });

                    if (promiseList.length > 0) {
                        Promise.all(promiseList).then(function (result) {
                            resolve(articles);
                        });
                    } else {
                        resolve(articles);
                    }
                }, reject);
            });
        } else { //onlyAuthor
            databaseConnector.findArticlesByAuthor(author).then(function (articles) {
                var promiseList = [];

                articles.forEach(function (article) {
                    promiseList.push(fileSystemConnector.readArticleContent(article._id).then(function (content) {
                        article.text = content.substring(0, 200);
                    }));
                });

                if (promiseList.length > 0) {
                    Promise.all(promiseList).then(function (result) {
                        resolve(articles);
                    });
                } else {
                    resolve(articles);
                }
            });
        }
    });
};

articleService.getArticlesByIds = function (ids) {
    return new Promise(function (resolve, reject) {
        databaseConnector.findArticlesByIds(ids).then(function (articles) {
            var promiseList = [];

            articles.forEach(function (article) {
                promiseList.push(fileSystemConnector.readArticleContent(article._id).then(function (content) {
                    article.text = content.substring(0, 200);
                }));
            });

            if (promiseList.length > 0) {
                Promise.all(promiseList).then(function (result) {
                    resolve(articles);
                });
            } else {
                resolve(articles);
            }
        });
    });
};

articleService.deleteEmptyArticles = function(){
    databaseConnector.findAllPermArticleIds().then(function (articleIds) {
        articleIds.forEach(function (articleId) {
            fileSystemConnector.isArticleFileExists(articleId).then(function(exists){
                if(!exists) {
                    return databaseConnector.deleteArticles(articleId);
                }
            });
        });

    });
};<commit_msg>[KNOW-162] fix author recognition with spaces<commit_after>var asyncLib = require('async');
var fileSystemConnector = require('../data_connection/file_system_connector');
var databaseConnector = require('../data_connection/database_connector');
var searchEngineConnector = require('../data_connection/search_engine_connector');
var config = require('../config/config');
var path = require('path');

var articleService = module.exports = {};

articleService.createArticle = function () {
    return databaseConnector.createArticle();
};

articleService.saveArticle = function (article, title, content, author) {
    return new Promise(function (resolve, reject) {
        var authorName = '';
        var authorMail = '';
        if (author) {
            authorName = author.name || '';
            authorMail = author.email || '';
        }

        content = fileSystemConnector.wrapContentInHTMLBody(content, title);
        fileSystemConnector.saveContent(article._id, content, article.isTemporary).then(function () {
            updateArticle(article, title, authorName, authorMail);
            databaseConnector.saveArticle(article).then(resolve, reject);
            searchEngineConnector.updateIndex();
        }, reject);
    });
};

articleService.findArticleById = function (id) {
    return databaseConnector.findArticleById(id);
};

articleService.saveDocument = function (article, document) {
    return new Promise(function (resolve, reject) {
        fileSystemConnector.saveDocument(document, article._id, article.isTemporary).then(function (storageInfo) {
            databaseConnector.addDocumentToArticle(article, storageInfo).then(resolve, reject);
            searchEngineConnector.updateIndex();
        }, reject);
    });
};

articleService.saveDocuments = function (article, documents) {
    var storageInfoList = [];
    return new Promise(function (resolve, reject) {
        asyncLib.each(documents, function (item, cb) {
            articleService.saveDocument(article, item).then(function (storageInfo) {
                storageInfoList.push(storageInfo);
                cb();
            }, cb);
        }, function (error) {
            if (!error) {
                return resolve(storageInfoList);
            }
            reject(error);
        });
    });
};

articleService.getArticleContent = function (articleId) {
    return new Promise(function(resolve) {
        fileSystemConnector.readArticleContent(articleId).then(function(content) {
            resolve(content);
        }, function() {
            resolve('');
        });
    });
};

articleService.getOldArticleContentAndTitle = function(articleId) {
    return new Promise(function(resolve) {
        fileSystemConnector.readOldArticleContentAndTitle(articleId).then(function(contentAndTitle) {
            resolve(contentAndTitle);
        }, function() {
            resolve('');
        });
    });
}

articleService.deleteArticle = function(articleId){
    var promiseList = [
        fileSystemConnector.deleteArticle(articleId),
        databaseConnector.deleteArticles(articleId)
    ];

    return Promise.all(promiseList).then(function () {
        searchEngineConnector.updateIndex();
    });
};

articleService.deleteDocument = function(article, filename){
    var document = removeFileFromArticle(article, filename);
    if (!document) {
        return Promise.reject(new Error('Document not found'));
    }
    var docPath = fileSystemConnector.getPathToDocumentUnsafe(article, document);

    var promiseList = [
        fileSystemConnector.deleteDocument(docPath),
        databaseConnector.saveArticle(article)
    ];

    return Promise.all(promiseList).then(function () {
        searchEngineConnector.updateIndex();
    });
};

/**
 * updateArticle sets the lastChangedBy JSON and the author iff empty.
 *
 * @param article
 * @param title
 * @param authorName
 * @param authorMail
 */
function updateArticle(article, title, authorName, authorMail) {
        article.title = title;
        if (!article.author.name && !article.author.email) {
            article.author.name = authorName;
            article.author.email = authorMail;
        }

        article.lastChangedBy.name = authorName;
        article.lastChangedBy.email = authorMail;
        if(article.isTemporary) {
            article.isTemporary = false;
            article.documents.forEach(function (document) {
                document.path = path.join(config.fileLinkPrefix, config.uploadDirPerm, article._id + '', document.name + '.' + document.filetype).replace(/[\\]/g, '/');
            });
        }
}

function removeFileFromArticle(article, filename) {
    var hasFound = false;
    article.documents.forEach(function(document, idx) {
        var tempFilename = document.name + "." + document.filetype;
        if(filename == tempFilename) {
            hasFound = document;
            var tempDocument = article.documents.pop();
            if(idx !== article.documents.length) {
                article.documents[idx] = tempDocument;
            }

            return false;
        }
    });

    if(hasFound && article.hasOwnProperty("markModified")) {
        article.markModified('documents');
    }

    return hasFound;
}

articleService.searchArticles = function (q) {
    // captures author:foobar and author:'foo bar'
    var author = q.match(/author:(['"].+?['"]|[^\s]+)/i);
    var onlyAuthor = q.match(/^author:(?:['"].+?['"]|[^\s]+)$/i);
    // remove author from search terms
    var search = q.replace(/(author:(?:['"].+?['"]|[^\s]+))/ig, '');

    if (author) {
        author = author[1].replace(/["']/g, '');
    }

    return new Promise(function (resolve, reject) {
        if (!onlyAuthor) {
            searchEngineConnector.searchArticles(search).then(function (searchResults) {
                if (searchResults.length === 0) {
                    return resolve(searchResults);
                }

                var ids = [];
                searchResults.forEach(function (searchResult) {
                    if (ids.indexOf(searchResult.id) < 0) {
                        ids.push(searchResult.id);
                    }
                });

                databaseConnector.findArticlesByIds(ids).then(function (articles) {
                    // sort articles like search results
                    articles = articles.sort(function (a, b) {
                        return ids.indexOf(a._id + '') - ids.indexOf(b._id + '');
                    });

                    // filter articles by author
                    if (author) {
                        var spliceIndexes = [];
                        articles.forEach(function (author, id) {
                            if (article.author !== author) {
                                spliceIndexes.push(id);
                            }
                        });
                        for (var i = spliceIndexes.length - 1; i >= 0; i--) {
                            articles = articles.splice(spliceIndexes[i], 1);
                        }
                    }

                    var promiseList = [];

                    // map search results to articles
                    articles.forEach(function (article) {
                        searchResults.forEach(function (searchResult) {
                            if (article._id + '' === searchResult.id) {
                                if (searchResult.filename === config.articleContentFileName) {
                                    if (searchResult.text) {
                                        article.text = searchResult.text;
                                    } else {
                                        promiseList.push(fileSystemConnector.readArticleContent(article._id).then(function (content) {
                                            article.text = content.substring(0, 200);
                                        }));
                                    }
                                } else {
                                    article.documents.forEach(function (document) {
                                        if (document.name + '.' + document.filetype === searchResult.filename) {
                                            document.containsSearchText = true;
                                            if (!article.text) {
                                                article.text = searchResult.text;
                                            }
                                        }
                                    });
                                }
                            }
                        });
                    });

                    if (promiseList.length > 0) {
                        Promise.all(promiseList).then(function (result) {
                            resolve(articles);
                        });
                    } else {
                        resolve(articles);
                    }
                }, reject);
            });
        } else { //onlyAuthor
            databaseConnector.findArticlesByAuthor(author).then(function (articles) {
                var promiseList = [];

                articles.forEach(function (article) {
                    promiseList.push(fileSystemConnector.readArticleContent(article._id).then(function (content) {
                        article.text = content.substring(0, 200);
                    }));
                });

                if (promiseList.length > 0) {
                    Promise.all(promiseList).then(function (result) {
                        resolve(articles);
                    });
                } else {
                    resolve(articles);
                }
            });
        }
    });
};

articleService.getArticlesByIds = function (ids) {
    return new Promise(function (resolve, reject) {
        databaseConnector.findArticlesByIds(ids).then(function (articles) {
            var promiseList = [];

            articles.forEach(function (article) {
                promiseList.push(fileSystemConnector.readArticleContent(article._id).then(function (content) {
                    article.text = content.substring(0, 200);
                }));
            });

            if (promiseList.length > 0) {
                Promise.all(promiseList).then(function (result) {
                    resolve(articles);
                });
            } else {
                resolve(articles);
            }
        });
    });
};

articleService.deleteEmptyArticles = function(){
    databaseConnector.findAllPermArticleIds().then(function (articleIds) {
        articleIds.forEach(function (articleId) {
            fileSystemConnector.isArticleFileExists(articleId).then(function(exists){
                if(!exists) {
                    return databaseConnector.deleteArticles(articleId);
                }
            });
        });

    });
};<|endoftext|>


<commit_before>import React from 'react'

import {
    Row,
    Col,
    FormGroup,
    Label,
    Input
} from 'reactstrap';

export default class PermissionForm extends React.Component {

    constructor(props) {
        super(props);

        this.updatePermission = this.updatePermission.bind(this);
        this.updateEmergency = this.updateEmergency.bind(this);
        this.updateNote = this.updateNote.bind(this);
        this.updateCampWith = this.updateCampWith.bind(this);
    }

    updatePermission() {
        this.props.updateValidation();
        this.props.update('permission', !this.props.permission);
    }

    updateEmergency(item) {
        return (e) => {
            this.props.updateValidation();
            this.props.update(item, e.target.value)
            e.preventDefault();
        }
    }

    updateCampWith(e) {
        this.props.updateValidation();
        this.props.update('campWith', e.target.value);
        e.preventDefault();
    }

    updateNote(e) {
        this.props.updateValidation();
        this.props.update('note', e.target.value);
        e.preventDefault();
    }

    valid(item) {
        if (this.props.validating && (!item || item === "")) return false;
        return null;
    }

    render() {

        const valid = "form-group";
        const invalid = "form-group has-error";

        const customQuestions = this.props.event.customQuestions || {};

        const emergency = this.props.event.bigCampMode ? null :
            <React.Fragment>
                <Row>
                    <Col>

Emergency Contact

Please provide details of someone we can contact in case of an emergency during the event (a
                            second
                            person is better even if you are not attending yourself)

                    </Col>
                </Row>
                <FormGroup row>

                        Name:

                    <Col sm={10}>
                        <Input type="text"
                               placeholder="Name"
                               value={this.props.emergencyName || ''}
                               valid={this.valid(this.props.emergencyName)}
                               onChange={this.updateEmergency("emergencyName")}/>
                    </Col>
                </FormGroup>
                <FormGroup row>

                        Phone:

                    <Col sm={10}>
                        <Input type="text"
                               placeholder="Phone number"
                               value={this.props.emergencyPhone || ''}
                               valid={this.valid(this.props.emergencyPhone)}
                               onChange={this.updateEmergency("emergencyPhone")}/>
                    </Col>
                </FormGroup>
            </React.Fragment>;

        const campWith = this.props.event.bigCampMode ?

            <FormGroup row>

                    Are there any Groups/Districts you would like to camp
                    with?:

                <Col sm={10}>
                    <Input type="textarea"
                           value={this.props.campWith || ''}
                           onChange={this.updateEmergency("campWith")}/>
                </Col>
            </FormGroup> : null;

        return (<React.Fragment>
            {emergency}
            <Row>
                <Col>

Additional Information

                </Col>
            </Row>
            <FormGroup row>

                    {customQuestions.extraQuestion ? customQuestions.extraQuestion : 'Anything else we need to know?:'}

                <Col sm={10}>
                    <Input type="textarea"
                           placeholder=""
                           value={this.props.note || customQuestions.extraTemplate || ''}
                           onChange={this.updateNote}/>
                </Col>
            </FormGroup>
            {campWith}
            <Row>
                <Col>

Permission

                </Col>
            </Row>
            <FormGroup row>
                <Col sm={{size: 10, offset: 2}}>
                    <FormGroup check>

                            <Input type="checkbox" checked={!!this.props.permission}
                                   onChange={this.updatePermission}/>{' '}
                            I give permission for the people named above to
                            attend {this.props.event.name}

                            I agree this information will be stored electronically and shared only with individuals who need this information to
                            engage your child safely in Woodcraft Folk activities. Based on the needs of your child we may also share any relevant
                            information with medical or child protection professionals. For more information please visit
                             www.woodcraft.org.uk/privacy or contact data@woodcraft.org.uk

                    </FormGroup>
                </Col>
            </FormGroup>
        </React.Fragment>);
    }
}<commit_msg>bigger<commit_after>import React from 'react'

import {
    Row,
    Col,
    FormGroup,
    Label,
    Input
} from 'reactstrap';

export default class PermissionForm extends React.Component {

    constructor(props) {
        super(props);

        this.updatePermission = this.updatePermission.bind(this);
        this.updateEmergency = this.updateEmergency.bind(this);
        this.updateNote = this.updateNote.bind(this);
        this.updateCampWith = this.updateCampWith.bind(this);
    }

    updatePermission() {
        this.props.updateValidation();
        this.props.update('permission', !this.props.permission);
    }

    updateEmergency(item) {
        return (e) => {
            this.props.updateValidation();
            this.props.update(item, e.target.value)
            e.preventDefault();
        }
    }

    updateCampWith(e) {
        this.props.updateValidation();
        this.props.update('campWith', e.target.value);
        e.preventDefault();
    }

    updateNote(e) {
        this.props.updateValidation();
        this.props.update('note', e.target.value);
        e.preventDefault();
    }

    valid(item) {
        if (this.props.validating && (!item || item === "")) return false;
        return null;
    }

    render() {

        const valid = "form-group";
        const invalid = "form-group has-error";

        const customQuestions = this.props.event.customQuestions || {};

        const emergency = this.props.event.bigCampMode ? null :
            <React.Fragment>
                <Row>
                    <Col>

Emergency Contact

Please provide details of someone we can contact in case of an emergency during the event (a
                            second
                            person is better even if you are not attending yourself)

                    </Col>
                </Row>
                <FormGroup row>

                        Name:

                    <Col sm={10}>
                        <Input type="text"
                               placeholder="Name"
                               value={this.props.emergencyName || ''}
                               valid={this.valid(this.props.emergencyName)}
                               onChange={this.updateEmergency("emergencyName")}/>
                    </Col>
                </FormGroup>
                <FormGroup row>

                        Phone:

                    <Col sm={10}>
                        <Input type="text"
                               placeholder="Phone number"
                               value={this.props.emergencyPhone || ''}
                               valid={this.valid(this.props.emergencyPhone)}
                               onChange={this.updateEmergency("emergencyPhone")}/>
                    </Col>
                </FormGroup>
            </React.Fragment>;

        const campWith = this.props.event.bigCampMode ?

            <FormGroup row>

                    Are there any Groups/Districts you would like to camp
                    with?:

                <Col sm={10}>
                    <Input type="textarea"
                           value={this.props.campWith || ''}
                           onChange={this.updateEmergency("campWith")}/>
                </Col>
            </FormGroup> : null;

        return (<React.Fragment>
            {emergency}
            <Row>
                <Col>

Additional Information

                </Col>
            </Row>
            <FormGroup row>

                    {customQuestions.extraQuestion ? customQuestions.extraQuestion : 'Anything else we need to know?:'}

                <Col sm={10}>
                    <Input type="textarea"
                           placeholder=""
                           value={this.props.note || customQuestions.extraTemplate || ''}
                           onChange={this.updateNote}
                           rows={6}/>
                </Col>
            </FormGroup>
            {campWith}
            <Row>
                <Col>

Permission

                </Col>
            </Row>
            <FormGroup row>
                <Col sm={{size: 10, offset: 2}}>
                    <FormGroup check>

                            <Input type="checkbox" checked={!!this.props.permission}
                                   onChange={this.updatePermission}/>{' '}
                            I give permission for the people named above to
                            attend {this.props.event.name}

                            I agree this information will be stored electronically and shared only with individuals who need this information to
                            engage your child safely in Woodcraft Folk activities. Based on the needs of your child we may also share any relevant
                            information with medical or child protection professionals. For more information please visit
                             www.woodcraft.org.uk/privacy or contact data@woodcraft.org.uk

                    </FormGroup>
                </Col>
            </FormGroup>
        </React.Fragment>);
    }
}<|endoftext|>


<commit_before>(function(root, factory) {
    if (typeof exports === 'object') { // Node.js
        module.exports = factory(
            require('../functional.js'),
            require('../reactive.js')
        );
    } else if (typeof define === 'function' && define.amd) { // Require.JS
        define(['jef/functional', 'jef/reactive'], factory);
    } else {  // Browser globals
        root.jefdemo = root.jefdemo || {};
        root.jefdemo.reactive_excel = factory(
            root.jef.functional,
            root.jef.reactive
        );
    }
})(this, function(f, r){
    'use strict';

    function node_set_text(node, value) {
        node.innerText = value();
    }
    function extract_function_name(value) {
        return value.match(/[a-z]+/i)[0].toUpperCase();
    }
    function extract_function_arguments(value) {
        return value.match(/[a-z]{1,2}[0-9]{1,2}/ig) || [];
    }
    function build_excel_function(name, args) {
        return new Function(
            'functions',
            'variables',
            'value',
            'console.log(arguments); value(functions["' + name + '"](variables["'+ args.join('"], variables["') +'"]))'
        );
    }

    function merge(a, b) {
        var r = {};
        f.each(a, function(value, key) {
            r[key] = (key in b) ? b[key] : value
        });
        return r;
    }

    var baseOptions = {
        functions: {},
        columns: '#ABCDEFGHIJ',
        rows: 5
    };

    return {
        main: function(element, document, options) {
            options = merge(baseOptions, options);

            var tr, td, label, result, input, cellName,
                sharedVariables = {},
                sharedFunctions = {},
                cols = options.columns.length,
                rows = options.rows;

            // Build columns
            tr = document.createElement('tr');
            for (var j = 0; j < cols; j++) {
                td = document.createElement('td');
                td.innerText = options.columns.charAt(j);
                tr.appendChild(td);
            }
            element.appendChild(tr);

            // Build rows
            for (var i = 1; i < rows; i++) {
                tr = document.createElement('tr');

                td = document.createElement('td');
                td.innerText = i;
                tr.appendChild(td);

                // Build cells
                for (var j = 1; j < cols; j++) {
                    td = document.createElement('td');
                    input = document.createElement('input');
                    result = document.createElement('pre');

                    td.appendChild(input);
                    td.appendChild(result);
                    tr.appendChild(td);

                    cellName = options.columns.charAt(j) + i;
                    sharedVariables[cellName] = r.value(cellName)
                    sharedVariables[cellName] = r.observable(sharedVariables[cellName], node_set_text.bind(null, result, sharedVariables[cellName]));

                    input.onchange = (function(cellName) {
                        return function() {
                            if (this.value > 0) {
                                sharedVariables[cellName](this.value);
                            } else if(!this.value) {
                                sharedVariables[cellName](null);
                            } else {
                                sharedFunctions[cellName] = build_excel_function(
                                    extract_function_name(this.value),
                                    extract_function_arguments(this.value)
                                );

                                f.each(extract_function_arguments(this.value), function(variableName) {
                                    sharedVariables[variableName] = r.observable(sharedVariables[variableName], function() {
                                        if (cellName in sharedFunctions) {
                                            sharedFunctions[cellName](options.functions, sharedVariables, sharedVariables[cellName]);
                                        }
                                    });
                                });

                                sharedFunctions[cellName](options.functions, sharedVariables, sharedVariables[cellName]);
                            }
                        }
                    })(cellName);
                }
                element.appendChild(tr);
            }
        }
    };
});
<commit_msg>remove console.log<commit_after>(function(root, factory) {
    if (typeof exports === 'object') { // Node.js
        module.exports = factory(
            require('../functional.js'),
            require('../reactive.js')
        );
    } else if (typeof define === 'function' && define.amd) { // Require.JS
        define(['jef/functional', 'jef/reactive'], factory);
    } else {  // Browser globals
        root.jefdemo = root.jefdemo || {};
        root.jefdemo.reactive_excel = factory(
            root.jef.functional,
            root.jef.reactive
        );
    }
})(this, function(f, r){
    'use strict';

    function node_set_text(node, value) {
        node.innerText = value();
    }
    function extract_function_name(value) {
        return value.match(/[a-z]+/i)[0].toUpperCase();
    }
    function extract_function_arguments(value) {
        return value.match(/[a-z]{1,2}[0-9]{1,2}/ig) || [];
    }
    function build_excel_function(name, args) {
        return new Function(
            'functions',
            'variables',
            'value',
            'value(functions["' + name + '"](variables["'+ args.join('"], variables["') +'"]))'
        );
    }

    function merge(a, b) {
        var r = {};
        f.each(a, function(value, key) {
            r[key] = (key in b) ? b[key] : value
        });
        return r;
    }

    var baseOptions = {
        functions: {},
        columns: '#ABCDEFGHIJ',
        rows: 5
    };

    return {
        main: function(element, document, options) {
            options = merge(baseOptions, options);

            var tr, td, label, result, input, cellName,
                sharedVariables = {},
                sharedFunctions = {},
                cols = options.columns.length,
                rows = options.rows;

            // Build columns
            tr = document.createElement('tr');
            for (var j = 0; j < cols; j++) {
                td = document.createElement('td');
                td.innerText = options.columns.charAt(j);
                tr.appendChild(td);
            }
            element.appendChild(tr);

            // Build rows
            for (var i = 1; i < rows; i++) {
                tr = document.createElement('tr');

                td = document.createElement('td');
                td.innerText = i;
                tr.appendChild(td);

                // Build cells
                for (var j = 1; j < cols; j++) {
                    td = document.createElement('td');
                    input = document.createElement('input');
                    result = document.createElement('pre');

                    td.appendChild(input);
                    td.appendChild(result);
                    tr.appendChild(td);

                    cellName = options.columns.charAt(j) + i;
                    sharedVariables[cellName] = r.value(cellName)
                    sharedVariables[cellName] = r.observable(sharedVariables[cellName], node_set_text.bind(null, result, sharedVariables[cellName]));

                    input.onchange = (function(cellName) {
                        return function() {
                            if (this.value > 0) {
                                sharedVariables[cellName](this.value);
                            } else if(!this.value) {
                                sharedVariables[cellName](null);
                            } else {
                                sharedFunctions[cellName] = build_excel_function(
                                    extract_function_name(this.value),
                                    extract_function_arguments(this.value)
                                );

                                f.each(extract_function_arguments(this.value), function(variableName) {
                                    sharedVariables[variableName] = r.observable(sharedVariables[variableName], function() {
                                        if (cellName in sharedFunctions) {
                                            sharedFunctions[cellName](options.functions, sharedVariables, sharedVariables[cellName]);
                                        }
                                    });
                                });

                                sharedFunctions[cellName](options.functions, sharedVariables, sharedVariables[cellName]);
                            }
                        }
                    })(cellName);
                }
                element.appendChild(tr);
            }
        }
    };
});
<|endoftext|>


<commit_before>var abp = abp || {};
(function () {

    /* Application paths *****************************************/

    //Current application root path (including virtual directory if exists).
    abp.appPath = abp.appPath || '/';

    abp.pageLoadTime = new Date();

    //Converts given path to absolute path using abp.appPath variable.
    abp.toAbsAppPath = function (path) {
        if (path.indexOf('/') == 0) {
            path = path.substring(1);
        }

        return abp.appPath + path;
    };

    /* LOCALIZATION ***********************************************/
    //Implements Localization API that simplifies usage of localization scripts generated by Abp.

    abp.localization = abp.localization || {};

    abp.localization.localize = function (key, sourceName) {
        sourceName = sourceName || abp.localization.defaultSourceName;

        var source = abp.localization.values[sourceName];

        if (!source) {
            abp.log.warn('Could not find localization source: ' + sourceName);
            return key;
        }

        var value = source[key];
        if (value == undefined) {
            return key;
        }

        var copiedArguments = Array.prototype.slice.call(arguments, 0);
        copiedArguments.splice(1, 1);
        copiedArguments[0] = value;

        return abp.utils.formatString.apply(this, copiedArguments);
    };

    abp.localization.getSource = function (sourceName) {
        return function (key) {
            var copiedArguments = Array.prototype.slice.call(arguments, 0);
            copiedArguments.splice(1, 0, sourceName);
            return abp.localization.localize.apply(this, copiedArguments);
        };
    };

    abp.localization.isCurrentCulture = function (name) {
        return abp.localization.currentCulture
            && abp.localization.currentCulture.name
            && abp.localization.currentCulture.name.indexOf(name) == 0;
    };

    abp.localization.defaultSourceName = undefined;
    abp.localization.abpWeb = abp.localization.getSource('AbpWeb');

    /* AUTHORIZATION **********************************************/
    //Implements Authorization API that simplifies usage of authorization scripts generated by Abp.

    abp.auth = abp.auth || {};

    abp.auth.allPermissions = abp.auth.allPermissions || {};

    abp.auth.grantedPermissions = abp.auth.grantedPermissions || {};

    //Deprecated. Use abp.auth.isGranted instead.
    abp.auth.hasPermission = function (permissionName) {
        return abp.auth.isGranted.apply(this, arguments);
    };

    //Deprecated. Use abp.auth.isAnyGranted instead.
    abp.auth.hasAnyOfPermissions = function () {
        return abp.auth.isAnyGranted.apply(this, arguments);
    };

    //Deprecated. Use abp.auth.areAllGranted instead.
    abp.auth.hasAllOfPermissions = function () {
        return abp.auth.areAllGranted.apply(this, arguments);
    };

    abp.auth.isGranted = function (permissionName) {
        return abp.auth.allPermissions[permissionName] != undefined && abp.auth.grantedPermissions[permissionName] != undefined;
    };

    abp.auth.isAnyGranted = function () {
        if (!arguments || arguments.length <= 0) {
            return true;
        }

        for (var i = 0; i < arguments.length; i++) {
            if (abp.auth.isGranted(arguments[i])) {
                return true;
            }
        }

        return false;
    };

    abp.auth.areAllGranted = function () {
        if (!arguments || arguments.length <= 0) {
            return true;
        }

        for (var i = 0; i < arguments.length; i++) {
            if (!abp.auth.isGranted(arguments[i])) {
                return false;
            }
        }

        return true;
    };

    /* FEATURE SYSTEM *********************************************/
    //Implements Features API that simplifies usage of feature scripts generated by Abp.

    abp.features = abp.features || {};

    abp.features.allFeatures = abp.features.allFeatures || {};

    abp.features.get = function (name) {
        return abp.features.allFeatures[name];
    }

    abp.features.getValue = function (name) {
        var feature = abp.features.get(name);
        if (feature == undefined) {
            return undefined;
        }

        return feature.value;
    }

    abp.features.isEnabled = function (name) {
        var value = abp.features.getValue(name);
        return value == 'true' || value == 'True';
    }

    /* SETTINGS **************************************************/
    //Implements Settings API that simplifies usage of setting scripts generated by Abp.

    abp.setting = abp.setting || {};

    abp.setting.values = abp.setting.values || {};

    abp.setting.get = function (name) {
        return abp.setting.values[name];
    };

    abp.setting.getBoolean = function (name) {
        var value = abp.setting.get(name);
        return value == 'true' || value == 'True';
    };

    abp.setting.getInt = function (name) {
        return parseInt(abp.setting.values[name]);
    };

    /* REALTIME NOTIFICATIONS ************************************/

    abp.notifications = abp.notifications || {};

    abp.notifications.severity = {
        INFO: 0,
        SUCCESS: 1,
        WARN: 2,
        ERROR: 3,
        FATAL: 4
    };

    abp.notifications.userNotificationState = {
        UNREAD: 0,
        READ: 1
    };

    abp.notifications.getUserNotificationStateAsString = function (userNotificationState) {
        switch (userNotificationState) {
            case abp.notifications.userNotificationState.READ:
                return 'READ';
            case abp.notifications.userNotificationState.UNREAD:
                return 'UNREAD';
            default:
                abp.log.warn('Unknown user notification state value: ' + userNotificationState)
                return '?';
        }
    };

    abp.notifications.getUiNotifyFuncBySeverity = function (severity) {
        switch (severity) {
            case abp.notifications.severity.SUCCESS:
                return abp.notify.success;
            case abp.notifications.severity.WARN:
                return abp.notify.warn;
            case abp.notifications.severity.ERROR:
                return abp.notify.error;
            case abp.notifications.severity.FATAL:
                return abp.notify.error;
            case abp.notifications.severity.INFO:
            default:
                return abp.notify.info;
        }
    };

    abp.notifications.messageFormatters = [];

    abp.notifications.messageFormatters.push({
        type: 'Abp.Notifications.MessageNotificationData',
        format: function (userNotification) {
            return userNotification.notification.data.message;
        }
    });

    abp.notifications.messageFormatters.push({
        type: 'Abp.Notifications.LocalizableMessageNotificationData',
        format: function (userNotification) {
            var localizedMessage = abp.localization.localize(
                userNotification.notification.data.message.name,
                userNotification.notification.data.message.sourceName
            );

            $.each(userNotification.notification.data.properties, function (key, value) {
                localizedMessage = localizedMessage.replace('{' + key + '}', value);
            });
        }
    });

    abp.notifications.getFormattedMessageFromUserNotification = function (userNotification) {
        for (var i = 0; i < abp.notifications.messageFormatters.length; i++) {
            var formatter = abp.notifications.messageFormatters[i];
            if (formatter.type === userNotification.notification.data.type) {
                return formatter.format(userNotification);
            }
        }

        abp.log.warn('No message formatted defined for given data type: ' + userNotification.notification.data.type)
        return '?';
    }

    /* LOGGING ***************************************************/
    //Implements Logging API that provides secure & controlled usage of console.log

    abp.log = abp.log || {};

    abp.log.levels = {
        DEBUG: 1,
        INFO: 2,
        WARN: 3,
        ERROR: 4,
        FATAL: 5
    };

    abp.log.level = abp.log.levels.DEBUG;

    abp.log.log = function (logObject, logLevel) {
        if (!window.console || !window.console.log) {
            return;
        }

        if (logLevel != undefined && logLevel < abp.log.level) {
            return;
        }

        console.log(logObject);
    };

    abp.log.debug = function (logObject) {
        abp.log.log("DEBUG: ", abp.log.levels.DEBUG);
        abp.log.log(logObject, abp.log.levels.DEBUG);
    };

    abp.log.info = function (logObject) {
        abp.log.log("INFO: ", abp.log.levels.INFO);
        abp.log.log(logObject, abp.log.levels.INFO);
    };

    abp.log.warn = function (logObject) {
        abp.log.log("WARN: ", abp.log.levels.WARN);
        abp.log.log(logObject, abp.log.levels.WARN);
    };

    abp.log.error = function (logObject) {
        abp.log.log("ERROR: ", abp.log.levels.ERROR);
        abp.log.log(logObject, abp.log.levels.ERROR);
    };

    abp.log.fatal = function (logObject) {
        abp.log.log("FATAL: ", abp.log.levels.FATAL);
        abp.log.log(logObject, abp.log.levels.FATAL);
    };

    /* NOTIFICATION *********************************************/
    //Defines Notification API, not implements it

    abp.notify = abp.notify || {};

    abp.notify.success = function (message, title, options) {
        abp.log.warn('abp.notify.success is not implemented!');
    };

    abp.notify.info = function (message, title, options) {
        abp.log.warn('abp.notify.info is not implemented!');
    };

    abp.notify.warn = function (message, title, options) {
        abp.log.warn('abp.notify.warn is not implemented!');
    };

    abp.notify.error = function (message, title, options) {
        abp.log.warn('abp.notify.error is not implemented!');
    };

    /* MESSAGE **************************************************/
    //Defines Message API, not implements it

    abp.message = abp.message || {};

    var showMessage = function (message, title) {
        alert((title || '') + ' ' + message);

        if (!$) {
            abp.log.warn('abp.message can not return promise since jQuery is not defined!');
            return null;
        }

        return $.Deferred(function ($dfd) {
            $dfd.resolve();
        });
    };

    abp.message.info = function (message, title) {
        abp.log.warn('abp.message.info is not implemented!');
        return showMessage(message, title);
    };

    abp.message.success = function (message, title) {
        abp.log.warn('abp.message.success is not implemented!');
        return showMessage(message, title);
    };

    abp.message.warn = function (message, title) {
        abp.log.warn('abp.message.warn is not implemented!');
        return showMessage(message, title);
    };

    abp.message.error = function (message, title) {
        abp.log.warn('abp.message.error is not implemented!');
        return showMessage(message, title);
    };

    abp.message.confirm = function (message, titleOrCallback, callback) {
        abp.log.warn('abp.message.confirm is not implemented!');

        if (titleOrCallback && !(typeof titleOrCallback == 'string')) {
            callback = titleOrCallback;
        }

        var result = confirm(message);
        callback && callback(result);

        if (!$) {
            abp.log.warn('abp.message can not return promise since jQuery is not defined!');
            return null;
        }

        return $.Deferred(function ($dfd) {
            $dfd.resolve();
        });
    };

    /* UI *******************************************************/

    abp.ui = abp.ui || {};

    /* UI BLOCK */
    //Defines UI Block API, not implements it

    abp.ui.block = function (elm) {
        abp.log.warn('abp.ui.block is not implemented!');
    };

    abp.ui.unblock = function (elm) {
        abp.log.warn('abp.ui.unblock is not implemented!');
    };

    /* UI BUSY */
    //Defines UI Busy API, not implements it

    abp.ui.setBusy = function (elm, optionsOrPromise) {
        abp.log.warn('abp.ui.setBusy is not implemented!');
    };

    abp.ui.clearBusy = function (elm) {
        abp.log.warn('abp.ui.clearBusy is not implemented!');
    };

    /* SIMPLE EVENT BUS *****************************************/

    abp.event = (function () {

        var _callbacks = {};

        var on = function (eventName, callback) {
            if (!_callbacks[eventName]) {
                _callbacks[eventName] = [];
            }

            _callbacks[eventName].push(callback);
        };

        var off = function (eventName, callback) {
            var callbacks = _callbacks[eventName];
            if (!callbacks) {
                return;
            }

            var index = -1;
            for (var i = 0; i < callbacks.length; i++) {
                if (callbacks[i] === callback) {
                    index = i;
                    break;
                }
            }

            if (index < 0) {
                return;
            }

            _callbacks[eventName].splice(index, 1);
        };

        var trigger = function (eventName) {
            var callbacks = _callbacks[eventName];
            if (!callbacks || !callbacks.length) {
                return;
            }

            var args = Array.prototype.slice.call(arguments, 1);
            for (var i = 0; i < callbacks.length; i++) {
                callbacks[i].apply(this, args);
            }
        };

        // Public interface ///////////////////////////////////////////////////

        return {
            on: on,
            off: off,
            trigger: trigger
        };
    })();

    /* UTILS ***************************************************/

    abp.utils = abp.utils || {};

    /* Creates a name namespace.
    *  Example:
    *  var taskService = abp.utils.createNamespace(abp, 'services.task');
    *  taskService will be equal to abp.services.task
    *  first argument (root) must be defined first
    ************************************************************/
    abp.utils.createNamespace = function (root, ns) {
        var parts = ns.split('.');
        for (var i = 0; i < parts.length; i++) {
            if (typeof root[parts[i]] == 'undefined') {
                root[parts[i]] = {};
            }

            root = root[parts[i]];
        }

        return root;
    };

    /* Formats a string just like string.format in C#.
    *  Example:
    *  _formatString('Hello {0}','Halil') = 'Hello Halil'
    ************************************************************/
    abp.utils.formatString = function () {
        if (arguments.length < 1) {
            return null;
        }

        var str = arguments[0];

        for (var i = 1; i < arguments.length; i++) {
            var placeHolder = '{' + (i - 1) + '}';
            str = str.replace(placeHolder, arguments[i]);
        }

        return str;
    };

    abp.utils.toPascalCase = function (str) {
        if (!str || !str.length) {
            return str;
        }

        if (str.length === 1) {
            return str.charAt(0).toUpperCase();
        }

        return str.charAt(0).toUpperCase() + str.substr(1);
    }

    abp.utils.toCamelCase = function (str) {
        if (!str || !str.length) {
            return str;
        }

        if (str.length === 1) {
            return str.charAt(0).toLowerCase();
        }

        return str.charAt(0).toLowerCase() + str.substr(1);
    }

    abp.utils.truncateString = function (str, maxLength) {
        if (!str || !str.length || str.length <= maxLength) {
            return str;
        }

        return str.substr(0, maxLength);
    };

    abp.utils.truncateStringWithPostfix = function (str, maxLength, postfix) {
        postfix = postfix || '...';

        if (!str || !str.length || str.length <= maxLength) {
            return str;
        }

        if (maxLength <= postfix.length) {
            return postfix.substr(0, maxLength);
        }

        return str.substr(0, maxLength - postfix.length) + postfix;
    };

})();<commit_msg>Client side notification api improvements.<commit_after>var abp = abp || {};
(function ($) {

    /* Application paths *****************************************/

    //Current application root path (including virtual directory if exists).
    abp.appPath = abp.appPath || '/';

    abp.pageLoadTime = new Date();

    //Converts given path to absolute path using abp.appPath variable.
    abp.toAbsAppPath = function (path) {
        if (path.indexOf('/') == 0) {
            path = path.substring(1);
        }

        return abp.appPath + path;
    };

    /* LOCALIZATION ***********************************************/
    //Implements Localization API that simplifies usage of localization scripts generated by Abp.

    abp.localization = abp.localization || {};

    abp.localization.localize = function (key, sourceName) {
        sourceName = sourceName || abp.localization.defaultSourceName;

        var source = abp.localization.values[sourceName];

        if (!source) {
            abp.log.warn('Could not find localization source: ' + sourceName);
            return key;
        }

        var value = source[key];
        if (value == undefined) {
            return key;
        }

        var copiedArguments = Array.prototype.slice.call(arguments, 0);
        copiedArguments.splice(1, 1);
        copiedArguments[0] = value;

        return abp.utils.formatString.apply(this, copiedArguments);
    };

    abp.localization.getSource = function (sourceName) {
        return function (key) {
            var copiedArguments = Array.prototype.slice.call(arguments, 0);
            copiedArguments.splice(1, 0, sourceName);
            return abp.localization.localize.apply(this, copiedArguments);
        };
    };

    abp.localization.isCurrentCulture = function (name) {
        return abp.localization.currentCulture
            && abp.localization.currentCulture.name
            && abp.localization.currentCulture.name.indexOf(name) == 0;
    };

    abp.localization.defaultSourceName = undefined;
    abp.localization.abpWeb = abp.localization.getSource('AbpWeb');

    /* AUTHORIZATION **********************************************/
    //Implements Authorization API that simplifies usage of authorization scripts generated by Abp.

    abp.auth = abp.auth || {};

    abp.auth.allPermissions = abp.auth.allPermissions || {};

    abp.auth.grantedPermissions = abp.auth.grantedPermissions || {};

    //Deprecated. Use abp.auth.isGranted instead.
    abp.auth.hasPermission = function (permissionName) {
        return abp.auth.isGranted.apply(this, arguments);
    };

    //Deprecated. Use abp.auth.isAnyGranted instead.
    abp.auth.hasAnyOfPermissions = function () {
        return abp.auth.isAnyGranted.apply(this, arguments);
    };

    //Deprecated. Use abp.auth.areAllGranted instead.
    abp.auth.hasAllOfPermissions = function () {
        return abp.auth.areAllGranted.apply(this, arguments);
    };

    abp.auth.isGranted = function (permissionName) {
        return abp.auth.allPermissions[permissionName] != undefined && abp.auth.grantedPermissions[permissionName] != undefined;
    };

    abp.auth.isAnyGranted = function () {
        if (!arguments || arguments.length <= 0) {
            return true;
        }

        for (var i = 0; i < arguments.length; i++) {
            if (abp.auth.isGranted(arguments[i])) {
                return true;
            }
        }

        return false;
    };

    abp.auth.areAllGranted = function () {
        if (!arguments || arguments.length <= 0) {
            return true;
        }

        for (var i = 0; i < arguments.length; i++) {
            if (!abp.auth.isGranted(arguments[i])) {
                return false;
            }
        }

        return true;
    };

    /* FEATURE SYSTEM *********************************************/
    //Implements Features API that simplifies usage of feature scripts generated by Abp.

    abp.features = abp.features || {};

    abp.features.allFeatures = abp.features.allFeatures || {};

    abp.features.get = function (name) {
        return abp.features.allFeatures[name];
    }

    abp.features.getValue = function (name) {
        var feature = abp.features.get(name);
        if (feature == undefined) {
            return undefined;
        }

        return feature.value;
    }

    abp.features.isEnabled = function (name) {
        var value = abp.features.getValue(name);
        return value == 'true' || value == 'True';
    }

    /* SETTINGS **************************************************/
    //Implements Settings API that simplifies usage of setting scripts generated by Abp.

    abp.setting = abp.setting || {};

    abp.setting.values = abp.setting.values || {};

    abp.setting.get = function (name) {
        return abp.setting.values[name];
    };

    abp.setting.getBoolean = function (name) {
        var value = abp.setting.get(name);
        return value == 'true' || value == 'True';
    };

    abp.setting.getInt = function (name) {
        return parseInt(abp.setting.values[name]);
    };

    /* REALTIME NOTIFICATIONS ************************************/

    abp.notifications = abp.notifications || {};

    abp.notifications.severity = {
        INFO: 0,
        SUCCESS: 1,
        WARN: 2,
        ERROR: 3,
        FATAL: 4
    };

    abp.notifications.userNotificationState = {
        UNREAD: 0,
        READ: 1
    };

    abp.notifications.getUserNotificationStateAsString = function (userNotificationState) {
        switch (userNotificationState) {
            case abp.notifications.userNotificationState.READ:
                return 'READ';
            case abp.notifications.userNotificationState.UNREAD:
                return 'UNREAD';
            default:
                abp.log.warn('Unknown user notification state value: ' + userNotificationState)
                return '?';
        }
    };

    abp.notifications.getUiNotifyFuncBySeverity = function (severity) {
        switch (severity) {
            case abp.notifications.severity.SUCCESS:
                return abp.notify.success;
            case abp.notifications.severity.WARN:
                return abp.notify.warn;
            case abp.notifications.severity.ERROR:
                return abp.notify.error;
            case abp.notifications.severity.FATAL:
                return abp.notify.error;
            case abp.notifications.severity.INFO:
            default:
                return abp.notify.info;
        }
    };

    abp.notifications.messageFormatters = {};

    abp.notifications.messageFormatters['Abp.Notifications.MessageNotificationData'] = function (userNotification) {
        return userNotification.notification.data.message;
    };

    abp.notifications.messageFormatters['Abp.Notifications.LocalizableMessageNotificationData'] = function (userNotification) {
        var localizedMessage = abp.localization.localize(
            userNotification.notification.data.message.name,
            userNotification.notification.data.message.sourceName
        );

        //TODO: Do not use $ if possible!
        if (userNotification.notification.data.properties) {
            if ($) {
                $.each(userNotification.notification.data.properties, function (key, value) {
                    localizedMessage = localizedMessage.replace('{' + key + '}', value);
                });
            } else {
                //alternative for $.each
                var properties = Object.keys(userNotification.notification.data.properties);
                for (var i = 0; i < properties.length; i++) {
                    localizedMessage = localizedMessage.replace('{' + properties[i] + '}', userNotification.notification.data.properties[properties[i]]);
                }
            }
        }

        return localizedMessage;
    };

    abp.notifications.getFormattedMessageFromUserNotification = function (userNotification) {
        var formatter = abp.notifications.messageFormatters[userNotification.notification.data.type];
        if (!formatter) {
            abp.log.warn('No message formatter defined for given data type: ' + userNotification.notification.data.type)
            return '?';
        }

        if (!abp.utils.isFunction(formatter)) {
            abp.log.warn('Message formatter should be a function! It is invalid for data type: ' + userNotification.notification.data.type)
            return '?';
        }

        return formatter(userNotification);
    }

    /* LOGGING ***************************************************/
    //Implements Logging API that provides secure & controlled usage of console.log

    abp.log = abp.log || {};

    abp.log.levels = {
        DEBUG: 1,
        INFO: 2,
        WARN: 3,
        ERROR: 4,
        FATAL: 5
    };

    abp.log.level = abp.log.levels.DEBUG;

    abp.log.log = function (logObject, logLevel) {
        if (!window.console || !window.console.log) {
            return;
        }

        if (logLevel != undefined && logLevel < abp.log.level) {
            return;
        }

        console.log(logObject);
    };

    abp.log.debug = function (logObject) {
        abp.log.log("DEBUG: ", abp.log.levels.DEBUG);
        abp.log.log(logObject, abp.log.levels.DEBUG);
    };

    abp.log.info = function (logObject) {
        abp.log.log("INFO: ", abp.log.levels.INFO);
        abp.log.log(logObject, abp.log.levels.INFO);
    };

    abp.log.warn = function (logObject) {
        abp.log.log("WARN: ", abp.log.levels.WARN);
        abp.log.log(logObject, abp.log.levels.WARN);
    };

    abp.log.error = function (logObject) {
        abp.log.log("ERROR: ", abp.log.levels.ERROR);
        abp.log.log(logObject, abp.log.levels.ERROR);
    };

    abp.log.fatal = function (logObject) {
        abp.log.log("FATAL: ", abp.log.levels.FATAL);
        abp.log.log(logObject, abp.log.levels.FATAL);
    };

    /* NOTIFICATION *********************************************/
    //Defines Notification API, not implements it

    abp.notify = abp.notify || {};

    abp.notify.success = function (message, title, options) {
        abp.log.warn('abp.notify.success is not implemented!');
    };

    abp.notify.info = function (message, title, options) {
        abp.log.warn('abp.notify.info is not implemented!');
    };

    abp.notify.warn = function (message, title, options) {
        abp.log.warn('abp.notify.warn is not implemented!');
    };

    abp.notify.error = function (message, title, options) {
        abp.log.warn('abp.notify.error is not implemented!');
    };

    /* MESSAGE **************************************************/
    //Defines Message API, not implements it

    abp.message = abp.message || {};

    var showMessage = function (message, title) {
        alert((title || '') + ' ' + message);

        if (!$) {
            abp.log.warn('abp.message can not return promise since jQuery is not defined!');
            return null;
        }

        return $.Deferred(function ($dfd) {
            $dfd.resolve();
        });
    };

    abp.message.info = function (message, title) {
        abp.log.warn('abp.message.info is not implemented!');
        return showMessage(message, title);
    };

    abp.message.success = function (message, title) {
        abp.log.warn('abp.message.success is not implemented!');
        return showMessage(message, title);
    };

    abp.message.warn = function (message, title) {
        abp.log.warn('abp.message.warn is not implemented!');
        return showMessage(message, title);
    };

    abp.message.error = function (message, title) {
        abp.log.warn('abp.message.error is not implemented!');
        return showMessage(message, title);
    };

    abp.message.confirm = function (message, titleOrCallback, callback) {
        abp.log.warn('abp.message.confirm is not implemented!');

        if (titleOrCallback && !(typeof titleOrCallback == 'string')) {
            callback = titleOrCallback;
        }

        var result = confirm(message);
        callback && callback(result);

        if (!$) {
            abp.log.warn('abp.message can not return promise since jQuery is not defined!');
            return null;
        }

        return $.Deferred(function ($dfd) {
            $dfd.resolve();
        });
    };

    /* UI *******************************************************/

    abp.ui = abp.ui || {};

    /* UI BLOCK */
    //Defines UI Block API, not implements it

    abp.ui.block = function (elm) {
        abp.log.warn('abp.ui.block is not implemented!');
    };

    abp.ui.unblock = function (elm) {
        abp.log.warn('abp.ui.unblock is not implemented!');
    };

    /* UI BUSY */
    //Defines UI Busy API, not implements it

    abp.ui.setBusy = function (elm, optionsOrPromise) {
        abp.log.warn('abp.ui.setBusy is not implemented!');
    };

    abp.ui.clearBusy = function (elm) {
        abp.log.warn('abp.ui.clearBusy is not implemented!');
    };

    /* SIMPLE EVENT BUS *****************************************/

    abp.event = (function () {

        var _callbacks = {};

        var on = function (eventName, callback) {
            if (!_callbacks[eventName]) {
                _callbacks[eventName] = [];
            }

            _callbacks[eventName].push(callback);
        };

        var off = function (eventName, callback) {
            var callbacks = _callbacks[eventName];
            if (!callbacks) {
                return;
            }

            var index = -1;
            for (var i = 0; i < callbacks.length; i++) {
                if (callbacks[i] === callback) {
                    index = i;
                    break;
                }
            }

            if (index < 0) {
                return;
            }

            _callbacks[eventName].splice(index, 1);
        };

        var trigger = function (eventName) {
            var callbacks = _callbacks[eventName];
            if (!callbacks || !callbacks.length) {
                return;
            }

            var args = Array.prototype.slice.call(arguments, 1);
            for (var i = 0; i < callbacks.length; i++) {
                callbacks[i].apply(this, args);
            }
        };

        // Public interface ///////////////////////////////////////////////////

        return {
            on: on,
            off: off,
            trigger: trigger
        };
    })();

    /* UTILS ***************************************************/

    abp.utils = abp.utils || {};

    /* Creates a name namespace.
    *  Example:
    *  var taskService = abp.utils.createNamespace(abp, 'services.task');
    *  taskService will be equal to abp.services.task
    *  first argument (root) must be defined first
    ************************************************************/
    abp.utils.createNamespace = function (root, ns) {
        var parts = ns.split('.');
        for (var i = 0; i < parts.length; i++) {
            if (typeof root[parts[i]] == 'undefined') {
                root[parts[i]] = {};
            }

            root = root[parts[i]];
        }

        return root;
    };

    /* Formats a string just like string.format in C#.
    *  Example:
    *  _formatString('Hello {0}','Halil') = 'Hello Halil'
    ************************************************************/
    abp.utils.formatString = function () {
        if (arguments.length < 1) {
            return null;
        }

        var str = arguments[0];

        for (var i = 1; i < arguments.length; i++) {
            var placeHolder = '{' + (i - 1) + '}';
            str = str.replace(placeHolder, arguments[i]);
        }

        return str;
    };

    abp.utils.toPascalCase = function (str) {
        if (!str || !str.length) {
            return str;
        }

        if (str.length === 1) {
            return str.charAt(0).toUpperCase();
        }

        return str.charAt(0).toUpperCase() + str.substr(1);
    }

    abp.utils.toCamelCase = function (str) {
        if (!str || !str.length) {
            return str;
        }

        if (str.length === 1) {
            return str.charAt(0).toLowerCase();
        }

        return str.charAt(0).toLowerCase() + str.substr(1);
    }

    abp.utils.truncateString = function (str, maxLength) {
        if (!str || !str.length || str.length <= maxLength) {
            return str;
        }

        return str.substr(0, maxLength);
    };

    abp.utils.truncateStringWithPostfix = function (str, maxLength, postfix) {
        postfix = postfix || '...';

        if (!str || !str.length || str.length <= maxLength) {
            return str;
        }

        if (maxLength <= postfix.length) {
            return postfix.substr(0, maxLength);
        }

        return str.substr(0, maxLength - postfix.length) + postfix;
    };

    abp.utils.isFunction = function (obj) {
        if ($) {
            return $.isFunction(obj);
        }

        //alternative for $.isFunction
        return !!(obj && obj.constructor && obj.call && obj.apply);
    };

})(jQuery);<|endoftext|>