Add error catching, viewing and reporting via sentry

This commit is contained in:
Ilya Ig. Petrov 2016-12-02 10:52:11 -08:00
parent 6750872ea5
commit 38ac7450a6
13 changed files with 607 additions and 234 deletions

View File

@ -4,7 +4,6 @@
const handlersState = function(key, value) {
console.log(key, value, '!');
key = 'handlers-' + key;
if (value === null) {
return localStorage.removeItem(key);
@ -21,6 +20,7 @@
};
const ifPrefix = 'if-on-';
const extName = chrome.runtime.getManifest().name;
window.apis.errorHandlers = {
@ -67,29 +67,19 @@
},
};
idToErr: {},
}
// INIT
chrome.proxy.settings.get(
{},
(details) => window.apis.errorHandlers.isNotControlled(details)
);
{
const extName = chrome.runtime.getManifest().name;
const mayNotify = function(
id, title, message,
mayNotify(
id, title, errOrMessage,
icon = 'default-128.png',
context = extName
) {
if ( !window.apis.errorHandlers.isOn(id) ) {
if ( !this.isOn(id) ) {
return;
}
this.idToErr[id] = errOrMessage;
const message = errOrMessage.message || errOrMessage.toString();
chrome.notifications.create(
id,
{
@ -103,31 +93,66 @@ chrome.proxy.settings.get(
}
);
};
},
window.addEventListener('error', (err) => {
installListenersOn(win, name, cb) {
console.warn('GLOBAL ERROR', err);
mayNotify('ext-error', 'Unhandled error', JSON.stringify(err),
'ext-error-128.png');
win.addEventListener('error', (errEvent) => {
console.warn(name + ':GLOBAL ERROR', errEvent);
this.mayNotify('ext-error', 'Ошибка расширения', errEvent,
'ext-error-128.png');
});
win.addEventListener('unhandledrejection', (event) => {
console.warn(name + ':Unhandled rejection. Throwing error.');
event.preventDefault();
throw event.reason;
});
if (cb) {
// setTimeout changes error context.
setTimeout(cb, 0);
}
},
};
}
{
const handlers = window.apis.errorHandlers;
// INIT
chrome.proxy.settings.get(
{},
(details) => handlers.isNotControlled(details)
);
chrome.notifications.onClicked.addListener( function(notId) {
chrome.notifications.clear(notId);
if(notId === 'no-control') {
return chrome.tabs.create({active: true, url: 'chrome://settings/#proxy'});
}
chrome.tabs.create({active: true, url: './pages/view-error/index.html#' + notId});
});
window.addEventListener('unhandledrejection', (event) => {
console.warn('Unhandled rejection. Throwing error.');
event.preventDefault();
throw event.reason;
});
handlers.installListenersOn(window, 'BG');
chrome.proxy.onProxyError.addListener((details) => {
if (window.apis.errorHandlers.ifNoControl) {
if (handlers.ifNoControl) {
return;
}
console.warn('PAC ERROR', details);
mayNotify('pac-error', ' PAC !', JSON.stringify(details),
handlers.mayNotify('pac-error', 'Ошибка PAC!', details,
'pac-error-128.png' );
});
@ -136,8 +161,9 @@ chrome.proxy.settings.get(
console.log('Proxy settings changed.', details);
const noCon = 'no-control';
if ( window.apis.errorHandlers.isNotControlled(details) ) {
mayNotify(noCon, 'Proxy changed', JSON.stringify(details),
if ( handlers.isNotControlled(details) ) {
console.log(details);
handlers.mayNotify(noCon, 'Прокси контролирует другое расширение', details,
'no-control-128.png');
} else {
chrome.notifications.clear( noCon );

View File

@ -99,7 +99,7 @@
_periodicUpdateAlarmReason: 'Периодичное обновление PAC-скрипта Антизапрет',
pushToStorage(cb) {
pushToStorageAsync(cb) {
console.log('Pushing to storage...');
@ -143,7 +143,7 @@
},
*/
syncWithPacProvider(key, cb) {
syncWithPacProviderAsync(key, cb) {
if( !key || typeof(key) === 'function' ) {
cb = key;
@ -200,7 +200,7 @@
if (pacErr && ipsErr) {
return cb(pacErr, pacRes);
}
this.pushToStorage(
this.pushToStorageAsync(
(pushErr) => cb(pacErr || ipsErr || pushErr, pacRes)
);
@ -239,21 +239,21 @@
},
installPac(key, cb) {
installPacAsync(key, cb) {
console.log('Installing PAC...');
if (!key) {
throw new Error('Key must be defined.');
}
if (this.currentProviderKey !== key) {
return this.syncWithPacProvider(key, cb);
return this.syncWithPacProviderAsync(key, cb);
}
console.log(key + ' already installed.');
cb();
},
clearPac(cb) {
clearPacAsync(cb) {
cb = asyncLogGroup('Cearing alarms and PAC...', cb);
chrome.alarms.clearAll(
@ -266,7 +266,7 @@
return cb(err);
}
this.currentPacProviderKey = null;
this.pushToStorage(cb);
this.pushToStorageAsync(cb);
}
)
@ -296,7 +296,7 @@
'Periodic PAC update triggered:',
new Date().toLocaleString('ru-RU')
);
antiCensorRu.syncWithPacProvider(/* Swallows errors. */);
antiCensorRu.syncWithPacProviderAsync(/* Swallows errors. */);
}
}
@ -358,7 +358,7 @@
// UPDATE & MIGRATION
console.log('Extension updated.');
if (!ifAlarmTriggered) {
antiCensorRu.pushToStorage(/* Swallows errors. */);
antiCensorRu.pushToStorageAsync(/* Swallows errors. */);
}
/*
@ -414,7 +414,9 @@
args = replaceArgs;
}
const err = checkChromeError(stack);
cb && cb.call(null, err, ...args);
if (cb) {
setTimeout( cb.bind(null, err, ...args), 0 );
}
};

View File

@ -4,20 +4,23 @@
(e.g. popup) to background window, so we may catch errors
in bg error handlers.
More: https://bugs.chromium.org/p/chromium/issues/detail?id=357568
setTimeout is applied to Async methods only (name ends with Async)
*/
// Fix error context of methods of all APIs.
for(const api of Object.keys(window.apis)) {
/*
for(const apiName of Object.keys(window.apis)) {
const api = window.apis[apiName];
for(const prop of Object.keys(api)) {
if ( typeof(api[prop]) !== 'function' ) {
const method = api[prop];
if ( !(typeof(api[prop]) === 'function'
&& method.name.endsWith('Async')) ) {
continue;
}
const method = api[prop];
api[prop] = function(...args) {
setTimeout(method.bind(this, ...args), 0);
};
}
}
}*/

View File

@ -20,6 +20,7 @@
"contextMenus",
"notifications"
],
"background": {
"scripts": ["00-init-apis.js", "11-api-error-handlers.js", "12-api-sync-pac-script-with-pac-provider.js", "20-api-fixes.js", "30-block-informer.js", "40-context-menus.js"]
},

View File

@ -1,210 +1,211 @@
'use strict';
chrome.runtime.getBackgroundPage( (backgroundPage) => {
chrome.runtime.getBackgroundPage( (backgroundPage) =>
backgroundPage.apis.errorHandlers.installListenersOn(window, 'POPUP', () => {
const getStatus = () => document.querySelector('#status');
const getStatus = () => document.querySelector('#status');
const setStatusTo = (msg) => {
const setStatusTo = (msg) => {
const status = getStatus();
if (msg) {
status.classList.remove('off');
status.innerHTML = msg;
} else {
status.classList.add('off');
}
};
const antiCensorRu = backgroundPage.apis.antiCensorRu;
// SET DATE
const setDate = () => {
let dateForUser = 'никогда';
if( antiCensorRu.lastPacUpdateStamp ) {
let diff = Date.now() - antiCensorRu.lastPacUpdateStamp;
let units = ' мс';
const gauges = [
[1000, ' с'],
[60, ' мин'],
[60, ' ч'],
[24, ' дн'],
[7, ' недель'],
[4, ' месяцев'],
[12, ' г'],
];
for(const g of gauges) {
const diffy = Math.floor(diff / g[0]);
if (!diffy)
break;
diff = diffy;
units = g[1];
const status = getStatus();
if (msg) {
status.classList.remove('off');
status.innerHTML = msg;
} else {
status.classList.add('off');
}
dateForUser = diff + units + ' назад';
}
const dateElement = document.querySelector('.update-date');
dateElement.innerText = dateForUser;
dateElement.title = new Date(antiCensorRu.lastPacUpdateStamp)
.toLocaleString('ru-RU');
};
setDate();
chrome.storage.onChanged.addListener(
(changes) => changes.lastPacUpdateStamp.newValue && setDate()
);
// CLOSE BUTTON
document.querySelector('.close-button').onclick = () => window.close();
// RADIOS
const currentProviderRadio = () => {
const id = antiCensorRu.currentPacProviderKey || 'none';
return document.querySelector('#'+id);
};
const checkChosenProvider = () => currentProviderRadio().checked = true;
const showError = (err) => {
let clarification = err.clarification;
const ifNotCritical = clarification && clarification.ifNotCritical;
let message = err.message || '';
while( clarification ) {
message = (clarification && (clarification.message + ' ')) + message;
clarification = clarification.prev;
}
message = message.trim();
setStatusTo(
`<span style="color:red">
${ifNotCritical ? 'Некритичная ошибка.' : 'Ошибка!'}
</span>
<br/>
<span style="font-size: 0.9em; color: darkred">${message}</span>
<a href class="link-button">[Ещё&nbsp;подробнее]</a>`
);
getStatus().querySelector('.link-button').onclick = function() {
const div = document.createElement('div');
div.innerHTML = `
Более подробную информацию можно узнать из логов фоновой страницы:<br/>
<a href="chrome://extensions?id=${chrome.runtime.id}" data-in-bg="true">
chrome://extensions</a>
Это расширение Отладка страниц: фоновая страница Console (DevTools)
<br>
Ещё: ${JSON.stringify({err: err, stack: err.stack})}
`;
getStatus().replaceChild(div, this);
return false;
};
};
const antiCensorRu = backgroundPage.apis.antiCensorRu;
const enableDisableInputs = function() {
// SET DATE
const inputs = document.querySelectorAll('input');
for ( let i = 0; i < inputs.length; i++ ) {
inputs[i].disabled = !inputs[i].disabled;
}
const setDate = () => {
};
const conduct = (beforeStatus, operation, afterStatus, onSuccess) => {
setStatusTo(beforeStatus);
enableDisableInputs();
operation((err) => {
if (err) {
showError(err);
} else {
setStatusTo(afterStatus);
onSuccess && onSuccess();
let dateForUser = 'никогда';
if( antiCensorRu.lastPacUpdateStamp ) {
let diff = Date.now() - antiCensorRu.lastPacUpdateStamp;
let units = ' мс';
const gauges = [
[1000, ' с'],
[60, ' мин'],
[60, ' ч'],
[24, ' дн'],
[7, ' недель'],
[4, ' месяцев'],
[12, ' г'],
];
for(const g of gauges) {
const diffy = Math.floor(diff / g[0]);
if (!diffy)
break;
diff = diffy;
units = g[1];
}
dateForUser = diff + units + ' назад';
}
const dateElement = document.querySelector('.update-date');
dateElement.innerText = dateForUser;
dateElement.title = new Date(antiCensorRu.lastPacUpdateStamp)
.toLocaleString('ru-RU');
};
setDate();
chrome.storage.onChanged.addListener(
(changes) => changes.lastPacUpdateStamp.newValue && setDate()
);
// CLOSE BUTTON
document.querySelector('.close-button').onclick = () => window.close();
// RADIOS
const currentProviderRadio = () => {
const id = antiCensorRu.currentPacProviderKey || 'none';
return document.querySelector('#'+id);
};
const checkChosenProvider = () => currentProviderRadio().checked = true;
const showError = (err) => {
let clarification = err.clarification;
const ifNotCritical = clarification && clarification.ifNotCritical;
let message = err.message || '';
while( clarification ) {
message = (clarification && (clarification.message + ' ')) + message;
clarification = clarification.prev;
}
message = message.trim();
setStatusTo(
`<span style="color:red">
${ifNotCritical ? 'Некритичная ошибка.' : 'Ошибка!'}
</span>
<br/>
<span style="font-size: 0.9em; color: darkred">${message}</span>
<a href class="link-button">[Ещё&nbsp;подробнее]</a>`
);
getStatus().querySelector('.link-button').onclick = function() {
const div = document.createElement('div');
div.innerHTML = `
Более подробную информацию можно узнать из логов фоновой страницы:<br/>
<a href="chrome://extensions?id=${chrome.runtime.id}" data-in-bg="true">
chrome://extensions</a>
Это расширение Отладка страниц: фоновая страница Console (DevTools)
<br>
Ещё: ` + JSON.stringify({err: err, stack: err.stack});
getStatus().replaceChild(div, this);
return false;
};
};
const enableDisableInputs = function() {
const inputs = document.querySelectorAll('input');
for ( let i = 0; i < inputs.length; i++ ) {
inputs[i].disabled = !inputs[i].disabled;
}
};
const conduct = (beforeStatus, operation, afterStatus, onSuccess) => {
setStatusTo(beforeStatus);
enableDisableInputs();
});
operation((err) => {
if (err) {
showError(err);
} else {
setStatusTo(afterStatus);
onSuccess && onSuccess();
}
enableDisableInputs();
});
};
};
const ul = document.querySelector('#list-of-providers');
const _firstChild = ul.firstChild;
for( const providerKey of Object.keys(antiCensorRu.pacProviders).sort() ) {
const li = document.createElement('li');
li.innerHTML = `<input type="radio" name="pacProvider" id="${providerKey}">
<label for="${providerKey}">${providerKey}</label>
<a href class="link-button checked-radio-panel"
id="update-${providerKey}">[обновить]</a>`;
li.querySelector('.link-button').onclick =
() => {
conduct(
'Обновляем...', (cb) => antiCensorRu.syncWithPacProvider(cb),
'Обновлено.'
);
const ul = document.querySelector('#list-of-providers');
const _firstChild = ul.firstChild;
for( const providerKey of Object.keys(antiCensorRu.pacProviders).sort() ) {
const li = document.createElement('li');
li.innerHTML = `<input type="radio" name="pacProvider" id="${providerKey}">
<label for="${providerKey}">${providerKey}</label>
<a href class="link-button checked-radio-panel"
id="update-${providerKey}">[обновить]</a>`;
li.querySelector('.link-button').onclick =
() => {
conduct(
'Обновляем...', (cb) => antiCensorRu.syncWithPacProviderAsync(cb),
'Обновлено.'
);
return false;
};
ul.insertBefore( li, _firstChild );
}
checkChosenProvider();
const radios = [].slice.apply(
document.querySelectorAll('[name=pacProvider]')
);
for(const radio of radios) {
radio.onclick = function(event) {
if (event.target.id === (antiCensorRu.currentPacProviderKey || 'none')) {
return false;
}
const pacKey = event.target.id;
if (pacKey === 'none') {
conduct(
'Отключение...',
(cb) => antiCensorRu.clearPacAsync(cb),
'Отключено.',
checkChosenProvider
);
} else {
conduct(
'Установка...',
(cb) => antiCensorRu.installPacAsync(pacKey, cb),
'PAC-скрипт установлен.',
checkChosenProvider
);
}
return false;
};
ul.insertBefore( li, _firstChild );
}
checkChosenProvider();
}
const radios = [].slice.apply(
document.querySelectorAll('[name=pacProvider]')
);
for(const radio of radios) {
radio.onclick = function(event) {
const conpanel = document.getElementById('list-of-handlers');
backgroundPage.apis.errorHandlers.getEventsMap().forEach( (value, name) => {
if (event.target.id === (antiCensorRu.currentPacProviderKey || 'none')) {
return false;
}
const pacKey = event.target.id;
if (pacKey === 'none') {
conduct(
'Отключение...',
(cb) => antiCensorRu.clearPac(cb),
'Отключено.',
checkChosenProvider
);
} else {
conduct(
'Установка...',
(cb) => antiCensorRu.installPac(pacKey, cb),
'PAC-скрипт установлен.',
checkChosenProvider
);
}
return false;
};
}
const li = document.createElement('li');
li.innerHTML = `
<input type="checkbox" id="if-on-${name}"/>
<label for="if-on-${name}">${value}</label>`;
const box = li.querySelector('input');
box.checked = backgroundPage.apis.errorHandlers.isOn(name);
box.onclick = function() {
const conpanel = document.getElementById('list-of-handlers');
backgroundPage.apis.errorHandlers.getEventsMap().forEach( (value, name) => {
const id = this.id.replace('if-on-', '');
backgroundPage.apis.errorHandlers.switch(this.checked ? 'on' : 'off', id);
const li = document.createElement('li');
li.innerHTML = `
<input type="checkbox" id="if-on-${name}"/>
<label for="if-on-${name}">${value}</label>`;
const box = li.querySelector('input');
box.checked = backgroundPage.apis.errorHandlers.isOn(name);
box.onclick = function() {
};
conpanel.appendChild(li);
const id = this.id.replace('if-on-', '');
backgroundPage.apis.errorHandlers.switch(this.checked ? 'on' : 'off', id);
});
};
conpanel.appendChild(li);
setStatusTo('');
if (antiCensorRu.ifFirstInstall) {
const id = antiCensorRu.currentPacProviderKey || 'none';
document.querySelector('#update-' + id).click();
}
});
setStatusTo('');
if (antiCensorRu.ifFirstInstall) {
const id = antiCensorRu.currentPacProviderKey || 'none';
document.querySelector('#update-' + id).click();
}
});
})
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

@ -0,0 +1,75 @@
<!DOCTYPE html>
<html>
<head>
<title>Ошибка</title>
<style type="text/css" media="screen">
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
html {
background: black url('./err.jpg') repeat;
background-position: left;
color: white;
}
body {
background: linear-gradient(to bottom, black 45em, transparent);
}
header {
padding-top: 1em;
}
h2 {
margin-top: 0;
}
main {
color: #999999;
font-size: 1.5em;
}
pre {
margin: 0 !important;
padding: 1em !important;
width: 100% !important;
border-radius: 0 !important;
box-sizing: border-box !important;
background-color: transparent !important;
}
header {
padding-left: 2em;
}
ol, ul {
list-style-type: none;
}
textarea {
width: 40em;
height: 10em;
font: inherit;
}
#output {
background-color: black;
}
</style>
</head>
<body>
<header>
<h2>Информация об ошибке</h2>
</header>
<main>
<ol>
<li>Тип: <span>ext-error</span></li>
<li><pre><code id="output" class='hljs'></code></pre></li>
<li><textarea placeholder='Ваш комментарий (опционально)' id="comment"></textarea></li>
<li>
<button id="raven-report">Отправить автору</button>
<button id="github-search">Искать на GitHub</button>
<button id="github-report">Написать на GitHub</button>
</li>
</ol>
</main>
<script src="./vendor/raven3.8.1.min.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="./vendor/highlight.js/styles/atom-one-dark.css">
<script src="./vendor/highlight.js/highlight9.8.0.min.js"></script>
<script src="./index.js" charset="utf-8"></script>
</body>
</html>

View File

@ -0,0 +1,100 @@
'use strict';
function errorJsonReplacer(key, value) {
if ( !['Error', 'ErrorEvent'].includes( value.constructor.name ) ) {
return value;
}
const alt = {};
Object.getOwnPropertyNames(value).forEach(function (key) {
alt[key] = value[key];
}, value);
for(const prop in value) {
if (/^[A-Z]/.test(prop)) {
continue;
}
alt[prop] = value[prop];
}
// fooWindow.ErrorEvent !== barWindow.ErrorEvent
if (value.constructor.name === 'ErrorEvent') {
for(const circularProp of
[ // First line are circular props.
'target', 'srcElement', 'path', 'currentTarget',
'bubbles', 'cancelBubble', 'cancelable', 'composed',
'defaultPrevented', 'eventPhase', 'isTrusted', 'returnValue',
'timeStamp']) {
delete alt[circularProp];
}
}
return alt;
}
chrome.runtime.getBackgroundPage( (backgroundPage) =>
backgroundPage.apis.errorHandlers.installListenersOn(window, 'ErrView', () => {
Raven.config('https://bc534321358f455b9ae861740c2d3af8@sentry.io/116007', {
release: chrome.runtime.getManifest().version,
autoBreadcrumbs: false,
}).install();
const errId = window.location.hash.slice(1);
const err = backgroundPage.apis.errorHandlers.idToErr[errId];
const json = JSON.stringify(err, errorJsonReplacer, 2);
document.getElementById('output').innerHTML = hljs.highlight('json', json).value;
document.addEventListener('ravenSuccess', () => alert('Готово'));
document.addEventListener('ravenFailure', () => alert('Ошибка sentry.io! (подробности недоступны)'));
document.getElementById('raven-report').onclick = () => {
const e = err.error || err;
const extra = JSON.parse(json);
const comment = document.getElementById('comment').value;
if (comment.trim()) {
extra.comment = comment;
}
Raven.captureException(e, {
extra: extra,
onSuccess: () => alert('Готово'),
onError: (err) => { throw err; }
});
};
document.getElementById('github-search').onclick = () => {
const title = err.message || err;
chrome.tabs.create({
active: true,
url: 'https://rebrand.ly/ac-search-issues?q=' + encodeURIComponent(title)
});
};
document.getElementById('github-report').onclick = () => {
const comment = document.getElementById('comment').value;
const title = err.message || err;
const body = (comment || 'Ваш текст') + `
### Ошибка
\`\`\`json
${json}
\`\`\`
Версия: ${chrome.runtime.getManifest().version}
`;
chrome.tabs.create({
active: true,
url: `https://rebrand.ly/ac-new-issue?title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}`
});
};
})
);

View File

@ -0,0 +1,63 @@
!function(){/*
Copyright (C) 2013 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Copyright (C) 2006 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function(){function ba(g){function k(){try{M.doScroll("left")}catch(g){t.setTimeout(k,50);return}z("poll")}function z(k){if("readystatechange"!=k.type||"complete"==A.readyState)("load"==k.type?t:A)[B](p+k.type,z,!1),!q&&(q=!0)&&g.call(t,k.type||k)}var Y=A.addEventListener,q=!1,C=!0,x=Y?"addEventListener":"attachEvent",B=Y?"removeEventListener":"detachEvent",p=Y?"":"on";if("complete"==A.readyState)g.call(t,"lazy");else{if(A.createEventObject&&M.doScroll){try{C=!t.frameElement}catch(da){}C&&k()}A[x](p+
"DOMContentLoaded",z,!1);A[x](p+"readystatechange",z,!1);t[x](p+"load",z,!1)}}function U(){V&&ba(function(){var g=N.length;ca(g?function(){for(var k=0;k<g;++k)(function(g){t.setTimeout(function(){t.exports[N[g]].apply(t,arguments)},0)})(k)}:void 0)})}for(var t=window,A=document,M=A.documentElement,O=A.head||A.getElementsByTagName("head")[0]||M,B="",F=A.getElementsByTagName("script"),q=F.length;0<=--q;){var P=F[q],Z=P.src.match(/^[^?#]*\/run_prettify\.js(\?[^#]*)?(?:#.*)?$/);if(Z){B=Z[1]||"";P.parentNode.removeChild(P);
break}}var V=!0,H=[],Q=[],N=[];B.replace(/[?&]([^&=]+)=([^&]+)/g,function(g,k,z){z=decodeURIComponent(z);k=decodeURIComponent(k);"autorun"==k?V=!/^[0fn]/i.test(z):"lang"==k?H.push(z):"skin"==k?Q.push(z):"callback"==k&&N.push(z)});q=0;for(B=H.length;q<B;++q)(function(){var g=A.createElement("script");g.onload=g.onerror=g.onreadystatechange=function(){!g||g.readyState&&!/loaded|complete/.test(g.readyState)||(g.onerror=g.onload=g.onreadystatechange=null,--T,T||t.setTimeout(U,0),g.parentNode&&g.parentNode.removeChild(g),
g=null)};g.type="text/javascript";g.src="https://cdn.rawgit.com/google/code-prettify/master/loader/lang-"+encodeURIComponent(H[q])+".js";O.insertBefore(g,O.firstChild)})(H[q]);for(var T=H.length,F=[],q=0,B=Q.length;q<B;++q)F.push("./vendor/code-prettify/skins/"+encodeURIComponent(Q[q])+".css");F.push("https://cdn.rawgit.com/google/code-prettify/master/loader/prettify.css");(function(g){function k(q){if(q!==z){var t=A.createElement("link");t.rel="stylesheet";t.type=
"text/css";q+1<z&&(t.error=t.onerror=function(){k(q+1)});t.href=g[q];O.appendChild(t)}}var z=g.length;k(0)})(F);var ca=function(){window.PR_SHOULD_USE_CONTINUATION=!0;var g;(function(){function k(a){function d(e){var b=e.charCodeAt(0);if(92!==b)return b;var a=e.charAt(1);return(b=W[a])?b:"0"<=a&&"7">=a?parseInt(e.substring(1),8):"u"===a||"x"===a?parseInt(e.substring(2),16):e.charCodeAt(1)}function f(e){if(32>e)return(16>e?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);return"\\"===e||"-"===
e||"]"===e||"^"===e?"\\"+e:e}function b(e){var b=e.substring(1,e.length-1).match(/\\u[0-9A-Fa-f]{4}|\\x[0-9A-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\s\S]|-|[^-\\]/g);e=[];var a="^"===b[0],c=["["];a&&c.push("^");for(var a=a?1:0,h=b.length;a<h;++a){var l=b[a];if(/\\[bdsw]/i.test(l))c.push(l);else{var l=d(l),n;a+2<h&&"-"===b[a+1]?(n=d(b[a+2]),a+=2):n=l;e.push([l,n]);65>n||122<l||(65>n||90<l||e.push([Math.max(65,l)|32,Math.min(n,90)|32]),97>n||122<l||e.push([Math.max(97,l)&-33,Math.min(n,122)&-33]))}}e.sort(function(e,
a){return e[0]-a[0]||a[1]-e[1]});b=[];h=[];for(a=0;a<e.length;++a)l=e[a],l[0]<=h[1]+1?h[1]=Math.max(h[1],l[1]):b.push(h=l);for(a=0;a<b.length;++a)l=b[a],c.push(f(l[0])),l[1]>l[0]&&(l[1]+1>l[0]&&c.push("-"),c.push(f(l[1])));c.push("]");return c.join("")}function g(e){for(var a=e.source.match(/(?:\[(?:[^\x5C\x5D]|\\[\s\S])*\]|\\u[A-Fa-f0-9]{4}|\\x[A-Fa-f0-9]{2}|\\[0-9]+|\\[^ux0-9]|\(\?[:!=]|[\(\)\^]|[^\x5B\x5C\(\)\^]+)/g),c=a.length,d=[],h=0,l=0;h<c;++h){var n=a[h];"("===n?++l:"\\"===n.charAt(0)&&(n=
+n.substring(1))&&(n<=l?d[n]=-1:a[h]=f(n))}for(h=1;h<d.length;++h)-1===d[h]&&(d[h]=++k);for(l=h=0;h<c;++h)n=a[h],"("===n?(++l,d[l]||(a[h]="(?:")):"\\"===n.charAt(0)&&(n=+n.substring(1))&&n<=l&&(a[h]="\\"+d[n]);for(h=0;h<c;++h)"^"===a[h]&&"^"!==a[h+1]&&(a[h]="");if(e.ignoreCase&&I)for(h=0;h<c;++h)n=a[h],e=n.charAt(0),2<=n.length&&"["===e?a[h]=b(n):"\\"!==e&&(a[h]=n.replace(/[a-zA-Z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var k=0,I=!1,
m=!1,J=0,c=a.length;J<c;++J){var r=a[J];if(r.ignoreCase)m=!0;else if(/[a-z]/i.test(r.source.replace(/\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi,""))){I=!0;m=!1;break}}for(var W={b:8,t:9,n:10,v:11,f:12,r:13},u=[],J=0,c=a.length;J<c;++J){r=a[J];if(r.global||r.multiline)throw Error(""+r);u.push("(?:"+g(r)+")")}return new RegExp(u.join("|"),m?"gi":"g")}function q(a,d){function f(a){var c=a.nodeType;if(1==c){if(!b.test(a.className)){for(c=a.firstChild;c;c=c.nextSibling)f(c);c=a.nodeName.toLowerCase();if("br"===
c||"li"===c)g[m]="\n",I[m<<1]=k++,I[m++<<1|1]=a}}else if(3==c||4==c)c=a.nodeValue,c.length&&(c=d?c.replace(/\r\n?/g,"\n"):c.replace(/[ \t\r\n]+/g," "),g[m]=c,I[m<<1]=k,k+=c.length,I[m++<<1|1]=a)}var b=/(?:^|\s)nocode(?:\s|$)/,g=[],k=0,I=[],m=0;f(a);return{a:g.join("").replace(/\n$/,""),c:I}}function t(a,d,f,b,g){f&&(a={h:a,l:1,j:null,m:null,a:f,c:null,i:d,g:null},b(a),g.push.apply(g,a.g))}function A(a){for(var d=void 0,f=a.firstChild;f;f=f.nextSibling)var b=f.nodeType,d=1===b?d?a:f:3===b?T.test(f.nodeValue)?
a:d:d;return d===a?void 0:d}function C(a,d){function f(a){for(var m=a.i,k=a.h,c=[m,"pln"],r=0,W=a.a.match(g)||[],u={},e=0,q=W.length;e<q;++e){var D=W[e],w=u[D],h=void 0,l;if("string"===typeof w)l=!1;else{var n=b[D.charAt(0)];if(n)h=D.match(n[1]),w=n[0];else{for(l=0;l<p;++l)if(n=d[l],h=D.match(n[1])){w=n[0];break}h||(w="pln")}!(l=5<=w.length&&"lang-"===w.substring(0,5))||h&&"string"===typeof h[1]||(l=!1,w="src");l||(u[D]=w)}n=r;r+=D.length;if(l){l=h[1];var E=D.indexOf(l),G=E+l.length;h[2]&&(G=D.length-
h[2].length,E=G-l.length);w=w.substring(5);t(k,m+n,D.substring(0,E),f,c);t(k,m+n+E,l,F(w,l),c);t(k,m+n+G,D.substring(G),f,c)}else c.push(m+n,w)}a.g=c}var b={},g;(function(){for(var f=a.concat(d),m=[],p={},c=0,r=f.length;c<r;++c){var q=f[c],u=q[3];if(u)for(var e=u.length;0<=--e;)b[u.charAt(e)]=q;q=q[1];u=""+q;p.hasOwnProperty(u)||(m.push(q),p[u]=null)}m.push(/[\0-\uffff]/);g=k(m)})();var p=d.length;return f}function x(a){var d=[],f=[];a.tripleQuotedStrings?d.push(["str",/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
null,"'\""]):a.multiLineStrings?d.push(["str",/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"]):d.push(["str",/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"]);a.verbatimStrings&&f.push(["str",/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null]);var b=a.hashComments;b&&(a.cStyleComments?(1<b?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\r\n]*)/,
null,"#"]),f.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,null])):d.push(["com",/^#[^\r\n]*/,null,"#"]));a.cStyleComments&&(f.push(["com",/^\/\/[^\r\n]*/,null]),f.push(["com",/^\/\*[\s\S]*?(?:\*\/|$)/,null]));if(b=a.regexLiterals){var g=(b=1<b?"":"\n\r")?".":"[\\S\\s]";f.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+
("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+g+"|\\x5B(?:[^\\x5C\\x5D"+b+"]|\\x5C"+g+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&f.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&f.push(["kwd",new RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),null]);d.push(["pln",/^\s+/,null," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");f.push(["lit",/^@[a-z_$][a-z_$@0-9]*/i,null],["typ",/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],["pln",/^[a-z_$][a-z_$@0-9]*/i,
null],["lit",/^(?:0x[a-f0-9]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+\-]?\d+)?)[a-z]*/i,null,"0123456789"],["pln",/^\\[\s\S]?/,null],["pun",new RegExp(b),null]);return C(d,f)}function B(a,d,f){function b(a){var c=a.nodeType;if(1==c&&!k.test(a.className))if("br"===a.nodeName)g(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((3==c||4==c)&&f){var d=a.nodeValue,p=d.match(q);p&&(c=d.substring(0,p.index),a.nodeValue=c,(d=d.substring(p.index+p[0].length))&&
a.parentNode.insertBefore(m.createTextNode(d),a.nextSibling),g(a),c||a.parentNode.removeChild(a))}}function g(a){function b(a,c){var d=c?a.cloneNode(!1):a,n=a.parentNode;if(n){var n=b(n,1),e=a.nextSibling;n.appendChild(d);for(var f=e;f;f=e)e=f.nextSibling,n.appendChild(f)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;a=b(a.nextSibling,0);for(var d;(d=a.parentNode)&&1===d.nodeType;)a=d;c.push(a)}for(var k=/(?:^|\s)nocode(?:\s|$)/,q=/\r\n?|\n/,m=a.ownerDocument,p=m.createElement("li");a.firstChild;)p.appendChild(a.firstChild);
for(var c=[p],r=0;r<c.length;++r)b(c[r]);d===(d|0)&&c[0].setAttribute("value",d);var t=m.createElement("ol");t.className="linenums";d=Math.max(0,d-1|0)||0;for(var r=0,u=c.length;r<u;++r)p=c[r],p.className="L"+(r+d)%10,p.firstChild||p.appendChild(m.createTextNode("\u00a0")),t.appendChild(p);a.appendChild(t)}function p(a,d){for(var f=d.length;0<=--f;){var b=d[f];X.hasOwnProperty(b)?R.console&&console.warn("cannot override language handler %s",b):X[b]=a}}function F(a,d){a&&X.hasOwnProperty(a)||(a=/^\s*</.test(d)?
"default-markup":"default-code");return X[a]}function H(a){var d=a.j;try{var f=q(a.h,a.l),b=f.a;a.a=b;a.c=f.c;a.i=0;F(d,b)(a);var g=/\bMSIE\s(\d+)/.exec(navigator.userAgent),g=g&&8>=+g[1],d=/\n/g,p=a.a,k=p.length,f=0,m=a.c,t=m.length,b=0,c=a.g,r=c.length,x=0;c[r]=k;var u,e;for(e=u=0;e<r;)c[e]!==c[e+2]?(c[u++]=c[e++],c[u++]=c[e++]):e+=2;r=u;for(e=u=0;e<r;){for(var A=c[e],D=c[e+1],w=e+2;w+2<=r&&c[w+1]===D;)w+=2;c[u++]=A;c[u++]=D;e=w}c.length=u;var h=a.h;a="";h&&(a=h.style.display,h.style.display="none");
try{for(;b<t;){var l=m[b+2]||k,n=c[x+2]||k,w=Math.min(l,n),E=m[b+1],G;if(1!==E.nodeType&&(G=p.substring(f,w))){g&&(G=G.replace(d,"\r"));E.nodeValue=G;var aa=E.ownerDocument,v=aa.createElement("span");v.className=c[x+1];var B=E.parentNode;B.replaceChild(v,E);v.appendChild(E);f<l&&(m[b+1]=E=aa.createTextNode(p.substring(w,l)),B.insertBefore(E,v.nextSibling))}f=w;f>=l&&(b+=2);f>=n&&(x+=2)}}finally{h&&(h.style.display=a)}}catch(y){R.console&&console.log(y&&y.stack||y)}}var R=window,K=["break,continue,do,else,for,if,return,while"],
L=[[K,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],S=[L,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],
M=[L,"abstract,assert,boolean,byte,extends,finally,final,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"],N=[L,"abstract,as,base,bool,by,byte,checked,decimal,delegate,descending,dynamic,event,finally,fixed,foreach,from,group,implicit,in,interface,internal,into,is,let,lock,null,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],L=[L,"debugger,eval,export,function,get,instanceof,null,set,undefined,var,with,Infinity,NaN"],
O=[K,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],P=[K,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],K=[K,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],Q=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,
T=/\S/,U=x({keywords:[S,N,M,L,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",O,P,K],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),X={};p(U,["default-code"]);p(C([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",
/^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),"default-markup htm html mxml xhtml xml xsl".split(" "));p(C([["pln",/^[\s]+/,null," \t\r\n"],["atv",/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
["pun",/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);p(C([],[["atv",/^[\s\S]+/]]),["uq.val"]);p(x({keywords:S,hashComments:!0,cStyleComments:!0,types:Q}),"c cc cpp cxx cyc m".split(" "));p(x({keywords:"null,true,false"}),["json"]);p(x({keywords:N,hashComments:!0,cStyleComments:!0,
verbatimStrings:!0,types:Q}),["cs"]);p(x({keywords:M,cStyleComments:!0}),["java"]);p(x({keywords:K,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);p(x({keywords:O,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);p(x({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),
["perl","pl","pm"]);p(x({keywords:P,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);p(x({keywords:L,cStyleComments:!0,regexLiterals:!0}),["javascript","js"]);p(x({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);p(C([],[["str",/^[\s\S]+/]]),["regex"]);
var V=R.PR={createSimpleLexer:C,registerLangHandler:p,sourceDecorator:x,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:function(a,d,f){f=f||!1;d=d||null;var b=document.createElement("div");b.innerHTML="<pre>"+a+"</pre>";b=b.firstChild;f&&B(b,f,!0);H({j:d,m:f,h:b,l:1,a:null,i:null,c:null,g:null});return b.innerHTML},
prettyPrint:g=g=function(a,d){function f(){for(var b=R.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;r<p.length&&c.now()<b;r++){for(var d=p[r],k=h,q=d;q=q.previousSibling;){var m=q.nodeType,v=(7===m||8===m)&&q.nodeValue;if(v?!/^\??prettify\b/.test(v):3!==m||/\S/.test(q.nodeValue))break;if(v){k={};v.replace(/\b(\w+)=([\w:.%+-]+)/g,function(a,b,c){k[b]=c});break}}q=d.className;if((k!==h||u.test(q))&&!e.test(q)){m=!1;for(v=d.parentNode;v;v=v.parentNode)if(w.test(v.tagName)&&v.className&&u.test(v.className)){m=
!0;break}if(!m){d.className+=" prettyprinted";m=k.lang;if(!m){var m=q.match(t),C;!m&&(C=A(d))&&z.test(C.tagName)&&(m=C.className.match(t));m&&(m=m[1])}if(x.test(d.tagName))v=1;else var v=d.currentStyle,y=g.defaultView,v=(v=v?v.whiteSpace:y&&y.getComputedStyle?y.getComputedStyle(d,null).getPropertyValue("white-space"):0)&&"pre"===v.substring(0,3);y=k.linenums;(y="true"===y||+y)||(y=(y=q.match(/\blinenums\b(?::(\d+))?/))?y[1]&&y[1].length?+y[1]:!0:!1);y&&B(d,y,v);H({j:m,h:d,m:y,l:v,a:null,i:null,c:null,
g:null})}}}r<p.length?R.setTimeout(f,250):"function"===typeof a&&a()}for(var b=d||document.body,g=b.ownerDocument||document,b=[b.getElementsByTagName("pre"),b.getElementsByTagName("code"),b.getElementsByTagName("xmp")],p=[],k=0;k<b.length;++k)for(var m=0,q=b[k].length;m<q;++m)p.push(b[k][m]);var b=null,c=Date;c.now||(c={now:function(){return+new Date}});var r=0,t=/\blang(?:uage)?-([\w.]+)(?!\S)/,u=/\bprettyprint\b/,e=/\bprettyprinted\b/,x=/pre|xmp/i,z=/^code$/i,w=/^(?:pre|code|xmp)$/i,h={};f()}},
S=R.define;"function"===typeof S&&S.amd&&S("google-code-prettify",[],function(){return V})})();return g}();T||t.setTimeout(U,0)})();}()

View File

@ -0,0 +1 @@
pre .str,code .str{color:#65b042}pre .kwd,code .kwd{color:#e28964}pre .com,code .com{color:#aeaeae;font-style:italic}pre .typ,code .typ{color:#89bdff}pre .lit,code .lit{color:#3387cc}pre .pun,code .pun{color:#fff}pre .pln,code .pln{color:#fff}pre .tag,code .tag{color:#89bdff}pre .atn,code .atn{color:#bdb76b}pre .atv,code .atv{color:#65b042}pre .dec,code .dec{color:#3387cc}pre.prettyprint,code.prettyprint{background-color:#000;-moz-border-radius:8px;-webkit-border-radius:8px;-o-border-radius:8px;-ms-border-radius:8px;-khtml-border-radius:8px;border-radius:8px}pre.prettyprint{width:95%;margin:1em auto;padding:1em;white-space:pre-wrap}ol.linenums{margin-top:0;margin-bottom:0;color:#aeaeae}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}@media print{pre .str,code .str{color:#060}pre .kwd,code .kwd{color:#006;font-weight:bold}pre .com,code .com{color:#600;font-style:italic}pre .typ,code .typ{color:#404;font-weight:bold}pre .lit,code .lit{color:#044}pre .pun,code .pun{color:#440}pre .pln,code .pln{color:#000}pre .tag,code .tag{color:#006;font-weight:bold}pre .atn,code .atn{color:#404}pre .atv,code .atv{color:#060}}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,96 @@
/*
Atom One Dark by Daniel Gamage
Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax
base: #282c34
mono-1: #abb2bf
mono-2: #818896
mono-3: #5c6370
hue-1: #56b6c2
hue-2: #61aeee
hue-3: #c678dd
hue-4: #98c379
hue-5: #e06c75
hue-5-2: #be5046
hue-6: #d19a66
hue-6-2: #e6c07b
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #abb2bf;
background: #282c34;
}
.hljs-comment,
.hljs-quote {
color: #5c6370;
font-style: italic;
}
.hljs-doctag,
.hljs-keyword,
.hljs-formula {
color: #c678dd;
}
.hljs-section,
.hljs-name,
.hljs-selector-tag,
.hljs-deletion,
.hljs-subst {
color: #e06c75;
}
.hljs-literal {
color: #56b6c2;
}
.hljs-string,
.hljs-regexp,
.hljs-addition,
.hljs-attr,
.hljs-meta-string {
color: #98c379;
}
.hljs-built_in,
.hljs-class .hljs-title {
color: #e6c07b;
}
.hljs-attr,
.hljs-variable,
.hljs-template-variable,
.hljs-type,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-number {
color: #d19a66;
}
.hljs-symbol,
.hljs-bullet,
.hljs-link,
.hljs-meta,
.hljs-selector-id,
.hljs-title {
color: #61aeee;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
.hljs-link {
text-decoration: underline;
}

File diff suppressed because one or more lines are too long