/*!
 * AmplifyJS 1.1.0 - Core, Store, Request
 *
 * Copyright 2011 appendTo LLC. (http://appendto.com/team)
 * Dual licensed under the MIT or GPL licenses.
 * http://appendto.com/open-source-licenses
 *
 * http://amplifyjs.com
 */
/*!
 * Amplify Core 1.1.0
 *
 * Copyright 2011 appendTo LLC. (http://appendto.com/team)
 * Dual licensed under the MIT or GPL licenses.
 * http://appendto.com/open-source-licenses
 *
 * http://amplifyjs.com
 */
(function( global, undefined ) {

var slice = [].slice,
	subscriptions = {};

var amplify = global.amplify = {
	publish: function( topic ) {
		var args = slice.call( arguments, 1 ),
			topicSubscriptions,
			subscription,
			length,
			i = 0,
			ret;

		if ( !subscriptions[ topic ] ) {
			return true;
		}

		topicSubscriptions = subscriptions[ topic ].slice();
		for ( length = topicSubscriptions.length; i < length; i++ ) {
			subscription = topicSubscriptions[ i ];
			ret = subscription.callback.apply( subscription.context, args );
			if ( ret === false ) {
				break;
			}
		}
		return ret !== false;
	},

	subscribe: function( topic, context, callback, priority ) {
		if ( arguments.length === 3 && typeof callback === "number" ) {
			priority = callback;
			callback = context;
			context = null;
		}
		if ( arguments.length === 2 ) {
			callback = context;
			context = null;
		}
		priority = priority || 10;

		var topicIndex = 0,
			topics = topic.split( /\s/ ),
			topicLength = topics.length,
			added;
		for ( ; topicIndex < topicLength; topicIndex++ ) {
			topic = topics[ topicIndex ];
			added = false;
			if ( !subscriptions[ topic ] ) {
				subscriptions[ topic ] = [];
			}

			var i = subscriptions[ topic ].length - 1,
				subscriptionInfo = {
					callback: callback,
					context: context,
					priority: priority
				};

			for ( ; i >= 0; i-- ) {
				if ( subscriptions[ topic ][ i ].priority <= priority ) {
					subscriptions[ topic ].splice( i + 1, 0, subscriptionInfo );
					added = true;
					break;
				}
			}

			if ( !added ) {
				subscriptions[ topic ].unshift( subscriptionInfo );
			}
		}

		return callback;
	},

	unsubscribe: function( topic, callback ) {
		if ( !subscriptions[ topic ] ) {
			return;
		}

		var length = subscriptions[ topic ].length,
			i = 0;

		for ( ; i < length; i++ ) {
			if ( subscriptions[ topic ][ i ].callback === callback ) {
				subscriptions[ topic ].splice( i, 1 );
				break;
			}
		}
	}
};

}( this ) );
/*!
 * Amplify Store - Persistent Client-Side Storage 1.1.0
 *
 * Copyright 2011 appendTo LLC. (http://appendto.com/team)
 * Dual licensed under the MIT or GPL licenses.
 * http://appendto.com/open-source-licenses
 *
 * http://amplifyjs.com
 */
(function( amplify, undefined ) {

var store = amplify.store = function( key, value, options, type ) {
	var type = store.type;
	if ( options && options.type && options.type in store.types ) {
		type = options.type;
	}
	return store.types[ type ]( key, value, options || {} );
};

store.types = {};
store.type = null;
store.addType = function( type, storage ) {
	if ( !store.type ) {
		store.type = type;
	}

	store.types[ type ] = storage;
	store[ type ] = function( key, value, options ) {
		options = options || {};
		options.type = type;
		return store( key, value, options );
	};
}
store.error = function() {
	return "amplify.store quota exceeded";
};

var rprefix = /^__amplify__/;
function createFromStorageInterface( storageType, storage ) {
	store.addType( storageType, function( key, value, options ) {
		var storedValue, parsed, i, remove,
			ret = value,
			now = (new Date()).getTime();

		if ( !key ) {
			ret = {};
			remove = [];
			i = 0;
			try {
				// accessing the length property works around a localStorage bug
				// in Firefox 4.0 where the keys don't update cross-page
				// we assign to key just to avoid Closure Compiler from removing
				// the access as "useless code"
				// https://bugzilla.mozilla.org/show_bug.cgi?id=662511
				key = storage.length;

				while ( key = storage.key( i++ ) ) {
					if ( rprefix.test( key ) ) {
						parsed = JSON.parse( storage.getItem( key ) );
						if ( parsed.expires && parsed.expires <= now ) {
							remove.push( key );
						} else {
							ret[ key.replace( rprefix, "" ) ] = parsed.data;
						}
					}
				}
				while ( key = remove.pop() ) {
					storage.removeItem( key );
				}
			} catch ( error ) {}
			return ret;
		}

		// protect against name collisions with direct storage
		key = "__amplify__" + key;

		if ( value === undefined ) {
			storedValue = storage.getItem( key );
			parsed = storedValue ? JSON.parse( storedValue ) : { expires: -1 };
			if ( parsed.expires && parsed.expires <= now ) {
				storage.removeItem( key );
			} else {
				return parsed.data;
			}
		} else {
			if ( value === null ) {
				storage.removeItem( key );
			} else {
				parsed = JSON.stringify({
					data: value,
					expires: options.expires ? now + options.expires : null
				});
				try {
					storage.setItem( key, parsed );
				// quota exceeded
				} catch( error ) {
					// expire old data and try again
					store[ storageType ]();
					try {
						storage.setItem( key, parsed );
					} catch( error ) {
						throw store.error();
					}
				}
			}
		}

		return ret;
	});
}

// localStorage + sessionStorage
// IE 8+, Firefox 3.5+, Safari 4+, Chrome 4+, Opera 10.5+, iPhone 2+, Android 2+
for ( var webStorageType in { localStorage: 1, sessionStorage: 1 } ) {
	// try/catch for file protocol in Firefox
	try {
		if ( window[ webStorageType ].getItem ) {
			createFromStorageInterface( webStorageType, window[ webStorageType ] );
		}
	} catch( e ) {}
}

// globalStorage
// non-standard: Firefox 2+
// https://developer.mozilla.org/en/dom/storage#globalStorage
if ( window.globalStorage ) {
	// try/catch for file protocol in Firefox
	try {
		createFromStorageInterface( "globalStorage",
			window.globalStorage[ window.location.hostname ] );
		// Firefox 2.0 and 3.0 have sessionStorage and globalStorage
		// make sure we default to globalStorage
		// but don't default to globalStorage in 3.5+ which also has localStorage
		if ( store.type === "sessionStorage" ) {
			store.type = "globalStorage";
		}
	} catch( e ) {}
}

// userData
// non-standard: IE 5+
// http://msdn.microsoft.com/en-us/library/ms531424(v=vs.85).aspx
(function() {
	// IE 9 has quirks in userData that are a huge pain
	// rather than finding a way to detect these quirks
	// we just don't register userData if we have localStorage
	if ( store.types.localStorage ) {
		return;
	}

	// append to html instead of body so we can do this from the head
	var div = document.createElement( "div" ),
		attrKey = "amplify";
	div.style.display = "none";
	document.getElementsByTagName( "head" )[ 0 ].appendChild( div );

	// we can't feature detect userData support
	// so just try and see if it fails
	// surprisingly, even just adding the behavior isn't enough for a failure
	// so we need to load the data as well
	try {
		div.addBehavior( "#default#userdata" );
		div.load( attrKey );
	} catch( e ) {
		div.parentNode.removeChild( div );
		return;
	}

	store.addType( "userData", function( key, value, options ) {
		div.load( attrKey );
		var attr, parsed, prevValue, i, remove,
			ret = value,
			now = (new Date()).getTime();

		if ( !key ) {
			ret = {};
			remove = [];
			i = 0;
			while ( attr = div.XMLDocument.documentElement.attributes[ i++ ] ) {
				parsed = JSON.parse( attr.value );
				if ( parsed.expires && parsed.expires <= now ) {
					remove.push( attr.name );
				} else {
					ret[ attr.name ] = parsed.data;
				}
			}
			while ( key = remove.pop() ) {
				div.removeAttribute( key );
			}
			div.save( attrKey );
			return ret;
		}

		// convert invalid characters to dashes
		// http://www.w3.org/TR/REC-xml/#NT-Name
		// simplified to assume the starting character is valid
		// also removed colon as it is invalid in HTML attribute names
		key = key.replace( /[^-._0-9A-Za-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u37f-\u1fff\u200c-\u200d\u203f\u2040\u2070-\u218f]/g, "-" );

		if ( value === undefined ) {
			attr = div.getAttribute( key );
			parsed = attr ? JSON.parse( attr ) : { expires: -1 };
			if ( parsed.expires && parsed.expires <= now ) {
				div.removeAttribute( key );
			} else {
				return parsed.data;
			}
		} else {
			if ( value === null ) {
				div.removeAttribute( key );
			} else {
				// we need to get the previous value in case we need to rollback
				prevValue = div.getAttribute( key );
				parsed = JSON.stringify({
					data: value,
					expires: (options.expires ? (now + options.expires) : null)
				});
				div.setAttribute( key, parsed );
			}
		}

		try {
			div.save( attrKey );
		// quota exceeded
		} catch ( error ) {
			// roll the value back to the previous value
			if ( prevValue === null ) {
				div.removeAttribute( key );
			} else {
				div.setAttribute( key, prevValue );
			}

			// expire old data and try again
			store.userData();
			try {
				div.setAttribute( key, parsed );
				div.save( attrKey );
			} catch ( error ) {
				// roll the value back to the previous value
				if ( prevValue === null ) {
					div.removeAttribute( key );
				} else {
					div.setAttribute( key, prevValue );
				}
				throw store.error();
			}
		}
		return ret;
	});
}() );

// in-memory storage
// fallback for all browsers to enable the API even if we can't persist data
(function() {
	var memory = {},
		timeout = {};

	function copy( obj ) {
		return obj === undefined ? undefined : JSON.parse( JSON.stringify( obj ) );
	}

	store.addType( "memory", function( key, value, options ) {
		if ( !key ) {
			return copy( memory );
		}

		if ( value === undefined ) {
			return copy( memory[ key ] );
		}

		if ( timeout[ key ] ) {
			clearTimeout( timeout[ key ] );
			delete timeout[ key ];
		}

		if ( value === null ) {
			delete memory[ key ];
			return null;
		}

		memory[ key ] = value;
		if ( options.expires ) {
			timeout[ key ] = setTimeout(function() {
				delete memory[ key ];
				delete timeout[ key ];
			}, options.expires );
		}

		return value;
	});
}() );

}( this.amplify = this.amplify || {} ) );
/*!
 * Amplify Request 1.1.0
 *
 * Copyright 2011 appendTo LLC. (http://appendto.com/team)
 * Dual licensed under the MIT or GPL licenses.
 * http://appendto.com/open-source-licenses
 *
 * http://amplifyjs.com
 */
(function( amplify, undefined ) {

function noop() {}
function isFunction( obj ) {
	return ({}).toString.call( obj ) === "[object Function]";
}

function async( fn ) {
	var isAsync = false;
	setTimeout(function() {
		isAsync = true;
	}, 1 );
	return function() {
		var that = this,
			args = arguments;
		if ( isAsync ) {
			fn.apply( that, args );
		} else {
			setTimeout(function() {
				fn.apply( that, args );
			}, 1 );
		}
	};
}

amplify.request = function( resourceId, data, callback ) {
	// default to an empty hash just so we can handle a missing resourceId
	// in one place
	var settings = resourceId || {};

	if ( typeof settings === "string" ) {
		if ( isFunction( data ) ) {
			callback = data;
			data = {};
		}
		settings = {
			resourceId: resourceId,
			data: data || {},
			success: callback
		};
	}

	var request = { abort: noop },
		resource = amplify.request.resources[ settings.resourceId ],
		success = settings.success || noop,
		error = settings.error || noop;
	settings.success = async( function( data, status ) {
		status = status || "success";
		amplify.publish( "request.success", settings, data, status );
		amplify.publish( "request.complete", settings, data, status );
		success( data, status );
	});
	settings.error = async( function( data, status ) {
		status = status || "error";
		amplify.publish( "request.error", settings, data, status );
		amplify.publish( "request.complete", settings, data, status );
		error( data, status );
	});

	if ( !resource ) {
		if ( !settings.resourceId ) {
			throw "amplify.request: no resourceId provided";
		}
		throw "amplify.request: unknown resourceId: " + settings.resourceId;
	}

	if ( !amplify.publish( "request.before", settings ) ) {
		settings.error( null, "abort" );
		return;
	}

	amplify.request.resources[ settings.resourceId ]( settings, request );
	return request;
};

amplify.request.types = {};
amplify.request.resources = {};
amplify.request.define = function( resourceId, type, settings ) {
	if ( typeof type === "string" ) {
		if ( !( type in amplify.request.types ) ) {
			throw "amplify.request.define: unknown type: " + type;
		}

		settings.resourceId = resourceId;
		amplify.request.resources[ resourceId ] =
			amplify.request.types[ type ]( settings );
	} else {
		// no pre-processor or settings for one-off types (don't invoke)
		amplify.request.resources[ resourceId ] = type;
	}
};

}( amplify ) );

(function( amplify, $, undefined ) {

var xhrProps = [ "status", "statusText", "responseText", "responseXML", "readyState" ],
    rurlData = /\{([^\}]+)\}/g;

amplify.request.types.ajax = function( defnSettings ) {
	defnSettings = $.extend({
		type: "GET"
	}, defnSettings );

	return function( settings, request ) {
		var xhr,
			url = defnSettings.url,
			abort = request.abort,
			ajaxSettings = $.extend( true, {}, defnSettings, { data: settings.data } ),
			aborted = false,
			ampXHR = {
				readyState: 0,
				setRequestHeader: function( name, value ) {
					return xhr.setRequestHeader( name, value );
				},
				getAllResponseHeaders: function() {
					return xhr.getAllResponseHeaders();
				},
				getResponseHeader: function( key ) {
					return xhr.getResponseHeader( key );
				},
				overrideMimeType: function( type ) {
					return xhr.overrideMideType( type );
				},
				abort: function() {
					aborted = true;
					try {
						xhr.abort();
					// IE 7 throws an error when trying to abort
					} catch( e ) {}
					handleResponse( null, "abort" );
				},
				success: function( data, status ) {
					settings.success( data, status );
				},
				error: function( data, status ) {
					settings.error( data, status );
				}
			};

		amplify.publish( "request.ajax.preprocess",
			defnSettings, settings, ajaxSettings, ampXHR );

		$.extend( ajaxSettings, {
			success: function( data, status ) {
				handleResponse( data, status );
			},
			error: function( _xhr, status ) {
				handleResponse( null, status );
			},
			beforeSend: function( _xhr, _ajaxSettings ) {
				xhr = _xhr;
				ajaxSettings = _ajaxSettings;
				var ret = defnSettings.beforeSend ?
					defnSettings.beforeSend.call( this, ampXHR, ajaxSettings ) : true;
				return ret && amplify.publish( "request.before.ajax",
					defnSettings, settings, ajaxSettings, ampXHR );
			}
		});
		$.ajax( ajaxSettings );

		function handleResponse( data, status ) {
			$.each( xhrProps, function( i, key ) {
				try {
					ampXHR[ key ] = xhr[ key ];
				} catch( e ) {}
			});
			// Playbook returns "HTTP/1.1 200 OK"
			// TODO: something also returns "OK", what?
			if ( /OK$/.test( ampXHR.statusText ) ) {
				ampXHR.statusText = "success";
			}
			if ( data === undefined ) {
				// TODO: add support for ajax errors with data
				data = null;
			}
			if ( aborted ) {
				status = "abort";
			}
			if ( /timeout|error|abort/.test( status ) ) {
				ampXHR.error( data, status );
			} else {
				ampXHR.success( data, status );
			}
			// avoid handling a response multiple times
			// this can happen if a request is aborted
			// TODO: figure out if this breaks polling or multi-part responses
			handleResponse = $.noop;
		}

		request.abort = function() {
			ampXHR.abort();
			abort.call( this );
		};
	};
};

amplify.subscribe( "request.ajax.preprocess", function( defnSettings, settings, ajaxSettings ) {
	var mappedKeys = [],
		data = ajaxSettings.data;

	if ( typeof data === "string" ) {
		return;
	}

	data = $.extend( true, {}, defnSettings.data, data );

	ajaxSettings.url = ajaxSettings.url.replace( rurlData, function ( m, key ) {
		if ( key in data ) {
		    mappedKeys.push( key );
		    return data[ key ];
		}
	});

	// We delete the keys later so duplicates are still replaced
	$.each( mappedKeys, function ( i, key ) {
		delete data[ key ];
	});

	ajaxSettings.data = data;
});

amplify.subscribe( "request.ajax.preprocess", function( defnSettings, settings, ajaxSettings ) {
	var data = ajaxSettings.data,
		dataMap = defnSettings.dataMap;

	if ( !dataMap || typeof data === "string" ) {
		return;
	}

	if ( $.isFunction( dataMap ) ) {
		ajaxSettings.data = dataMap( data );
	} else {
		$.each( defnSettings.dataMap, function( orig, replace ) {
			if ( orig in data ) {
				data[ replace ] = data[ orig ];
				delete data[ orig ];
			}
		});
		ajaxSettings.data = data;
	}
});

var cache = amplify.request.cache = {
	_key: function( resourceId, url, data ) {
		data = url + data;
		var length = data.length,
			i = 0,
			checksum = chunk();

		while ( i < length ) {
			checksum ^= chunk();
		}

		function chunk() {
			return data.charCodeAt( i++ ) << 24 |
				data.charCodeAt( i++ ) << 16 |
				data.charCodeAt( i++ ) << 8 |
				data.charCodeAt( i++ ) << 0;
		}

		return "request-" + resourceId + "-" + checksum;
	},

	_default: (function() {
		var memoryStore = {};
		return function( resource, settings, ajaxSettings, ampXHR ) {
			// data is already converted to a string by the time we get here
			var cacheKey = cache._key( settings.resourceId,
					ajaxSettings.url, ajaxSettings.data ),
				duration = resource.cache;

			if ( cacheKey in memoryStore ) {
				ampXHR.success( memoryStore[ cacheKey ] );
				return false;
			}
			var success = ampXHR.success;
			ampXHR.success = function( data ) {
				memoryStore[ cacheKey ] = data;
				if ( typeof duration === "number" ) {
					setTimeout(function() {
						delete memoryStore[ cacheKey ];
					}, duration );
				}
				success.apply( this, arguments );
			};
		};
	}())
};

if ( amplify.store ) {
	$.each( amplify.store.types, function( type ) {
		cache[ type ] = function( resource, settings, ajaxSettings, ampXHR ) {
			var cacheKey = cache._key( settings.resourceId,
					ajaxSettings.url, ajaxSettings.data ),
				cached = amplify.store[ type ]( cacheKey );

			if ( cached ) {
				ajaxSettings.success( cached );
				return false;
			}
			var success = ampXHR.success;
			ampXHR.success = function( data ) {
				amplify.store[ type ]( cacheKey, data, { expires: resource.cache.expires } );
				success.apply( this, arguments );
			};
		};
	});
	cache.persist = cache[ amplify.store.type ];
}

amplify.subscribe( "request.before.ajax", function( resource ) {
	var cacheType = resource.cache;
	if ( cacheType ) {
		// normalize between objects and strings/booleans/numbers
		cacheType = cacheType.type || cacheType;
		return cache[ cacheType in cache ? cacheType : "_default" ]
			.apply( this, arguments );
	}
});

amplify.request.decoders = {
	// http://labs.omniti.com/labs/jsend
	jsend: function( data, status, ampXHR, success, error ) {
		if ( data.status === "success" ) {
			success( data.data );
		} else if ( data.status === "fail" ) {
			error( data.data, "fail" );
		} else if ( data.status === "error" ) {
			delete data.status;
			error( data, "error" );
		}
	}
};

amplify.subscribe( "request.before.ajax", function( resource, settings, ajaxSettings, ampXHR ) {
	var _success = ampXHR.success,
		_error = ampXHR.error,
		decoder = $.isFunction( resource.decoder )
			? resource.decoder
			: resource.decoder in amplify.request.decoders
				? amplify.request.decoders[ resource.decoder ]
				: amplify.request.decoders._default;

	if ( !decoder ) {
		return;
	}

	function success( data, status ) {
		_success( data, status );
	}
	function error( data, status ) {
		_error( data, status );
	}
	ampXHR.success = function( data, status ) {
		decoder( data, status, ampXHR, success, error );
	};
	ampXHR.error = function( data, status ) {
		decoder( data, status, ampXHR, success, error );
	};
});

}( amplify, jQuery ) );


false /* isSimulation */, userId, setUserId);

var ret = Meteor._CurrentInvocation.withValue(invocation, function () {

keep keep keep keep replace replace keep keep keep keep keep

var invocation = new Meteor._MethodInvocation({

@red: #842210;

h1 {
    color: @red;
}

img {
    display: block;
    position: absolute;
    top: 0px;
    right: 0px;
}


// webFrame.setSpellCheckProvider('en-US', true, new SpellCheckProvider('en-US').on('misspelling', function (suggestions) {

keep keep keep keep add keep keep keep keep

// prevent drag image or other file to Mark Text, and open it by Chrome default behavior.

window.addEventListener('dragover', function (e) {

window.addEventListener('drop', function (e) {

:root {
  --side-bar-bg-color: #fafafa;
  --control-text-color: #777;
}

@include-when-export url(http://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,700,400&subset=latin,latin-ext);

html, body {
  font-size: 16px;
}

body {
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  color: rgb(217, 217, 217);
  line-height: 1.6;
}

::-webkit-scrollbar {
  background: rgb(43, 43, 43);
}

::-webkit-scrollbar-thumb {
  background: rgb(55, 55, 55);
}

#ag-editor-id {
  max-width: 860px;
  margin: 0 auto;
  padding: 20px 30px 40px 30px;
  padding-top: 20px;
  padding-bottom: 100px;
}

#ag-editor-id, [contenteditable] {
  outline: none;
  caret-color: #efefef;
}

#ag-editor-id>ul:first-child,
#ag-editor-id>ol:first-child {
  margin-top: 30px;
}

.ag-float-box {
  background: #303133;
  border: 1px solid #303133;
}
.ag-float-item {
  color: #909399;
}

.ag-float-item-active {
  background: #606266;
  color: #C0C4CC;
}

.ag-table-picker {
  background: #606266;
  border: 1px solid #606266;
}

.ag-table-picker::before {
  background: #606266;
  border: 1px solid #606266;
  border-right: none;
  border-bottom: none;
}

.ag-gray {
  color: #909399;
  text-decoration: none;
  letter-spacing: .15rem;
}

.el-dialog {
  background: rgb(43, 43, 43);
}

.v-modal {
  background: rgba(0, 0, 0, .9);
}

body>*:first-child {
  margin-top: 0 !important;
}

body>*:last-child {
  margin-bottom: 0 !important;
}

a {
  color: #4183C4;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  position: relative;
  margin-top: 1rem;
  margin-bottom: 1rem;
  font-weight: bold;
  line-height: 1.4;
  cursor: text;
}

h1:hover a.anchor,
h2:hover a.anchor,
h3:hover a.anchor,
h4:hover a.anchor,
h5:hover a.anchor,
h6:hover a.anchor {
  /*background: url("../../images/modules/styleguide/para.png") no-repeat 10px center;*/
  text-decoration: none;
}

h1 tt,
h1 code {
  font-size: inherit;
}

h2 tt,
h2 code {
  font-size: inherit;
}

h3 tt,
h3 code {
  font-size: inherit;
}

h4 tt,
h4 code {
  font-size: inherit;
}

h5 tt,
h5 code {
  font-size: inherit;
}

h6 tt,
h6 code {
  font-size: inherit;
}

h1 {
  padding-bottom: .3em;
  font-size: 2.25em;
  line-height: 1.2;
  border-bottom: 1px solid #eee;
}

h2 {
  padding-bottom: .3em;
  font-size: 1.75em;
  line-height: 1.225;
  border-bottom: 1px solid #eee;
}

h3 {
  font-size: 1.5em;
  line-height: 1.43;
}

h4 {
  font-size: 1.25em;
}

h5 {
  font-size: 1em;
}

h6 {
  font-size: 1em;
  color: #777;
}

p,
blockquote,
ul,
ol,
dl,
table {
  margin: 0.8em 0;
}

li>ol,
li>ul {
  margin: 0 0;
}

hr {
  height: 4px;
  padding: 0;
  margin: 16px 0;
  background-color: #e7e7e7;
  border: 0 none;
  overflow: hidden;
  box-sizing: content-box;
  border-bottom: 1px solid #ddd;
}

body>h2:first-child {
  margin-top: 0;
  padding-top: 0;
}

body>h1:first-child {
  margin-top: 0;
  padding-top: 0;
}

body>h1:first-child+h2 {
  margin-top: 0;
  padding-top: 0;
}

body>h3:first-child,
body>h4:first-child,
body>h5:first-child,
body>h6:first-child {
  margin-top: 0;
  padding-top: 0;
}

a:first-child h1,
a:first-child h2,
a:first-child h3,
a:first-child h4,
a:first-child h5,
a:first-child h6 {
  margin-top: 0;
  padding-top: 0;
}

h1 p,
h2 p,
h3 p,
h4 p,
h5 p,
h6 p {
  margin-top: 0;
}

li p.first {
  display: inline-block;
}

ul,
ol {
  padding-left: 30px;
}

ul:first-child,
ol:first-child {
  margin-top: 0;
}

ul:last-child,
ol:last-child {
  margin-bottom: 0;
}

blockquote {
  border-left: 4px solid #606266;
  padding: 0 15px;
  color: #999999;
}

blockquote blockquote {
  padding-right: 0;
}

table {
  padding: 0;
  word-break: initial;
}

table tr {
  margin: 0;
  padding: 0;
}

table thead tr,
table tr:nth-child(2n) {
  background-color: #303133;
}

table tr th {
  font-weight: bold;
  border-top: 2px solid #606266;
  border-bottom: 2px solid #606266;
  text-align: left;
  margin: 0;
  padding: 6px 13px;
}

table tr td {
  border-bottom: 1px solid #606266;
  text-align: left;
  margin: 0;
  padding: 6px 13px;
}

table tr th:first-child,
table tr td:first-child {
  margin-top: 0;
}

table tr th:last-child,
table tr td:last-child {
  margin-bottom: 0;
}

.CodeMirror-gutters {
  border-right: 1px solid #ddd;
}

code,
tt {
  border: 1px solid #ddd;
  background-color: #606266;
  border-radius: 3px;
  padding: 0;
  font-family: Consolas, "Liberation Mono", Courier, monospace;
  padding: 2px 4px 0px 4px;
  font-size: 0.9em;
}

/* custom add */
code {
  border: none;
  padding: 2px 4px;
  font-size: 90%;
  color: #efefef;
  background-color: #606266;
  border-radius: 4px;
  font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
}

@media print {
  html {
    font-size: 13px;
  }
  table,
  pre {
    page-break-inside: avoid;
  }
  pre {
    word-wrap: break-word;
  }
}

#ag-editor-id pre.ag-code-block {
  padding: .4rem 1rem;
  font-size: 90%;
  line-height: 1.6;
  border: 1px solid #606266;
  border-radius: 3px;
  color: #777777;
  margin-top: 0;
}


| Q                | A
| ---------------- | ---
| Bug fix?         | yes/no
| New feature?     | yes/no
| BC breaks?       | yes/no
| Deprecations?    | yes/no
| New tests added? | yes/not needed
| Fixed tickets    | comma-separated list of tickets fixed by the PR, if any
| License          | MIT

### Description

[Description of the bug or feature]

--

#### Please, don't submit `/dist` files with your PR!


import { autoUpdater } from 'electron-updater'
import { ipcMain } from 'electron'

let updater
let win
autoUpdater.autoDownload = false

autoUpdater.on('error', error => {
  if (win) {
    win.webContents.send('AGANI::UPDATE_ERROR', error === null ? 'Error: unknown' : (error.stack || error).toString())
  }
})

ipcMain.on('AGANI::NEED_UPDATE', (e, { needUpdate }) => {
  if (needUpdate) {
    autoUpdater.downloadUpdate()
  } else {
    updater.enabled = true
    updater = null
  }
})

autoUpdater.on('update-available', () => {
  if (win) {
    win.webContents.send('AGANI::UPDATE_AVAILABLE', 'Found updates, do you want update now?')
  }
  updater.enabled = true
  updater = null
})

autoUpdater.on('update-not-available', () => {
  if (win) {
    win.webContents.send('AGANI::UPDATE_NOT_AVAILABLE', 'Current version is up-to-date.')
  }
  updater.enabled = true
  updater = null
})

autoUpdater.on('update-downloaded', () => {
  if (win) {
    win.webContents.send('AGANI::UPDATE_DOWNLOADED', 'Updates downloaded, application will be quit for update...')
  }
  setImmediate(() => autoUpdater.quitAndInstall())
})

export const checkUpdates = (menuItem, browserWindow) => {
  updater = menuItem
  win = browserWindow
  updater.enabled = false
  autoUpdater.checkForUpdates()
}


import * as actions from '../actions/theme'

export default {
  label: 'Theme',
  submenu: [{
    label: 'Dark',
    type: 'radio',
    checked: false,
    click (menuItem, browserWindow) {
      actions.selectTheme(browserWindow, 'dark')
    }
  }, {
    label: 'Light',
    type: 'radio',
    checked: true,
    click (menuItem, browserWindow) {
      actions.selectTheme(browserWindow, 'light')
    }
  }]
}


}
.ag-gray {
color: #909399;
text-decoration: none;
letter-spacing: .15rem;
}
.el-dialog {

keep keep keep keep keep replace keep keep keep

:root {
  --side-bar-bg-color: #fafafa;
  --control-text-color: #777;
}

@include-when-export url(http://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,700,400&subset=latin,latin-ext);

html, body {
  font-size: 16px;
  background: rgb(252, 252, 252);
}

body {
  font-family: "Open Sans", "Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
  color: #303133;
  line-height: 1.6;
}

::-webkit-scrollbar {
  background: rgb(252, 252, 252);
}

::-webkit-scrollbar-thumb {
  background: #EBEEF5;
}

#ag-editor-id {
  max-width: 860px;
  margin: 0 auto;
  padding: 20px 30px 40px 30px;
  padding-top: 20px;
  padding-bottom: 100px;
}

#ag-editor-id, [contenteditable] {
  outline: none;
  caret-color: #000000;
}

#ag-editor-id>ul:first-child,
#ag-editor-id>ol:first-child {
  margin-top: 30px;
}

.ag-gray {
  color: #E4E7ED;
  text-decoration: none;
  letter-spacing: .15rem;
}

.v-modal {
  background: #fff;
  opacity: .8;
}

body>*:first-child {
  margin-top: 0 !important;
}

body>*:last-child {
  margin-bottom: 0 !important;
}

a {
  color: #4183C4;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  position: relative;
  margin-top: 1rem;
  margin-bottom: 1rem;
  font-weight: bold;
  line-height: 1.4;
  cursor: text;
}

h1:hover a.anchor,
h2:hover a.anchor,
h3:hover a.anchor,
h4:hover a.anchor,
h5:hover a.anchor,
h6:hover a.anchor {
  /*background: url("../../images/modules/styleguide/para.png") no-repeat 10px center;*/
  text-decoration: none;
}

h1 tt,
h1 code {
  font-size: inherit;
}

h2 tt,
h2 code {
  font-size: inherit;
}

h3 tt,
h3 code {
  font-size: inherit;
}

h4 tt,
h4 code {
  font-size: inherit;
}

h5 tt,
h5 code {
  font-size: inherit;
}

h6 tt,
h6 code {
  font-size: inherit;
}

h1 {
  padding-bottom: .3em;
  font-size: 2.25em;
  line-height: 1.2;
  border-bottom: 1px solid #eee;
}

h2 {
  padding-bottom: .3em;
  font-size: 1.75em;
  line-height: 1.225;
  border-bottom: 1px solid #eee;
}

h3 {
  font-size: 1.5em;
  line-height: 1.43;
}

h4 {
  font-size: 1.25em;
}

h5 {
  font-size: 1em;
}

h6 {
  font-size: 1em;
  color: #777;
}

p,
blockquote,
ul,
ol,
dl,
table {
  margin: 0.8em 0;
}

li>ol,
li>ul {
  margin: 0 0;
}

hr {
  height: 4px;
  padding: 0;
  margin: 16px 0;
  background-color: #e7e7e7;
  border: 0 none;
  overflow: hidden;
  box-sizing: content-box;
  border-bottom: 1px solid #ddd;
}

body>h2:first-child {
  margin-top: 0;
  padding-top: 0;
}

body>h1:first-child {
  margin-top: 0;
  padding-top: 0;
}

body>h1:first-child+h2 {
  margin-top: 0;
  padding-top: 0;
}

body>h3:first-child,
body>h4:first-child,
body>h5:first-child,
body>h6:first-child {
  margin-top: 0;
  padding-top: 0;
}

a:first-child h1,
a:first-child h2,
a:first-child h3,
a:first-child h4,
a:first-child h5,
a:first-child h6 {
  margin-top: 0;
  padding-top: 0;
}

h1 p,
h2 p,
h3 p,
h4 p,
h5 p,
h6 p {
  margin-top: 0;
}

li p.first {
  display: inline-block;
}

ul,
ol {
  padding-left: 30px;
}

ul:first-child,
ol:first-child {
  margin-top: 0;
}

ul:last-child,
ol:last-child {
  margin-bottom: 0;
}

blockquote {
  border-left: 4px solid #dddddd;
  padding: 0 15px;
  color: #777777;
}

blockquote blockquote {
  padding-right: 0;
}

table {
  padding: 0;
  word-break: initial;
}

table tr {
  margin: 0;
  padding: 0;
}

table thead tr,
table tr:nth-child(2n) {
  background-color: #fafafa;
}

table tr th {
  font-weight: bold;
  border: 1px solid #ebeef5;
  text-align: left;
  margin: 0;
  padding: 6px 13px;
}

table tr td {
  border: 1px solid #ebeef5;
  text-align: left;
  margin: 0;
  padding: 6px 13px;
}

table tr th:first-child,
table tr td:first-child {
  margin-top: 0;
}

table tr th:last-child,
table tr td:last-child {
  margin-bottom: 0;
}

.CodeMirror-gutters {
  border-right: 1px solid #ddd;
}

code,
tt {
  border: 1px solid #ddd;
  background-color: #f8f8f8;
  border-radius: 3px;
  padding: 0;
  font-family: Consolas, "Liberation Mono", Courier, monospace;
  padding: 2px 4px 0px 4px;
  font-size: 0.9em;
}

/* custom add */
code {
  border: none;
  padding: 2px 4px;
  font-size: 90%;
  color: #c7254e;
  background-color: #f9f2f4;
  border-radius: 4px;
  font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
}

@media print {
  html {
    font-size: 13px;
  }
  table,
  pre {
    page-break-inside: avoid;
  }
  pre {
    word-wrap: break-word;
  }
}

#ag-editor-id pre.ag-code-block {
  padding: .4rem 1rem;
  font-size: 90%;
  line-height: 1.6;
  background-color: #f6f8fa;
  border: 0;
  border-radius: 3px;
  color: #777777;
  margin-top: 0;
}


### Description

[Description of the bug or feature]

### Steps to reproduce

1. [First step]
2. [Second step]
3. [and so on...]

**Expected behavior:** [What you expected to happen]

**Actual behavior:** [What actually happened]

**Link to an example:** [If you're reporting a bug that's not reproducible, please try to reproduce it on [JSFiddle](https://jsfiddle.net/), [JS Bin](https://jsbin.com), [CodePen](http://codepen.io/) or a similar service and paste a link here]

### Versions

- Mark Text:
- OS:


    <input type="text" class="row-input">
    ×
    <input type="text" class="column-input">
    <button>确定</button>


.ag-gray {
color: #E4E7ED;
text-decoration: none;
letter-spacing: .15rem;
}
.v-modal {

keep keep keep keep replace keep keep keep


import { LOWERCASE_TAGS, CLASS_OR_ID } from '../config'
import { conflict, isLengthEven, union, isEven, getIdWithoutSet, loadImage, getImageSrc } from '../utils'
import { insertAfter, operateClassName } from '../utils/domManipulate.js'
import { tokenizer } from './parse'
import { validEmoji } from '../emojis'

const snabbdom = require('snabbdom')
const patch = snabbdom.init([ // Init patch function with chosen modules
  require('snabbdom/modules/class').default, // makes it easy to toggle classes
  require('snabbdom/modules/attributes').default,
  require('snabbdom/modules/props').default, // for setting properties on DOM elements
  require('snabbdom/modules/dataset').default
])
const h = require('snabbdom/h').default // helper function for creating vnodes
const toVNode = require('snabbdom/tovnode').default

class StateRender {
  constructor () {
    this.container = null
    this.loadImageMap = new Map()
  }

  setContainer (container) {
    this.container = container
  }

  checkConflicted (block, token, cursor) {
    const { start, end } = cursor
    const key = block.key
    const { start: tokenStart, end: tokenEnd } = token.range

    if (key !== start.key && key !== end.key) {
      return false
    } else if (key === start.key && key !== end.key) {
      return conflict([tokenStart, tokenEnd], [start.offset, start.offset])
    } else if (key !== start.key && key === end.key) {
      return conflict([tokenStart, tokenEnd], [end.offset, end.offset])
    } else {
      return conflict([tokenStart, tokenEnd], [start.offset, start.offset]) ||
        conflict([tokenStart, tokenEnd], [end.offset, end.offset])
    }
  }

  getClassName (outerClass, block, token, cursor) {
    return outerClass || (this.checkConflicted(block, token, cursor) ? CLASS_OR_ID['AG_GRAY'] : CLASS_OR_ID['AG_HIDE'])
  }

  getHighlightClassName (active) {
    return active ? CLASS_OR_ID['AG_HIGHLIGHT'] : CLASS_OR_ID['AG_SELECTION']
  }

  /**
   * [render]: 2 steps:
   * render vdom
   */
  render (blocks, cursor, activeBlocks, matches) {
    const selector = `${LOWERCASE_TAGS.div}#${CLASS_OR_ID['AG_EDITOR_ID']}`

    const renderBlock = block => {
      const type = block.type === 'hr' ? 'p' : block.type
      const isActive = activeBlocks.some(b => b.key === block.key) || block.key === cursor.start.key

      let blockSelector = isActive
        ? `${type}#${block.key}.${CLASS_OR_ID['AG_PARAGRAPH']}.${CLASS_OR_ID['AG_ACTIVE']}`
        : `${type}#${block.key}.${CLASS_OR_ID['AG_PARAGRAPH']}`

      const data = {
        attrs: {},
        dataset: {}
      }

      if (block.children.length) {
        if (/div/.test(block.type) && !block.editable) {
          blockSelector += `.${CLASS_OR_ID['AG_TABLE_TOOL_BAR']}`
          Object.assign(data.attrs, { contenteditable: 'false' })
        }
        if (/ul|ol/.test(block.type) && block.listType) {
          switch (block.listType) {
            case 'order':
              blockSelector += `.${CLASS_OR_ID['AG_ORDER_LIST']}`
              break
            case 'bullet':
              blockSelector += `.${CLASS_OR_ID['AG_BULLET_LIST']}`
              break
            case 'task':
              blockSelector += `.${CLASS_OR_ID['AG_TASK_LIST']}`
              break
            default:
              break
          }
        }
        if (block.type === 'li' && block.label) {
          const { label } = block
          const { align } = activeBlocks[0]

          if (align && block.label === align) {
            blockSelector += '.active'
          }
          Object.assign(data.dataset, { label })
        }
        if (block.type === 'li' && block.listItemType) {
          switch (block.listItemType) {
            case 'order':
              blockSelector += `.${CLASS_OR_ID['AG_ORDER_LIST_ITEM']}`
              break
            case 'bullet':
              blockSelector += `.${CLASS_OR_ID['AG_BULLET_LIST_ITEM']}`
              break
            case 'task':
              blockSelector += `.${CLASS_OR_ID['AG_TASK_LIST_ITEM']}`
              break
            default:
              break
          }
        }
        if (block.type === 'ol') {
          Object.assign(data.attrs, { start: block.start })
        }

        return h(blockSelector, data, block.children.map(child => renderBlock(child)))
      } else {
        // highlight search key in block
        const highlights = matches.filter(m => m.key === block.key)
        let children = block.text
          ? tokenizer(block.text, highlights).reduce((acc, token) => {
            const chunk = this[token.type](h, cursor, block, token)
            return Array.isArray(chunk) ? [...acc, ...chunk] : [...acc, chunk]
          }, [])
          : [ h(LOWERCASE_TAGS.br) ]

        if (/th|td/.test(block.type)) {
          const { align } = block
          if (align) {
            Object.assign(data.attrs, { style: `text-align:${align}` })
          }
        }

        if (/svg/.test(block.type)) {
          const { icon } = block
          blockSelector += '.icon'
          Object.assign(data.attrs, { 'aria-hidden': 'true' })
          children = [
            h('use', {
              attrs: { 'xlink:href': `#${icon}` }
            })
          ]
        }
        if (/^h\d$/.test(block.type)) {
          Object.assign(data.dataset, { head: block.type })
        }

        if (/^h/.test(block.type)) { // h\d or hr
          Object.assign(data.dataset, { role: block.type })
        }

        if (block.type === 'pre') {
          if (block.lang) Object.assign(data.dataset, { lang: block.lang })
          blockSelector += `.${CLASS_OR_ID['AG_CODE_BLOCK']}`
          children = ''
        }

        if (block.type === 'input') {
          const { checked, type, key } = block
          Object.assign(data.attrs, { type: 'checkbox' })
          blockSelector = `${type}#${key}.${CLASS_OR_ID['AG_TASK_LIST_ITEM_CHECKBOX']}`
          if (checked) {
            Object.assign(data.attrs, { checked: true })
            blockSelector += `.${CLASS_OR_ID['AG_CHECKBOX_CHECKED']}`
          }
          children = ''
        }

        if (block.temp) {
          blockSelector += `.${CLASS_OR_ID['AG_TEMP']}`
        }

        return h(blockSelector, data, children)
      }
    }

    const children = blocks.map(block => {
      return renderBlock(block)
    })

    const newVdom = h(selector, children)
    const root = document.querySelector(selector) || this.container
    const oldVdom = toVNode(root)

    patch(oldVdom, newVdom)
  }

  hr (h, cursor, block, token, outerClass) {
    const { start, end } = token.range
    const content = this.highlight(h, block, start, end, token)
    return [
      h(`span.${CLASS_OR_ID['AG_GRAY']}.${CLASS_OR_ID['AG_REMOVE']}`, content)
    ]
  }

  header (h, cursor, block, token, outerClass) {
    const className = this.getClassName(outerClass, block, token, cursor)
    const { start, end } = token.range
    const content = this.highlight(h, block, start, end, token)
    return [
      h(`span.${className}.${CLASS_OR_ID['AG_REMOVE']}`, content)
    ]
  }

  ['code_fense'] (h, cursor, block, token, outerClass) {
    const { start, end } = token.range
    const { marker } = token

    const markerContent = this.highlight(h, block, start, start + marker.length, token)
    const content = this.highlight(h, block, start + marker.length, end, token)

    return [
      h(`span.${CLASS_OR_ID['AG_GRAY']}`, markerContent),
      h(`span.${CLASS_OR_ID['AG_LANGUAGE']}`, content)
    ]
  }

  backlash (h, cursor, block, token, outerClass) {
    const className = this.getClassName(outerClass, block, token, cursor)
    const { start, end } = token.range
    const content = this.highlight(h, block, start, end, token)

    return [
      h(`span.${className}.${CLASS_OR_ID['AG_REMOVE']}`, content)
    ]
  }

  ['inline_code'] (h, cursor, block, token, outerClass) {
    const className = this.getClassName(outerClass, block, token, cursor)
    const { marker } = token
    const { start, end } = token.range

    const startMarker = this.highlight(h, block, start, start + marker.length, token)
    const endMarker = this.highlight(h, block, end - marker.length, end, token)
    const content = this.highlight(h, block, start + marker.length, end - marker.length, token)

    return [
      h(`span.${className}.${CLASS_OR_ID['AG_REMOVE']}`, startMarker),
      h('code', content),
      h(`span.${className}.${CLASS_OR_ID['AG_REMOVE']}`, endMarker)
    ]
  }
  // change text to highlight vdom
  highlight (h, block, rStart, rEnd, token) {
    const { text } = block
    const { highlights } = token
    let result = []
    let unions = []
    let pos = rStart

    if (highlights) {
      for (const light of highlights) {
        const un = union({ start: rStart, end: rEnd }, light)
        if (un) unions.push(un)
      }
    }

    if (unions.length) {
      for (const u of unions) {
        const { start, end, active } = u
        const className = this.getHighlightClassName(active)

        if (pos < start) {
          result.push(text.substring(pos, start))
        }

        result.push(h(`span.${className}`, text.substring(start, end)))
        pos = end
      }
      if (pos < rEnd) {
        result.push(block.text.substring(pos, rEnd))
      }
    } else {
      result = [ text.substring(rStart, rEnd) ]
    }

    return result
  }
  // render token of text type to vdom.
  text (h, cursor, block, token) {
    const { start, end } = token.range
    return this.highlight(h, block, start, end, token)
  }
  // render token of emoji to vdom
  emoji (h, cursor, block, token, outerClass) {
    const { start: rStart, end: rEnd } = token.range
    const className = this.getClassName(outerClass, block, token, cursor)
    const validation = validEmoji(token.content)
    const finalClass = validation ? className : CLASS_OR_ID['AG_WARN']
    const CONTENT_CLASSNAME = `span.${finalClass}.${CLASS_OR_ID['AG_EMOJI_MARKED_TEXT']}`
    let startMarkerCN = `span.${finalClass}.${CLASS_OR_ID['AG_EMOJI_MARKER']}`
    let endMarkerCN = startMarkerCN
    let content = token.content
    let pos = rStart + token.marker.length

    if (token.highlights && token.highlights.length) {
      content = []
      for (const light of token.highlights) {
        let { start, end, active } = light
        const HIGHLIGHT_CLASSNAME = this.getHighlightClassName(active)
        if (start === rStart) {
          startMarkerCN += `.${HIGHLIGHT_CLASSNAME}`
          start++
        }
        if (end === rEnd) {
          endMarkerCN += `.${HIGHLIGHT_CLASSNAME}`
          end--
        }
        if (pos < start) {
          content.push(block.text.substring(pos, start))
        }
        if (start < end) {
          content.push(h(`span.${HIGHLIGHT_CLASSNAME}`, block.text.substring(start, end)))
        }
        pos = end
      }
      if (pos < rEnd - token.marker.length) {
        content.push(block.text.substring(pos, rEnd - 1))
      }
    }

    const emojiVdom = validation
      ? h(CONTENT_CLASSNAME, {
        dataset: {
          emoji: validation.emoji
        }
      }, content)
      : h(CONTENT_CLASSNAME, content)

    return [
      h(startMarkerCN, token.marker),
      emojiVdom,
      h(endMarkerCN, token.marker)
    ]
  }

  // render factory of `del`,`em`,`strong`
  delEmStrongFac (type, h, cursor, block, token, outerClass) {
    const className = this.getClassName(outerClass, block, token, cursor)
    const COMMON_MARKER = `span.${className}.${CLASS_OR_ID['AG_REMOVE']}`
    const { marker } = token
    const { start, end } = token.range
    const backlashStart = end - marker.length - token.backlash.length
    const content = [
      ...token.children.reduce((acc, to) => {
        const chunk = this[to.type](h, cursor, block, to, className)
        return Array.isArray(chunk) ? [...acc, ...chunk] : [...acc, chunk]
      }, []),
      ...this.backlashInToken(token.backlash, className, backlashStart, token)
    ]
    const startMarker = this.highlight(h, block, start, start + marker.length, token)
    const endMarker = this.highlight(h, block, end - marker.length, end, token)

    if (isLengthEven(token.backlash)) {
      return [
        h(COMMON_MARKER, startMarker),
        h(type, content),
        h(COMMON_MARKER, endMarker)
      ]
    } else {
      return [
        ...startMarker,
        ...content,
        ...endMarker
      ]
    }
  }
  // TODO HIGHLIGHT
  backlashInToken (backlashes, outerClass, start, token) {
    const { highlights = [] } = token
    const chunks = backlashes.split('')
    const len = chunks.length
    const result = []
    let i

    for (i = 0; i < len; i++) {
      const chunk = chunks[i]
      const light = highlights.filter(light => union({ start: start + i, end: start + i + 1 }, light))
      let selector = 'span'
      if (light.length) {
        const className = this.getHighlightClassName(light[0].active)
        selector += `.${className}`
      }
      if (isEven(i)) {
        result.push(
          h(`${selector}.${outerClass}`, chunk)
        )
      } else {
        result.push(
          h(`${selector}.${CLASS_OR_ID['AG_BACKLASH']}`, chunk)
        )
      }
    }

    return result
  }
  // I dont want operate dom directly, is there any better method? need help!
  image (h, cursor, block, token, outerClass) {
    const className = this.getClassName(outerClass, block, token, cursor)
    const imageClass = CLASS_OR_ID['AG_IMAGE_MARKED_TEXT']

    const {
      start,
      end
    } = token.range
    const titleContent = this.highlight(h, block, start, start + 2 + token.title.length, token)
    const srcContent = this.highlight(
      h, block,
      start + 2 + token.title.length + token.backlash.first.length,
      start + 2 + token.title.length + token.backlash.first.length + 2 + token.src.length,
      token
    )
    const firstBracketContent = this.highlight(h, block, start, start + 2, token)
    const lastBracketContent = this.highlight(h, block, end - 1, end, token)

    const firstBacklashStart = start + 2 + token.title.length
    const secondBacklashStart = end - 1 - token.backlash.second.length

    if (isLengthEven(token.backlash.first) && isLengthEven(token.backlash.second)) {
      let id
      let isSuccess
      let selector
      const src = getImageSrc(token.src + encodeURI(token.backlash.second))
      const alt = token.title + encodeURI(token.backlash.first)

      if (src) {
        if (!this.loadImageMap.has(src)) {
          id = getIdWithoutSet()
          loadImage(src)
            .then(url => {
              const imageWrapper = document.querySelector(`#${id}`)
              const img = document.createElement('img')
              img.src = url
              img.alt = alt
              if (imageWrapper) {
                insertAfter(img, imageWrapper)
                operateClassName(imageWrapper, 'add', className)
              }
              this.loadImageMap.set(src, {
                id,
                isSuccess: true
              })
            })
            .catch(() => {
              const imageWrapper = document.querySelector(`#${id}`)
              if (imageWrapper) {
                operateClassName(imageWrapper, 'add', CLASS_OR_ID['AG_IMAGE_FAIL'])
              }
              this.loadImageMap.set(src, {
                id,
                isSuccess: false
              })
            })
        } else {
          const imageInfo = this.loadImageMap.get(src)
          id = imageInfo.id
          isSuccess = imageInfo.isSuccess
        }
      }

      selector = id ? `span#${id}.${imageClass}.${CLASS_OR_ID['AG_REMOVE']}` : `span.${imageClass}.${CLASS_OR_ID['AG_REMOVE']}`

      if (isSuccess) {
        selector += `.${className}`
      } else {
        selector += `.${CLASS_OR_ID['AG_IMAGE_FAIL']}`
      }

      const children = [
        ...titleContent,
        ...this.backlashInToken(token.backlash.first, className, firstBacklashStart, token),
        ...srcContent,
        ...this.backlashInToken(token.backlash.second, className, secondBacklashStart, token),
        ...lastBracketContent
      ]

      return isSuccess
        ? [
          h(selector, children),
          h('img', { props: { alt, src } })
        ]
        : [h(selector, children)]
    } else {
      return [
        ...firstBracketContent,
        ...token.children.reduce((acc, to) => {
          const chunk = this[to.type](h, cursor, block, to, className)
          return Array.isArray(chunk) ? [...acc, ...chunk] : [...acc, chunk]
        }, []),
        ...this.backlashInToken(token.backlash.first, className, firstBacklashStart, token),
        ...srcContent,
        ...this.backlashInToken(token.backlash.second, className, secondBacklashStart, token),
        ...lastBracketContent
      ]
    }
  }
  // render auto_link to vdom
  ['auto_link'] (h, cursor, block, token, outerClass) {
    const { start, end } = token.range
    const content = this.highlight(h, block, start, end, token)

    return [
      h('a', {
        props: {
          href: token.href
        }
      }, content)
    ]
  }

  // 'link': /^(\[)((?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*?)(\\*)\]\((.*?)(\\*)\)/, // can nest
  link (h, cursor, block, token, outerClass) {
    const className = this.getClassName(outerClass, block, token, cursor)
    const linkClassName = className === CLASS_OR_ID['AG_HIDE'] ? className : CLASS_OR_ID['AG_LINK_IN_BRACKET']
    const { start, end } = token.range
    const firstMiddleBracket = this.highlight(h, block, start, start + 3, token)

    const firstBracket = this.highlight(h, block, start, start + 1, token)
    const middleBracket = this.highlight(
      h, block,
      start + 1 + token.anchor.length + token.backlash.first.length,
      start + 1 + token.anchor.length + token.backlash.first.length + 2,
      token
    )
    const hrefContent = this.highlight(
      h, block,
      start + 1 + token.anchor.length + token.backlash.first.length + 2,
      start + 1 + token.anchor.length + token.backlash.first.length + 2 + token.href.length,
      token
    )
    const middleHref = this.highlight(
      h, block, start + 1 + token.anchor.length + token.backlash.first.length,
      block, start + 1 + token.anchor.length + token.backlash.first.length + 2 + token.href.length,
      token
    )

    const lastBracket = this.highlight(h, block, end - 1, end, token)

    const firstBacklashStart = start + 1 + token.anchor.length
    const secondBacklashStart = end - 1 - token.backlash.second.length

    if (isLengthEven(token.backlash.first) && isLengthEven(token.backlash.second)) {
      if (!token.children.length && !token.backlash.first) { // no-text-link
        return [
          h(`span.${CLASS_OR_ID['AG_GRAY']}.${CLASS_OR_ID['AG_REMOVE']}`, firstMiddleBracket),
          h(`a.${CLASS_OR_ID['AG_NOTEXT_LINK']}`, {
            props: {
              href: token.href + encodeURI(token.backlash.second)
            }
          }, [
            ...hrefContent,
            ...this.backlashInToken(token.backlash.second, className, secondBacklashStart, token)
          ]),
          h(`span.${CLASS_OR_ID['AG_GRAY']}.${CLASS_OR_ID['AG_REMOVE']}`, lastBracket)
        ]
      } else { // has children
        return [
          h(`span.${className}.${CLASS_OR_ID['AG_REMOVE']}`, firstBracket),
          h('a', {
            dataset: {
              href: token.href + encodeURI(token.backlash.second)
            }
          }, [
            ...token.children.reduce((acc, to) => {
              const chunk = this[to.type](h, cursor, block, to, className)
              return Array.isArray(chunk) ? [...acc, ...chunk] : [...acc, chunk]
            }, []),
            ...this.backlashInToken(token.backlash.first, className, firstBacklashStart, token)
          ]),
          h(`span.${className}.${CLASS_OR_ID['AG_REMOVE']}`, middleBracket),
          h(`span.${linkClassName}.${CLASS_OR_ID['AG_REMOVE']}`, [
            ...hrefContent,
            ...this.backlashInToken(token.backlash.second, className, secondBacklashStart, token)
          ]),
          h(`span.${className}.${CLASS_OR_ID['AG_REMOVE']}`, lastBracket)
        ]
      }
    } else {
      return [
        ...firstBracket,
        ...token.children.reduce((acc, to) => {
          const chunk = this[to.type](h, cursor, block, to, className)
          return Array.isArray(chunk) ? [...acc, ...chunk] : [...acc, chunk]
        }, []),
        ...this.backlashInToken(token.backlash.first, className, firstBacklashStart, token),
        ...middleHref,
        ...this.backlashInToken(token.backlash.second, className, secondBacklashStart, token),
        ...lastBracket
      ]
    }
  }

  del (h, cursor, block, token, outerClass) {
    return this.delEmStrongFac('del', h, cursor, block, token, outerClass)
  }

  em (h, cursor, block, token, outerClass) {
    return this.delEmStrongFac('em', h, cursor, block, token, outerClass)
  }

  strong (h, cursor, block, token, outerClass) {
    return this.delEmStrongFac('strong', h, cursor, block, token, outerClass)
  }
}

export default StateRender


import { ipcRenderer } from 'electron'
import { error, message, promote } from '../notice'

const state = {
}

const mutations = {
}
// AGANI::UPDATE_DOWNLOADED
const actions = {
  LISTEN_FOR_UPDATE ({ commit }) {
    ipcRenderer.on('AGANI::UPDATE_ERROR', (e, msg) => {
      error(msg)
    })
    ipcRenderer.on('AGANI::UPDATE_NOT_AVAILABLE', (e, msg) => {
      message(msg)
    })
    ipcRenderer.on('AGANI::UPDATE_DOWNLOADED', (e, msg) => {
      message(msg)
    })
    ipcRenderer.on('AGANI::UPDATE_AVAILABLE', (e, msg) => {
      promote(msg)
        .then(() => {
          const needUpdate = true
          ipcRenderer.send('AGANI::NEED_UPDATE', { needUpdate })
        })
        .catch(() => {
          const needUpdate = false
          ipcRenderer.send('AGANI::NEED_UPDATE', { needUpdate })
        })
    })
  }
}

export default { state, mutations, actions }


import * as actions from '../actions/format'

export default {
  label: 'Format',
  submenu: [{
    label: 'Strong',
    type: 'checkbox',
    accelerator: 'Shift+CmdOrCtrl+B',
    click (menuItem, browserWindow) {
      actions.format(browserWindow, 'strong')
    }
  }, {
    label: 'Emphasis',
    type: 'checkbox',
    accelerator: 'CmdOrCtrl+E',
    click (menuItem, browserWindow) {
      actions.format(browserWindow, 'em')
    }
  }, {
    label: 'Inline Code',
    type: 'checkbox',
    accelerator: 'CmdOrCtrl+`',
    click (menuItem, browserWindow) {
      actions.format(browserWindow, 'inline_code')
    }
  }, {
    type: 'separator'
  }, {
    label: 'Strike',
    type: 'checkbox',
    accelerator: 'CmdOrCtrl+D',
    click (menuItem, browserWindow) {
      actions.format(browserWindow, 'del')
    }
  }, {
    label: 'Hyperlink',
    type: 'checkbox',
    accelerator: 'CmdOrCtrl+L',
    click (menuItem, browserWindow) {
      actions.format(browserWindow, 'link')
    }
  }, {
    label: 'Image',
    type: 'checkbox',
    accelerator: 'Shift+CmdOrCtrl+I',
    click (menuItem, browserWindow) {
      actions.format(browserWindow, 'image')
    }
  }, {
    type: 'separator'
  }, {
    label: 'Clear Format',
    accelerator: 'Shift+CmdOrCtrl+R',
    click (menuItem, browserWindow) {
      actions.format(browserWindow, 'clear')
    }
  }]
}


'SubSystem': '2',  # Set /SUBSYSTEM:WINDOWS

// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell co
// pies of the Software, and to permit persons to whom the Software is furnished
//  to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include "content/nw/src/shell_content_browser_client.h"

#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/file_util.h"
#include "base/strings/string_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "chrome/common/child_process_logging.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/public/browser/browser_url_handler.h"
#include "content/nw/src/browser/printing/printing_message_filter.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/resource_dispatcher_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_descriptors.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/renderer_preferences.h"
#include "content/public/common/url_constants.h"
#include "content/nw/src/api/dispatcher_host.h"
#include "content/nw/src/breakpad_mac.h"
#include "content/nw/src/common/shell_switches.h"
#include "content/nw/src/browser/printing/print_job_manager.h"
#include "content/nw/src/browser/shell_devtools_delegate.h"
#include "content/nw/src/browser/shell_resource_dispatcher_host_delegate.h"
#include "content/nw/src/media/media_internals.h"
#include "content/nw/src/nw_package.h"
#include "content/nw/src/nw_shell.h"
#include "content/nw/src/nw_version.h"
#include "content/nw/src/shell_browser_context.h"
#include "content/nw/src/shell_browser_main_parts.h"
#include "geolocation/shell_access_token_store.h"
#include "url/gurl.h"
#include "ui/base/l10n/l10n_util.h"
#include "content/common/dom_storage/dom_storage_map.h"
#include "webkit/common/webpreferences.h"
#include "webkit/common/user_agent/user_agent_util.h"
#include "content/common/plugin_list.h"
#include "content/public/browser/plugin_service.h"

#if defined(OS_LINUX)
#include "base/linux_util.h"
#include "content/nw/src/crash_handler_host_linux.h"
#endif

using base::FileDescriptor;

namespace {

#if defined(OS_POSIX) && !defined(OS_MACOSX)
int GetCrashSignalFD(const CommandLine& command_line) {
  std::string process_type =
      command_line.GetSwitchValueASCII(switches::kProcessType);

  if (process_type == switches::kRendererProcess)
    return RendererCrashHandlerHostLinux::GetInstance()->GetDeathSignalSocket();

  if (process_type == switches::kPluginProcess)
    return PluginCrashHandlerHostLinux::GetInstance()->GetDeathSignalSocket();

  if (process_type == switches::kPpapiPluginProcess)
    return PpapiCrashHandlerHostLinux::GetInstance()->GetDeathSignalSocket();

  if (process_type == switches::kGpuProcess)
    return GpuCrashHandlerHostLinux::GetInstance()->GetDeathSignalSocket();

  return -1;
}
#endif  // defined(OS_POSIX) && !defined(OS_MACOSX)

}

namespace content {

ShellContentBrowserClient::ShellContentBrowserClient()
  : shell_browser_main_parts_(NULL),
    master_rph_(NULL) {
}

ShellContentBrowserClient::~ShellContentBrowserClient() {
}

BrowserMainParts* ShellContentBrowserClient::CreateBrowserMainParts(
    const MainFunctionParams& parameters) {
  shell_browser_main_parts_ = new ShellBrowserMainParts(parameters);
  return shell_browser_main_parts_;
}

WebContentsViewPort* ShellContentBrowserClient::OverrideCreateWebContentsView(
      WebContents* web_contents,
      RenderViewHostDelegateView** render_view_host_delegate_view) {
  std::string user_agent, rules;
  nw::Package* package = shell_browser_main_parts()->package();
  content::RendererPreferences* prefs = web_contents->GetMutableRendererPrefs();
  if (package->root()->GetString(switches::kmUserAgent, &user_agent)) {
    std::string name, version;
    package->root()->GetString(switches::kmName, &name);
    package->root()->GetString("version", &version);
    ReplaceSubstringsAfterOffset(&user_agent, 0, "%name", name);
    ReplaceSubstringsAfterOffset(&user_agent, 0, "%ver", version);
    ReplaceSubstringsAfterOffset(&user_agent, 0, "%nwver", NW_VERSION_STRING);
    ReplaceSubstringsAfterOffset(&user_agent, 0, "%webkit_ver", webkit_glue::GetWebKitVersion());
    ReplaceSubstringsAfterOffset(&user_agent, 0, "%osinfo", webkit_glue::BuildOSInfo());
    prefs->user_agent_override = user_agent;
  }
  if (package->root()->GetString(switches::kmRemotePages, &rules))
      prefs->nw_remote_page_rules = rules;

  prefs->nw_app_root_path = package->path();
  return NULL;
}

std::string ShellContentBrowserClient::GetApplicationLocale() {
  return l10n_util::GetApplicationLocale("en-US");
}

void ShellContentBrowserClient::AppendExtraCommandLineSwitches(
    CommandLine* command_line,
    int child_process_id) {
#if defined(OS_MACOSX)
  if (IsCrashReporterEnabled()) {
    command_line->AppendSwitchASCII(switches::kEnableCrashReporter,
                                    child_process_logging::GetClientId());
  }
#elif defined(OS_POSIX)
  if (IsCrashReporterEnabled()) {
    command_line->AppendSwitchASCII(switches::kEnableCrashReporter,
        child_process_logging::GetClientId() + "," + base::GetLinuxDistro());
  }

#endif  // OS_MACOSX
  if (command_line->GetSwitchValueASCII("type") != "renderer")
    return;
  if (child_process_id > 0) {
    content::RenderWidgetHost::List widgets =
      content::RenderWidgetHost::GetRenderWidgetHosts();
    for (size_t i = 0; i < widgets.size(); ++i) {
      if (widgets[i]->GetProcess()->GetID() != child_process_id)
        continue;
      if (!widgets[i]->IsRenderView())
        continue;

      const content::RenderWidgetHost* widget = widgets[i];
      DCHECK(widget);

      content::RenderViewHost* host = content::RenderViewHost::From(
        const_cast<content::RenderWidgetHost*>(widget));
      content::Shell* shell = content::Shell::FromRenderViewHost(host);
      if (shell) {
        if (!shell->nodejs())
          return;
        if (shell->is_devtools()) {
          // DevTools should have powerful permissions to load local
          // files by XHR (e.g. for source map)
          command_line->AppendSwitch(switches::kNodejs);
          return;
        }
      }
    }
  }
  nw::Package* package = shell_browser_main_parts()->package();
  if (package && package->GetUseNode()) {
    // Allow node.js
    command_line->AppendSwitch(switches::kNodejs);

    // Set cwd
    command_line->AppendSwitchPath(switches::kWorkingDirectory,
                                   package->path());

    // Check if we have 'node-main'.
    std::string node_main;
    if (package->root()->GetString(switches::kNodeMain, &node_main))
      command_line->AppendSwitchASCII(switches::kNodeMain, node_main);

    std::string snapshot_path;
    if (package->root()->GetString(switches::kSnapshot, &snapshot_path))
      command_line->AppendSwitchASCII(switches::kSnapshot, snapshot_path);

    int dom_storage_quota_mb;
    if (package->root()->GetInteger("dom_storage_quota", &dom_storage_quota_mb)) {
      content::DOMStorageMap::SetQuotaOverride(dom_storage_quota_mb * 1024 * 1024);
      command_line->AppendSwitchASCII(switches::kDomStorageQuota, base::IntToString(dom_storage_quota_mb));
    }
  }

  // without the switch, the destructor of the shell object will
  // shutdown renderwidgethost (RenderWidgetHostImpl::Shutdown) and
  // destory rph immediately. Then the channel error msg is caught by
  // SuicideOnChannelErrorFilter and the renderer is killed
  // immediately
#if defined(OS_POSIX)
  command_line->AppendSwitch(switches::kChildCleanExit);
#endif

}

void ShellContentBrowserClient::ResourceDispatcherHostCreated() {
  resource_dispatcher_host_delegate_.reset(
      new ShellResourceDispatcherHostDelegate());
  ResourceDispatcherHost::Get()->SetDelegate(
      resource_dispatcher_host_delegate_.get());
}

std::string ShellContentBrowserClient::GetDefaultDownloadName() {
  return "download";
}

MediaObserver* ShellContentBrowserClient::GetMediaObserver() {
  return MediaInternals::GetInstance();
}

void ShellContentBrowserClient::BrowserURLHandlerCreated(
    BrowserURLHandler* handler) {
}

ShellBrowserContext* ShellContentBrowserClient::browser_context() {
  return shell_browser_main_parts_->browser_context();
}

ShellBrowserContext*
    ShellContentBrowserClient::off_the_record_browser_context() {
  return shell_browser_main_parts_->off_the_record_browser_context();
}

AccessTokenStore* ShellContentBrowserClient::CreateAccessTokenStore() {
  return new ShellAccessTokenStore(browser_context());
}

void ShellContentBrowserClient::OverrideWebkitPrefs(
      RenderViewHost* render_view_host,
      const GURL& url,
      WebPreferences* prefs) {
  nw::Package* package = shell_browser_main_parts()->package();

  // Disable web security.
  prefs->dom_paste_enabled = true;
  prefs->javascript_can_access_clipboard = true;
  prefs->web_security_enabled = true;
  prefs->allow_file_access_from_file_urls = true;

  // Open experimental features.
  prefs->css_sticky_position_enabled = true;
  prefs->css_shaders_enabled = true;
  prefs->css_variables_enabled = true;

  // Disable plugins and cache by default.
  prefs->plugins_enabled = false;
  prefs->java_enabled = false;

  base::DictionaryValue* webkit;
  if (package->root()->GetDictionary(switches::kmWebkit, &webkit)) {
    webkit->GetBoolean(switches::kmJava, &prefs->java_enabled);
    webkit->GetBoolean(switches::kmPlugin, &prefs->plugins_enabled);
    FilePath plugins_dir = package->path();
    //PathService::Get(base::DIR_CURRENT, &plugins_dir);
    plugins_dir = plugins_dir.AppendASCII("plugins");

    PluginService* plugin_service = PluginService::GetInstance();
    plugin_service->AddExtraPluginDir(plugins_dir);
  }
}

bool ShellContentBrowserClient::ShouldTryToUseExistingProcessHost(
      BrowserContext* browser_context, const GURL& url) {
  ShellBrowserContext* shell_browser_context =
    static_cast<ShellBrowserContext*>(browser_context);
  if (shell_browser_context->pinning_renderer())
    return true;
  else
    return false;
}

bool ShellContentBrowserClient::IsSuitableHost(RenderProcessHost* process_host,
                                          const GURL& site_url) {
  return process_host == master_rph_;
}

net::URLRequestContextGetter* ShellContentBrowserClient::CreateRequestContext(
    BrowserContext* content_browser_context,
    ProtocolHandlerMap* protocol_handlers) {
  ShellBrowserContext* shell_browser_context =
      ShellBrowserContextForBrowserContext(content_browser_context);
  return shell_browser_context->CreateRequestContext(protocol_handlers);
}

net::URLRequestContextGetter*
ShellContentBrowserClient::CreateRequestContextForStoragePartition(
    BrowserContext* content_browser_context,
    const base::FilePath& partition_path,
    bool in_memory,
    ProtocolHandlerMap* protocol_handlers) {
  ShellBrowserContext* shell_browser_context =
      ShellBrowserContextForBrowserContext(content_browser_context);
  return shell_browser_context->CreateRequestContextForStoragePartition(
      partition_path, in_memory, protocol_handlers);
}

ShellBrowserContext*
ShellContentBrowserClient::ShellBrowserContextForBrowserContext(
    BrowserContext* content_browser_context) {
  if (content_browser_context == browser_context())
    return browser_context();
  DCHECK_EQ(content_browser_context, off_the_record_browser_context());
  return off_the_record_browser_context();
}

printing::PrintJobManager* ShellContentBrowserClient::print_job_manager() {
  return shell_browser_main_parts_->print_job_manager();
}

void ShellContentBrowserClient::RenderProcessHostCreated(
    RenderProcessHost* host) {
  int id = host->GetID();
  if (!master_rph_)
    master_rph_ = host;
  // Grant file: scheme to the whole process, since we impose
  // per-view access checks.
  content::ChildProcessSecurityPolicy::GetInstance()->GrantScheme(
      host->GetID(), chrome::kFileScheme);
  content::ChildProcessSecurityPolicy::GetInstance()->GrantScheme(
      host->GetID(), "app");

#if defined(ENABLE_PRINTING)
  host->GetChannel()->AddFilter(new PrintingMessageFilter(id));
#endif
}

bool ShellContentBrowserClient::IsHandledURL(const GURL& url) {
  if (!url.is_valid())
    return false;
  DCHECK_EQ(url.scheme(), StringToLowerASCII(url.scheme()));
  // Keep in sync with ProtocolHandlers added by
  // ShellURLRequestContextGetter::GetURLRequestContext().
  static const char* const kProtocolList[] = {
    chrome::kFileSystemScheme,
    chrome::kFileScheme,
    "app",
  };
  for (size_t i = 0; i < arraysize(kProtocolList); ++i) {
    if (url.scheme() == kProtocolList[i])
      return true;
  }
  return false;
}

void ShellContentBrowserClient::AllowCertificateError(
    int render_process_id,
    int render_view_id,
    int cert_error,
    const net::SSLInfo& ssl_info,
    const GURL& request_url,
    ResourceType::Type resource_type,
    bool overridable,
    bool strict_enforcement,
    const base::Callback<void(bool)>& callback,
    content::CertificateRequestResultType* result) {
  VLOG(1) << "AllowCertificateError: " << request_url;
  CommandLine* cmd_line = CommandLine::ForCurrentProcess();
  if (cmd_line->HasSwitch(switches::kIgnoreCertificateErrors)) {
    *result = content::CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE;
  }
  else
    *result = content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY;
  return;
}

void ShellContentBrowserClient::GetAdditionalAllowedSchemesForFileSystem(
    std::vector<std::string>* additional_allowed_schemes) {
  ContentBrowserClient::GetAdditionalAllowedSchemesForFileSystem(
      additional_allowed_schemes);
  additional_allowed_schemes->push_back("app");
}

#if defined(OS_POSIX) && !defined(OS_MACOSX)
void ShellContentBrowserClient::GetAdditionalMappedFilesForChildProcess(
    const CommandLine& command_line,
    int child_process_id,
    std::vector<FileDescriptorInfo>* mappings) {
  int crash_signal_fd = GetCrashSignalFD(command_line);
  if (crash_signal_fd >= 0) {
    mappings->push_back(FileDescriptorInfo(kCrashDumpSignal,
                                           FileDescriptor(crash_signal_fd,
                                                          false)));
  }
}
#endif  // defined(OS_POSIX) && !defined(OS_MACOSX)

}  // namespace content


// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell co
// pies of the Software, and to permit persons to whom the Software is furnished
//  to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#define V8_USE_UNSAFE_HANDLES

#include "content/nw/src/renderer/shell_content_renderer_client.h"

#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/posix/global_descriptors.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/content/renderer/page_click_tracker.h"
#include "content/nw/src/api/dispatcher.h"
#include "content/nw/src/api/api_messages.h"
#include "content/nw/src/api/window_bindings.h"
#include "content/nw/src/common/shell_switches.h"
#include "content/nw/src/nw_package.h"
#include "content/nw/src/nw_version.h"
#include "components/autofill/content/renderer/autofill_agent.h"
#include "components/autofill/content/renderer/password_autofill_agent.h"
#include "content/nw/src/renderer/nw_render_view_observer.h"
#include "content/nw/src/renderer/prerenderer/prerenderer_client.h"
#include "content/nw/src/renderer/printing/print_web_view_helper.h"
#include "content/nw/src/renderer/shell_render_process_observer.h"
#include "content/public/common/content_switches.h"
#include "content/public/renderer/render_view.h"
#include "content/renderer/render_view_impl.h"
#include "ipc/ipc_descriptors.h"
#include "net/proxy/proxy_bypass_rules.h"
#include "third_party/node/src/node.h"
#include "third_party/node/src/req_wrap.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebSecurityOrigin.h"
#include "third_party/WebKit/public/web/WebSecurityPolicy.h"
#include "third_party/WebKit/public/web/WebView.h"
//#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
#include "content/common/dom_storage/dom_storage_map.h"

using content::RenderView;
using content::RenderViewImpl;
using autofill::AutofillAgent;
using autofill::PasswordAutofillAgent;
using net::ProxyBypassRules;
using WebKit::WebFrame;
using WebKit::WebView;
using WebKit::WebString;
using WebKit::WebSecurityPolicy;

namespace content {

namespace {

RenderView* GetCurrentRenderView() {
  WebFrame* frame = WebFrame::frameForCurrentContext();
  DCHECK(frame);
  if (!frame)
    return NULL;

  WebView* view = frame->view();
  if (!view)
    return NULL;  // can happen during closing.

  RenderView* render_view = RenderView::FromWebView(view);
  DCHECK(render_view);
  return render_view;
}

}  // namespace

ShellContentRendererClient::ShellContentRendererClient() {
}

ShellContentRendererClient::~ShellContentRendererClient() {
}

void ShellContentRendererClient::RenderThreadStarted() {
  // Change working directory.
  CommandLine* command_line = CommandLine::ForCurrentProcess();
  if (command_line->HasSwitch(switches::kWorkingDirectory)) {
    file_util::SetCurrentDirectory(
        command_line->GetSwitchValuePath(switches::kWorkingDirectory));
  }

  int argc = 1;
  char* argv[] = { const_cast<char*>("node"), NULL, NULL };
  std::string node_main;

  // Check if there is a 'node-main'.
  if (command_line->HasSwitch(switches::kNodeMain)) {
    argc++;
    node_main = command_line->GetSwitchValueASCII(switches::kNodeMain);
    argv[1] = const_cast<char*>(node_main.c_str());
  }

  // Initialize uv.
  node::SetupUv(argc, argv);

  std::string snapshot_path;
  if (command_line->HasSwitch(switches::kSnapshot)) {
    snapshot_path = command_line->GetSwitchValuePath(switches::kSnapshot).AsUTF8Unsafe();
  }

  if (command_line->HasSwitch(switches::kDomStorageQuota)) {
    std::string quota_str = command_line->GetSwitchValueASCII(switches::kDomStorageQuota);
    int quota = 0;
    if (base::StringToInt(quota_str, &quota) && quota > 0) {
      content::DOMStorageMap::SetQuotaOverride(quota * 1024 * 1024);
    }
  }
  // Initialize node after render thread is started.
  if (!snapshot_path.empty()) {
    v8::V8::Initialize(snapshot_path.c_str());
  }else
    v8::V8::Initialize();
  v8::HandleScope scope;

  // Install window bindings into node. The Window API is implemented in node's
  // context, so when a Shell changes to a new location and destroy previous
  // window context, our Window API can still work.
  window_bindings_.reset(new api::WindowBindings());
  v8::RegisterExtension(window_bindings_.get());
  const char* names[] = { "window_bindings.js" };
  v8::ExtensionConfiguration extension_configuration(1, names);

  node::g_context = v8::Context::New(&extension_configuration);
  node::g_context->SetSecurityToken(v8::String::NewSymbol("nw-token", 8));
  node::g_context->Enter();

  node::g_context->SetEmbedderData(0, v8::String::NewSymbol("node"));

  // Setup node.js.
  node::SetupContext(argc, argv, node::g_context->Global());

#if !defined(OS_WIN)
  v8::Local<v8::Script> script = v8::Script::New(v8::String::New((
      "process.__nwfds_to_close = [" +
      base::StringPrintf("%d", base::GlobalDescriptors::GetInstance()->Get(kPrimaryIPCChannel)) +
      "];"
    ).c_str()));
  CHECK(*script);
  script->Run();
#endif
  // Start observers.
  shell_observer_.reset(new ShellRenderProcessObserver());

  WebString file_scheme(ASCIIToUTF16("file"));
  WebString app_scheme(ASCIIToUTF16("app"));
  // file: resources should be allowed to receive CORS requests.
  WebSecurityPolicy::registerURLSchemeAsCORSEnabled(file_scheme);
  WebSecurityPolicy::registerURLSchemeAsCORSEnabled(app_scheme);

}

void ShellContentRendererClient::RenderViewCreated(RenderView* render_view) {
  new api::Dispatcher(render_view);
  new nw::NwRenderViewObserver(render_view);
  new prerender::PrerendererClient(render_view);
#if defined(ENABLE_PRINTING)
  new printing::PrintWebViewHelper(render_view);
#endif

  // PageClickTracker* page_click_tracker = new PageClickTracker(render_view);
  PasswordAutofillAgent* password_autofill_agent =
      new PasswordAutofillAgent(render_view);
  new AutofillAgent(render_view, password_autofill_agent);
  //page_click_tracker->AddListener(autofill_agent);
}

void ShellContentRendererClient::DidCreateScriptContext(
    WebKit::WebFrame* frame,
    v8::Handle<v8::Context> context,
    int extension_group,
    int world_id) {
  GURL url(frame->document().url());
  VLOG(1) << "DidCreateScriptContext: " << url;
  InstallNodeSymbols(frame, context, url);
}

bool ShellContentRendererClient::goodForNode(WebKit::WebFrame* frame)
{
  RenderViewImpl* rv = RenderViewImpl::FromWebView(frame->view());
  GURL url(frame->document().url());
  ProxyBypassRules rules;
  rules.ParseFromString(rv->renderer_preferences_.nw_remote_page_rules);
  bool force_on = rules.Matches(url);
  bool is_nw_protocol = url.SchemeIs("nw") || !url.is_valid();
  bool use_node =
    CommandLine::ForCurrentProcess()->HasSwitch(switches::kNodejs) &&
    !frame->isNwDisabledChildFrame() &&
    (force_on || url.SchemeIsFile() || is_nw_protocol || url.SchemeIs("app"));
  return use_node;
}

bool ShellContentRendererClient::WillSetSecurityToken(
    WebKit::WebFrame* frame,
    v8::Handle<v8::Context> context) {
  GURL url(frame->document().url());
  VLOG(1) << "WillSetSecurityToken: " << url;
  if (goodForNode(frame)) {
    // Override context's security token
    context->SetSecurityToken(node::g_context->GetSecurityToken());
    frame->document().securityOrigin().grantUniversalAccess();

    int ret;
    RenderViewImpl* rv = RenderViewImpl::FromWebView(frame->view());
    rv->Send(new ShellViewHostMsg_GrantUniversalPermissions(rv->GetRoutingID(), &ret));

    return true;
  }

  // window.open frame was enabled for Node earlier
  if (frame->isNodeJS()) {
    frame->setNodeJS(false);
    UninstallNodeSymbols(frame, context);
  }
  return false;
}

void ShellContentRendererClient::InstallNodeSymbols(
    WebKit::WebFrame* frame,
    v8::Handle<v8::Context> context,
    const GURL& url) {
  v8::HandleScope handle_scope;

  static bool installed_once = false;

  v8::Local<v8::Object> nodeGlobal = node::g_context->Global();
  v8::Local<v8::Object> v8Global = context->Global();

  // Use WebKit's console globally
  nodeGlobal->Set(v8::String::New("console"),
                  v8Global->Get(v8::String::New("console")));

  // Do we integrate node?
  bool use_node = goodForNode(frame);

  // Test if protocol is 'nw:'
  // test for 'about:blank' is also here becuase window.open would
  // open 'about:blank' first // FIXME
  bool is_nw_protocol = url.SchemeIs("nw") || !url.is_valid();

  if (use_node || is_nw_protocol) {
    frame->setNodeJS(true);

    v8::Local<v8::Array> symbols = v8::Array::New(4);
    symbols->Set(0, v8::String::New("global"));
    symbols->Set(1, v8::String::New("process"));
    symbols->Set(2, v8::String::New("Buffer"));
    symbols->Set(3, v8::String::New("root"));

    for (unsigned i = 0; i < symbols->Length(); ++i) {
      v8::Local<v8::Value> key = symbols->Get(i);
      v8Global->Set(key, nodeGlobal->Get(key));
    }

    if (!installed_once) {
      installed_once = true;

      // The following listener on process should not be added each
      // time when a document is created, or it will leave the
      // reference to the closure created by the call back and leak
      // memory (see #203)

      nodeGlobal->Set(v8::String::New("window"), v8Global);

      // Listen uncaughtException with ReportException.
      v8::Local<v8::Function> cb = v8::FunctionTemplate::New(ReportException)->
        GetFunction();
      v8::Local<v8::Value> argv[] = { v8::String::New("uncaughtException"), cb };
      node::MakeCallback(node::process, "on", 2, argv);
    }
  }

  if (use_node) {
    RenderViewImpl* rv = RenderViewImpl::FromWebView(frame->view());
    std::string root_path = rv->renderer_preferences_.nw_app_root_path.AsUTF8Unsafe();
#if defined(OS_WIN)
    ReplaceChars(root_path, "\\", "\\\\", &root_path);
#endif
    v8::Local<v8::Script> script = v8::Script::New(v8::String::New((
        // Make node's relative modules work
        "if (!process.mainModule.filename) {"
        "  var root = '" + root_path + "';"
#if defined(OS_WIN)
        "process.mainModule.filename = decodeURIComponent(window.location.pathname.substr(1));"
#else
        "process.mainModule.filename = decodeURIComponent(window.location.pathname);"
#endif
        "if (window.location.href.indexOf('app://') === 0) {process.mainModule.filename = root + '/' + process.mainModule.filename}"
        "process.mainModule.paths = global.require('module')._nodeModulePaths(process.cwd());"
        "process.mainModule.loaded = true;"
        "}").c_str()
    ));
    CHECK(*script);
    script->Run();
  }

  if (use_node || is_nw_protocol) {
    v8::Local<v8::Script> script = v8::Script::New(v8::String::New(
        // Overload require
        "window.require = function(name) {"
        "  if (name == 'nw.gui')"
        "    return nwDispatcher.requireNwGui();"
        "  return global.require(name);"
        "};"

        // Save node-webkit version
        "process.versions['node-webkit'] = '" NW_VERSION_STRING "';"
        "process.versions['chromium'] = '" CHROME_VERSION "';"
    ));
    script->Run();
  }
}

// static
void ShellContentRendererClient::ReportException(
            const v8::FunctionCallbackInfo<v8::Value>&  args) {
  v8::HandleScope handle_scope;

  // Do nothing if user is listening to uncaughtException.
  v8::Local<v8::Value> listeners_v =
      node::process->Get(v8::String::New("listeners"));
  v8::Local<v8::Function> listeners =
      v8::Local<v8::Function>::Cast(listeners_v);

  v8::Local<v8::Value> argv[1] = { v8::String::New("uncaughtException") };
  v8::Local<v8::Value> ret = listeners->Call(node::process, 1, argv);
  v8::Local<v8::Array> listener_array = v8::Local<v8::Array>::Cast(ret);

  uint32_t length = listener_array->Length();
  if (length > 1) {
    args.GetReturnValue().Set(v8::Undefined());
    return;
  }

  // Print stacktrace.
  v8::Local<v8::String> stack_symbol = v8::String::New("stack");
  std::string error;

  v8::Local<v8::Object> exception = args[0]->ToObject();
  if (exception->Has(stack_symbol))
    error = *v8::String::Utf8Value(exception->Get(stack_symbol));
  else
    error = *v8::String::Utf8Value(exception);

  RenderView* render_view = GetCurrentRenderView();
  if (!render_view) {
    args.GetReturnValue().Set(v8::Undefined());
    return;
  }

  render_view->Send(new ShellViewHostMsg_UncaughtException(
      render_view->GetRoutingID(),
      error));

  args.GetReturnValue().Set(v8::Undefined());
}

void ShellContentRendererClient::UninstallNodeSymbols(
    WebKit::WebFrame* frame,
    v8::Handle<v8::Context> context) {
  v8::HandleScope handle_scope;

  v8::Local<v8::Object> v8Global = context->Global();
  v8::Local<v8::Array> symbols = v8::Array::New(5);
  symbols->Set(0, v8::String::New("global"));
  symbols->Set(1, v8::String::New("process"));
  symbols->Set(2, v8::String::New("Buffer"));
  symbols->Set(3, v8::String::New("root"));
  symbols->Set(4, v8::String::New("require"));

  for (unsigned i = 0; i < symbols->Length(); ++i) {
    v8::Local<v8::String> key = symbols->Get(i)->ToString();
    if(v8Global->Has(key))
      v8Global->Delete(key);
  }
}

}  // namespace content


exports.run = function(done) {
  var mc = new window.MessageChannel()
  mc.port1.onmessage = function(m) {
    done();
  }
  mc.port2.postMessage("HELLO");
}


<!DOCTYPE html>

    <meta charset="UTF-8">
    <title>new_win2</title>

Please wait to be closed.