parent
4b3f974b56
commit
9d2495aa64
@ -0,0 +1,22 @@ |
||||
{ |
||||
"env": { |
||||
"browser": true, |
||||
"jquery": true |
||||
}, |
||||
"extends": [ |
||||
"eslint:recommended", |
||||
"plugin:vue/essential" |
||||
], |
||||
"parserOptions": { |
||||
"ecmaVersion": 6, |
||||
"sourceType": "module" |
||||
}, |
||||
"plugins": [ |
||||
"vue" |
||||
], |
||||
"globals": { |
||||
"Vue": "readable" |
||||
}, |
||||
"rules": { |
||||
} |
||||
} |
@ -0,0 +1,79 @@ |
||||
""" Manage the content documents. """ |
||||
|
||||
import os |
||||
import io |
||||
import glob |
||||
|
||||
from flask import jsonify, send_file, url_for, abort |
||||
|
||||
from asl_rulebook2.webapp import app |
||||
from asl_rulebook2.webapp.utils import change_extn, slugify |
||||
|
||||
content_docs = None |
||||
|
||||
# --------------------------------------------------------------------- |
||||
|
||||
def load_content_docs(): |
||||
"""Load the content documents from the data directory.""" |
||||
|
||||
# initialize |
||||
global content_docs |
||||
content_docs = {} |
||||
dname = app.config.get( "DATA_DIR" ) |
||||
if not dname: |
||||
return |
||||
if not os.path.dirname( dname ): |
||||
raise RuntimeError( "Invalid data directory: {}".format( dname ) ) |
||||
|
||||
def get_doc( content_doc, key, fname, binary=False ): |
||||
fname = os.path.join( dname, fname ) |
||||
if not os.path.isfile( fname ): |
||||
return |
||||
kwargs = {} |
||||
kwargs["mode"] = "rb" if binary else "r" |
||||
if not binary: |
||||
kwargs["encoding"] = "utf-8" |
||||
with open( fname, **kwargs ) as fp: |
||||
content_doc[ key ] = fp.read() |
||||
|
||||
# load each content doc |
||||
fspec = os.path.join( dname, "*.index" ) |
||||
for fname in glob.glob( fspec ): |
||||
fname = os.path.basename( fname ) |
||||
title = os.path.splitext( fname )[0] |
||||
content_doc = { |
||||
"doc_id": slugify( title ), |
||||
"title": title, |
||||
} |
||||
get_doc( content_doc, "index", fname ) |
||||
get_doc( content_doc, "targets", change_extn(fname,".targets") ) |
||||
get_doc( content_doc, "footnotes", change_extn(fname,".footnotes") ) |
||||
get_doc( content_doc, "content", change_extn(fname,".pdf"), binary=True ) |
||||
content_docs[ content_doc["doc_id"] ] = content_doc |
||||
|
||||
# --------------------------------------------------------------------- |
||||
|
||||
@app.route( "/content-docs" ) |
||||
def get_content_docs(): |
||||
"""Return the available content docs.""" |
||||
resp = {} |
||||
for cdoc in content_docs.values(): |
||||
cdoc2 = { |
||||
"docId": cdoc["doc_id"], |
||||
"title": cdoc["title"], |
||||
} |
||||
if "content" in cdoc: |
||||
cdoc2["url"] = url_for( "get_content", doc_id=cdoc["doc_id"] ) |
||||
resp[ cdoc["doc_id"] ] = cdoc2 |
||||
return jsonify( resp ) |
||||
|
||||
# --------------------------------------------------------------------- |
||||
|
||||
@app.route( "/content/<doc_id>" ) |
||||
def get_content( doc_id ): |
||||
"""Return the content for the specified document.""" |
||||
cdoc = content_docs.get( doc_id ) |
||||
if not cdoc or "content" not in cdoc: |
||||
abort( 404 ) |
||||
buf = io.BytesIO( cdoc["content"] ) |
||||
return send_file( buf, mimetype="application/pdf" ) |
@ -0,0 +1,40 @@ |
||||
import { gMainApp, gEventBus, gUrlParams } from "./MainApp.js" ; |
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
gMainApp.component( "content-pane", { |
||||
|
||||
props: [ "contentDocs" ], |
||||
|
||||
template: ` |
||||
<tabbed-pages ref="tabbedPages"> |
||||
<tabbed-page v-for="doc in contentDocs" :tabId=doc.docId :caption=doc.title > |
||||
<content-doc :doc=doc /> |
||||
</tabbed-page> |
||||
</tabbed-pages>`, |
||||
|
||||
mounted() { |
||||
gEventBus.on( "show-content-doc", (docId) => { |
||||
this.$refs.tabbedPages.activateTab( docId ) ; // nb: tabId == docId
|
||||
} ) ; |
||||
}, |
||||
|
||||
} ) ; |
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
gMainApp.component( "content-doc", { |
||||
|
||||
props: [ "doc" ], |
||||
data() { return { |
||||
noContent: gUrlParams.get( "no-content" ), |
||||
} ; }, |
||||
|
||||
template: ` |
||||
<div class="content-doc"> |
||||
<div v-if=noContent class="disabled"> Content disabled. </div> |
||||
<iframe v-else-if=doc.url :src=doc.url /> |
||||
<div v-else class="disabled"> No content. </div> |
||||
</div>`, |
||||
|
||||
} ) ; |
@ -0,0 +1,70 @@ |
||||
import { showErrorMsg } from "./utils.js" ; |
||||
|
||||
// parse any URL parameters
|
||||
export let gUrlParams = new URLSearchParams( window.location.search.substring(1) ) ; |
||||
|
||||
// create the main application
|
||||
export const gMainApp = Vue.createApp( { //eslint-disable-line no-undef
|
||||
template: "<main-app />", |
||||
} ) ; |
||||
export const gEventBus = new TinyEmitter() ; //eslint-disable-line no-undef
|
||||
$(document).ready( () => { |
||||
gMainApp.mount( "#main-app" ) ; |
||||
} ) ; |
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
gMainApp.component( "main-app", { |
||||
|
||||
data() { return { |
||||
contentDocs: [], |
||||
isLoaded: false, |
||||
} ; }, |
||||
|
||||
template: ` |
||||
<nav-pane id="nav" /> |
||||
<content-pane id="content" :contentDocs=contentDocs /> |
||||
<div v-if=isLoaded id="_mainapp-loaded_" /> |
||||
`,
|
||||
|
||||
mounted() { |
||||
// initialize the splitter
|
||||
Split( [ "#nav", "#content" ], { //eslint-disable-line no-undef
|
||||
direction: "horizontal", |
||||
sizes: [ 25, 75 ], |
||||
gutterSize: 2, |
||||
} ) ; |
||||
// initialze the webapp
|
||||
// NOTE: We don't provide a catch handler, since each individual Promise should report
|
||||
// their own errors i.e. what could we do here, other than show a generic "startup failed" error?
|
||||
Promise.all( [ |
||||
this.getContentDocs( this ), |
||||
] ).then( () => { |
||||
this.isLoaded = true ; |
||||
$( "#query-string" ).focus() ; // nb: because autofocus on the <input> doesn't work :-/
|
||||
} ) ; |
||||
}, |
||||
|
||||
methods: { |
||||
|
||||
getContentDocs: (self) => new Promise( (resolve, reject) => { |
||||
// get the content docs
|
||||
$.getJSON( gGetContentDocsUrl, (resp) => { //eslint-disable-line no-undef
|
||||
self.contentDocs = resp ; |
||||
let docIds = Object.keys( resp ) ; |
||||
if ( docIds.length > 0 ) { |
||||
Vue.nextTick( () => { |
||||
gEventBus.emit( "show-content-doc", docIds[0] ) ; // FIXME! which one do we choose?
|
||||
} ) ; |
||||
} |
||||
resolve() ; |
||||
} ).fail( (xhr, status, errorMsg) => { |
||||
const msg = "Couldn't get the content docs." ; |
||||
showErrorMsg( msg + " <div class='pre'>" + errorMsg + "</div>" ) ; |
||||
reject( msg ) |
||||
} ) ; |
||||
} ), |
||||
|
||||
}, |
||||
|
||||
} ) ; |
@ -0,0 +1,23 @@ |
||||
import { gMainApp, gEventBus } from "./MainApp.js" ; |
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
gMainApp.component( "nav-pane", { |
||||
|
||||
template: ` |
||||
<tabbed-pages> |
||||
<tabbed-page tabId="search" caption="Search" data-display="flex" > |
||||
<search-box id="search-box" @search=onSearch /> |
||||
<search-results id="search-results" /> |
||||
</tabbed-page> |
||||
</tabbed-pages>`, |
||||
|
||||
methods: { |
||||
|
||||
onSearch: (queryString) => { |
||||
gEventBus.emit( "search", queryString ) ; |
||||
}, |
||||
|
||||
}, |
||||
|
||||
} ) ; |
@ -0,0 +1,83 @@ |
||||
import { gMainApp, gEventBus } from "./MainApp.js" ; |
||||
import { IndexSearchResult } from "./SearchResult.js" ; |
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
gMainApp.component( "search-panel", { |
||||
|
||||
template: "<search-box /> <search-results />", |
||||
|
||||
} ) ; |
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
gMainApp.component( "search-box", { |
||||
|
||||
data: function() { return { |
||||
queryString: "", |
||||
} ; }, |
||||
|
||||
template: ` |
||||
<div> |
||||
<input type="text" id="query-string" @keyup=onKeyUp v-model.trim="queryString" ref="queryString" autofocus > |
||||
<button @click="$emit('search',this.queryString)" ref=submit> Go </button> |
||||
</div>`, |
||||
|
||||
mounted: function() { |
||||
// initialize
|
||||
$( this.$refs.queryString ).addClass( "ui-widget ui-state-default ui-corner-all" ) ; |
||||
$( this.$refs.submit ).button() ; |
||||
}, |
||||
|
||||
methods: { |
||||
onKeyUp: function( evt ) { |
||||
if ( evt.keyCode == 13 ) |
||||
this.$refs["submit"].click() ; |
||||
} |
||||
}, |
||||
|
||||
} ) ; |
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
gMainApp.component( "search-results", { |
||||
|
||||
data() { return { |
||||
searchResults: [], |
||||
} ; }, |
||||
|
||||
template: `<div>
|
||||
<div v-for="sr in searchResults" :key=sr.key > |
||||
<index-sr v-if="sr.srType == 'index'" :sr=sr /> |
||||
<div v-else> ??? </div> |
||||
</div> |
||||
</div>`, |
||||
|
||||
mounted() { |
||||
gEventBus.on( "search", this.onSearch ) ; |
||||
}, |
||||
|
||||
methods: { |
||||
|
||||
onSearch( queryString ) { |
||||
// generate some dummy search results
|
||||
let searchResults = [] ; |
||||
for ( let i=0 ; i < queryString.length ; ++i ) { |
||||
let buf = [ "Search result #" + (1+i) ] ; |
||||
let nItems = Math.floor( Math.sqrt( 100 * Math.random() ) ) - 1 ; |
||||
if ( nItems > 0 ) { |
||||
buf.push( "<ul style='padding-left:1em;'>" ) ; |
||||
for ( let j=0 ; j < nItems ; ++j ) |
||||
buf.push( "<li> item " + (1+j) ) ; |
||||
buf.push( "</ul>" ) ; |
||||
} |
||||
searchResults.push( |
||||
new IndexSearchResult( i, buf.join("") ) |
||||
) ; |
||||
} |
||||
this.searchResults = searchResults ; |
||||
}, |
||||
|
||||
}, |
||||
|
||||
} ) ; |
@ -0,0 +1,23 @@ |
||||
import { gMainApp } from "./MainApp.js" ; |
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
export class IndexSearchResult { |
||||
constructor( key, content ) { |
||||
this.key = key ; |
||||
this.srType = "index" ; |
||||
this.content = content ; |
||||
} |
||||
} |
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
gMainApp.component( "index-sr", { |
||||
|
||||
props: [ "sr" ], |
||||
|
||||
template: ` |
||||
<div class="sr index-sr" v-html=sr.content /> |
||||
`,
|
||||
|
||||
} ) ; |
@ -0,0 +1,80 @@ |
||||
import { gMainApp, gEventBus } from "./MainApp.js" ; |
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
gMainApp.component( "tabbed-pages", { |
||||
|
||||
data: function() { return { |
||||
tabs: [], |
||||
activeTabId: null, |
||||
} ; }, |
||||
|
||||
template: ` |
||||
<div class="tabbed-pages"> |
||||
<slot /> |
||||
<div class="tab-strip"> |
||||
<div v-for="tab in tabs" :data-tabid=tab.tabId @click=onTabClicked class="tab" v-bind:class="{'active': tab.tabId == activeTabId}" > |
||||
{{tab.caption}} |
||||
</div> |
||||
</div> |
||||
</div>`, |
||||
|
||||
created() { |
||||
// FUDGE! It's nice to have the parent manage the TabbedPage's, and we just show the content
|
||||
// as a slot, but that makes it tricky for us to create the tab strip, since we don't know anything
|
||||
// about the tabs themselves. We work around this by having each TabbedPage emit an event when
|
||||
// they mount, but since we ourself mount only after all our children have mounted, it's tricky
|
||||
// for us to catch these events ($on() was remove in Vue 3 :-/). So, we emit the event on the
|
||||
// global event bus, and check if they're for one of our TabbedPage's when we receive them.
|
||||
gEventBus.on( "tab-loaded", (tabbedPage) => { |
||||
if ( ! tabbedPage.$el.parentNode.isSameNode( this.$el ) ) |
||||
return ; |
||||
// one of our TabbedPage's has just mounted - show it in our tab strip
|
||||
this.tabs.push( { |
||||
tabId: tabbedPage.tabId, caption: tabbedPage.caption |
||||
} ) ; |
||||
} ) ; |
||||
}, |
||||
|
||||
mounted() { |
||||
// start with the first tab activated
|
||||
if ( this.tabs.length > 0 ) |
||||
this.activateTab( this.tabs[0].tabId ) ; |
||||
}, |
||||
|
||||
methods: { |
||||
|
||||
onTabClicked: function( evt ) { |
||||
// activate the selected tab
|
||||
this.activateTab( evt.target.dataset.tabid ) ; |
||||
}, |
||||
|
||||
activateTab: function( tabId ) { |
||||
// activate the specified tab
|
||||
this.activeTabId = tabId ; |
||||
$( this.$el ).find( ".tabbed-page" ).each( function() { |
||||
let displayStyle = $(this).data("display") || "block" ; |
||||
$(this).css( "display", ($(this).data("tabid") == tabId) ? displayStyle : "none" ) ; |
||||
} ) ; |
||||
}, |
||||
|
||||
}, |
||||
|
||||
} ) ; |
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
gMainApp.component( "tabbed-page", { |
||||
|
||||
props: [ "tabId", "caption", "isActive" ], |
||||
|
||||
template: ` |
||||
<div :data-tabid=tabId v-show=isActive class="tabbed-page" > |
||||
<slot /> |
||||
</div>`, |
||||
|
||||
mounted() { |
||||
gEventBus.emit( "tab-loaded", this ) ; |
||||
}, |
||||
|
||||
} ) ; |
@ -0,0 +1,10 @@ |
||||
#content { position: relative ; } |
||||
|
||||
#content .tabbed-page { border-bottom: 1px solid #aaa ; } |
||||
#content .tab-strip { padding: 0 0.5em ; } |
||||
#content .tab-strip .tab { margin-top: -1px ; z-index: 5 ; border-radius: 0 0 5px 5px ; } |
||||
#content .tab-strip .tab { background: #f0f0f0 ; color: #808080 ; } |
||||
#content .tab-strip .tab.active { background: white ; color: #444 ; border-top: none ; } |
||||
|
||||
#content .content-doc iframe { position: absolute ; width: 100% ; height: calc(100% - 35px) ; border: none ; } |
||||
#content .content-doc .disabled { margin-top: 1em ; text-align: center ; font-style: italic ; color: #888 ; } |
@ -0,0 +1,6 @@ |
||||
#main-app { width: 100% ; height: 100% ; display: flex ; } |
||||
|
||||
/* splitter */ |
||||
#nav { min-width: 300px ; } |
||||
#content { flex-grow: 1 ; min-width: 500px ; } |
||||
#main-app .gutter.gutter-horizontal { cursor: ew-resize ; background: #ccc ; } |
@ -0,0 +1,3 @@ |
||||
#nav { padding: 5px ; } |
||||
|
||||
#nav .tabbed-page { height: calc(100% - 25px) ; padding: 2px ; } |
@ -0,0 +1,8 @@ |
||||
#nav .tabbed-page[data-tabid="search"] { display: flex ; flex-direction: column ; } |
||||
|
||||
/* search box */ |
||||
#search-box { display: flex ; } |
||||
#search-box input#query-string { margin-right: 5px ; flex-grow: 1 ; } |
||||
|
||||
/* search results */ |
||||
#search-results { flex-grow: -1 ; margin: 8px 0 2px 0 ; overflow-y: auto ; } |
@ -0,0 +1 @@ |
||||
#search-results .sr { margin: 0 10px 2px 0 ; border: 1px dotted #666 ; padding: 5px ; } |
@ -0,0 +1,4 @@ |
||||
.tabbed-pages { display: flex ; flex-direction: column ; } |
||||
.tabbed-pages .tabbed-page { height: calc(100% - 24px) ; } |
||||
.tabbed-pages .tab-strip { height: 24px ; display: flex ; font-size: 10pt ; font-style: italic ; } |
||||
.tabbed-pages .tab-strip .tab { border: 1px solid #aaa ; padding: 2px 5px ; margin-right: 5px ; } |
@ -0,0 +1,15 @@ |
||||
*/* global reset */ |
||||
* { margin: 0 ; padding: 0 } |
||||
html { height: 100% ; } |
||||
body { height: 100% ; overflow: hidden ; } |
||||
|
||||
/* general styling */ |
||||
body { font-family: Arial, Helvetica, sans-serif ; font-size: 16px ; } |
||||
input[type="text"] { height: 22px ; padding: 0 5px ; } |
||||
button { height: 24px ; padding: 0 5px !important ; } |
||||
|
||||
/* notification balloons */ |
||||
.growl .growl-close { position: absolute ; top: 0 ; right: 6px ; } |
||||
.growl .growl-title { display: none ; } |
||||
.growl .pre { font-family: monospace ; } |
||||
.growl div.pre { margin: 0 0 15px 15px ; font-size: 80% ; } |
@ -0,0 +1,96 @@ |
||||
/* jQuery Growl |
||||
* Copyright 2015 Kevin Sylvestre |
||||
* 1.3.5 |
||||
*/ |
||||
.ontop, #growls-default, #growls-tl, #growls-tr, #growls-bl, #growls-br, #growls-tc, #growls-bc, #growls-cc, #growls-cl, #growls-cr { |
||||
z-index: 50000; |
||||
position: fixed; } |
||||
|
||||
#growls-default { |
||||
top: 10px; |
||||
right: 10px; } |
||||
#growls-tl { |
||||
top: 10px; |
||||
left: 10px; } |
||||
#growls-tr { |
||||
top: 10px; |
||||
right: 10px; } |
||||
#growls-bl { |
||||
bottom: 10px; |
||||
left: 10px; } |
||||
#growls-br { |
||||
bottom: 10px; |
||||
right: 10px; } |
||||
#growls-tc { |
||||
top: 10px; |
||||
right: 10px; |
||||
left: 10px; } |
||||
#growls-bc { |
||||
bottom: 10px; |
||||
right: 10px; |
||||
left: 10px; } |
||||
#growls-cc { |
||||
top: 50%; |
||||
left: 50%; |
||||
margin-left: -125px; } |
||||
#growls-cl { |
||||
top: 50%; |
||||
left: 10px; } |
||||
#growls-cr { |
||||
top: 50%; |
||||
right: 10px; } |
||||
#growls-tc .growl, #growls-bc .growl { |
||||
margin-left: auto; |
||||
margin-right: auto; } |
||||
|
||||
.growl { |
||||
opacity: 0.8; |
||||
filter: alpha(opacity=80); |
||||
position: relative; |
||||
border-radius: 4px; |
||||
-webkit-transition: all 0.4s ease-in-out; |
||||
-moz-transition: all 0.4s ease-in-out; |
||||
transition: all 0.4s ease-in-out; } |
||||
.growl.growl-incoming { |
||||
opacity: 0; |
||||
filter: alpha(opacity=0); } |
||||
.growl.growl-outgoing { |
||||
opacity: 0; |
||||
filter: alpha(opacity=0); } |
||||
.growl.growl-small { |
||||
width: 200px; |
||||
padding: 5px; |
||||
margin: 5px; } |
||||
.growl.growl-medium { |
||||
width: 250px; |
||||
padding: 10px; |
||||
margin: 10px; } |
||||
.growl.growl-large { |
||||
width: 300px; |
||||
padding: 15px; |
||||
margin: 15px; } |
||||
.growl.growl-default { |
||||
color: #FFF; |
||||
background: #7f8c8d; } |
||||
.growl.growl-error { |
||||
color: #FFF; |
||||
background: #C0392B; } |
||||
.growl.growl-notice { |
||||
color: #FFF; |
||||
background: #2ECC71; } |
||||
.growl.growl-warning { |
||||
color: #FFF; |
||||
background: #F39C12; } |
||||
.growl .growl-close { |
||||
cursor: pointer; |
||||
float: right; |
||||
font-size: 14px; |
||||
line-height: 18px; |
||||
font-weight: normal; |
||||
font-family: helvetica, verdana, sans-serif; } |
||||
.growl .growl-title { |
||||
font-size: 18px; |
||||
line-height: 24px; } |
||||
.growl .growl-message { |
||||
font-size: 14px; |
||||
line-height: 18px; } |
@ -0,0 +1,311 @@ |
||||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); |
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } |
||||
|
||||
// Generated by CoffeeScript 2.1.0
|
||||
(function () { |
||||
/* |
||||
jQuery Growl |
||||
Copyright 2015 Kevin Sylvestre |
||||
1.3.5 |
||||
*/ |
||||
"use strict"; |
||||
|
||||
var $, Animation, Growl; |
||||
|
||||
$ = jQuery; |
||||
|
||||
Animation = function () { |
||||
var Animation = function () { |
||||
function Animation() { |
||||
_classCallCheck(this, Animation); |
||||
} |
||||
|
||||
_createClass(Animation, null, [{ |
||||
key: "transition", |
||||
value: function transition($el) { |
||||
var el, ref, result, type; |
||||
el = $el[0]; |
||||
ref = this.transitions; |
||||
for (type in ref) { |
||||
result = ref[type]; |
||||
if (el.style[type] != null) { |
||||
return result; |
||||
} |
||||
} |
||||
} |
||||
}]); |
||||
|
||||
return Animation; |
||||
}(); |
||||
|
||||
; |
||||
|
||||
Animation.transitions = { |
||||
"webkitTransition": "webkitTransitionEnd", |
||||
"mozTransition": "mozTransitionEnd", |
||||
"oTransition": "oTransitionEnd", |
||||
"transition": "transitionend" |
||||
}; |
||||
|
||||
return Animation; |
||||
}(); |
||||
|
||||
Growl = function () { |
||||
var Growl = function () { |
||||
_createClass(Growl, null, [{ |
||||
key: "growl", |
||||
value: function growl() { |
||||
var settings = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; |
||||
|
||||
return new Growl(settings); |
||||
} |
||||
}]); |
||||
|
||||
function Growl() { |
||||
var settings = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; |
||||
|
||||
_classCallCheck(this, Growl); |
||||
|
||||
this.render = this.render.bind(this); |
||||
this.bind = this.bind.bind(this); |
||||
this.unbind = this.unbind.bind(this); |
||||
this.mouseEnter = this.mouseEnter.bind(this); |
||||
this.mouseLeave = this.mouseLeave.bind(this); |
||||
this.click = this.click.bind(this); |
||||
this.close = this.close.bind(this); |
||||
this.cycle = this.cycle.bind(this); |
||||
this.waitAndDismiss = this.waitAndDismiss.bind(this); |
||||
this.present = this.present.bind(this); |
||||
this.dismiss = this.dismiss.bind(this); |
||||
this.remove = this.remove.bind(this); |
||||
this.animate = this.animate.bind(this); |
||||
this.$growls = this.$growls.bind(this); |
||||
this.$growl = this.$growl.bind(this); |
||||
this.html = this.html.bind(this); |
||||
this.content = this.content.bind(this); |
||||
this.container = this.container.bind(this); |
||||
this.settings = $.extend({}, Growl.settings, settings); |
||||
this.initialize(this.settings.location); |
||||
this.render(); |
||||
} |
||||
|
||||
_createClass(Growl, [{ |
||||
key: "initialize", |
||||
value: function initialize(location) { |
||||
var id; |
||||
id = 'growls-' + location; |
||||
return $('body:not(:has(#' + id + '))').append('<div id="' + id + '" />'); |
||||
} |
||||
}, { |
||||
key: "render", |
||||
value: function render() { |
||||
var $growl; |
||||
$growl = this.$growl(); |
||||
this.$growls(this.settings.location).append($growl); |
||||
if (this.settings.fixed) { |
||||
this.present(); |
||||
} else { |
||||
this.cycle(); |
||||
} |
||||
} |
||||
}, { |
||||
key: "bind", |
||||
value: function bind() { |
||||
var $growl = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.$growl(); |
||||
|
||||
$growl.on("click", this.click); |
||||
if (this.settings.delayOnHover) { |
||||
$growl.on("mouseenter", this.mouseEnter); |
||||
$growl.on("mouseleave", this.mouseLeave); |
||||
} |
||||
return $growl.on("contextmenu", this.close).find("." + this.settings.namespace + "-close").on("click", this.close); |
||||
} |
||||
}, { |
||||
key: "unbind", |
||||
value: function unbind() { |
||||
var $growl = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.$growl(); |
||||
|
||||
$growl.off("click", this.click); |
||||
if (this.settings.delayOnHover) { |
||||
$growl.off("mouseenter", this.mouseEnter); |
||||
$growl.off("mouseleave", this.mouseLeave); |
||||
} |
||||
return $growl.off("contextmenu", this.close).find("." + this.settings.namespace + "-close").off("click", this.close); |
||||
} |
||||
}, { |
||||
key: "mouseEnter", |
||||
value: function mouseEnter(event) { |
||||
var $growl; |
||||
$growl = this.$growl(); |
||||
return $growl.stop(true, true); |
||||
} |
||||
}, { |
||||
key: "mouseLeave", |
||||
value: function mouseLeave(event) { |
||||
return this.waitAndDismiss(); |
||||
} |
||||
}, { |
||||
key: "click", |
||||
value: function click(event) { |
||||
if (this.settings.url != null) { |
||||
event.preventDefault(); |
||||
event.stopPropagation(); |
||||
return window.open(this.settings.url); |
||||
} |
||||
} |
||||
}, { |
||||
key: "close", |
||||
value: function close(event) { |
||||
var $growl; |
||||
event.preventDefault(); |
||||
event.stopPropagation(); |
||||
$growl = this.$growl(); |
||||
return $growl.stop().queue(this.dismiss).queue(this.remove); |
||||
} |
||||
}, { |
||||
key: "cycle", |
||||
value: function cycle() { |
||||
var $growl; |
||||
$growl = this.$growl(); |
||||
return $growl.queue(this.present).queue(this.waitAndDismiss()); |
||||
} |
||||
}, { |
||||
key: "waitAndDismiss", |
||||
value: function waitAndDismiss() { |
||||
var $growl; |
||||
$growl = this.$growl(); |
||||
return $growl.delay(this.settings.duration).queue(this.dismiss).queue(this.remove); |
||||
} |
||||
}, { |
||||
key: "present", |
||||
value: function present(callback) { |
||||
var $growl; |
||||
$growl = this.$growl(); |
||||
this.bind($growl); |
||||
return this.animate($growl, this.settings.namespace + "-incoming", 'out', callback); |
||||
} |
||||
}, { |
||||
key: "dismiss", |
||||
value: function dismiss(callback) { |
||||
var $growl; |
||||
$growl = this.$growl(); |
||||
this.unbind($growl); |
||||
return this.animate($growl, this.settings.namespace + "-outgoing", 'in', callback); |
||||
} |
||||
}, { |
||||
key: "remove", |
||||
value: function remove(callback) { |
||||
this.$growl().remove(); |
||||
return typeof callback === "function" ? callback() : void 0; |
||||
} |
||||
}, { |
||||
key: "animate", |
||||
value: function animate($element, name) { |
||||
var direction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'in'; |
||||
var callback = arguments[3]; |
||||
|
||||
var transition; |
||||
transition = Animation.transition($element); |
||||
$element[direction === 'in' ? 'removeClass' : 'addClass'](name); |
||||
$element.offset().position; |
||||
$element[direction === 'in' ? 'addClass' : 'removeClass'](name); |
||||
if (callback == null) { |
||||
return; |
||||
} |
||||
if (transition != null) { |
||||
$element.one(transition, callback); |
||||
} else { |
||||
callback(); |
||||
} |
||||
} |
||||
}, { |
||||
key: "$growls", |
||||
value: function $growls(location) { |
||||
var base; |
||||
if (this.$_growls == null) { |
||||
this.$_growls = []; |
||||
} |
||||
return (base = this.$_growls)[location] != null ? base[location] : base[location] = $('#growls-' + location); |
||||
} |
||||
}, { |
||||
key: "$growl", |
||||
value: function $growl() { |
||||
return this.$_growl != null ? this.$_growl : this.$_growl = $(this.html()); |
||||
} |
||||
}, { |
||||
key: "html", |
||||
value: function html() { |
||||
return this.container(this.content()); |
||||
} |
||||
}, { |
||||
key: "content", |
||||
value: function content() { |
||||
return "<div class='" + this.settings.namespace + "-close'>" + this.settings.close + "</div>\n<div class='" + this.settings.namespace + "-title'>" + this.settings.title + "</div>\n<div class='" + this.settings.namespace + "-message'>" + this.settings.message + "</div>"; |
||||
} |
||||
}, { |
||||
key: "container", |
||||
value: function container(content) { |
||||
return "<div class='" + this.settings.namespace + " " + this.settings.namespace + "-" + this.settings.style + " " + this.settings.namespace + "-" + this.settings.size + "'>\n " + content + "\n</div>"; |
||||
} |
||||
}]); |
||||
|
||||
return Growl; |
||||
}(); |
||||
|
||||
; |
||||
|
||||
Growl.settings = { |
||||
namespace: 'growl', |
||||
duration: 3200, |
||||
close: "×", |
||||
location: "default", |
||||
style: "default", |
||||
size: "medium", |
||||
delayOnHover: true |
||||
}; |
||||
|
||||
return Growl; |
||||
}(); |
||||
|
||||
this.Growl = Growl; |
||||
|
||||
$.growl = function () { |
||||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; |
||||
|
||||
return Growl.growl(options); |
||||
}; |
||||
|
||||
$.growl.error = function () { |
||||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; |
||||
|
||||
var settings; |
||||
settings = { |
||||
title: "Error!", |
||||
style: "error" |
||||
}; |
||||
return $.growl($.extend(settings, options)); |
||||
}; |
||||
|
||||
$.growl.notice = function () { |
||||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; |
||||
|
||||
var settings; |
||||
settings = { |
||||
title: "Notice!", |
||||
style: "notice" |
||||
}; |
||||
return $.growl($.extend(settings, options)); |
||||
}; |
||||
|
||||
$.growl.warning = function () { |
||||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; |
||||
|
||||
var settings; |
||||
settings = { |
||||
title: "Warning!", |
||||
style: "warning" |
||||
}; |
||||
return $.growl($.extend(settings, options)); |
||||
}; |
||||
}).call(this); |
@ -0,0 +1,769 @@ |
||||
/*! Split.js - v1.6.2 */ |
||||
|
||||
(function (global, factory) { |
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : |
||||
typeof define === 'function' && define.amd ? define(factory) : |
||||
(global = global || self, global.Split = factory()); |
||||
}(this, (function () { 'use strict'; |
||||
|
||||
// The programming goals of Split.js are to deliver readable, understandable and
|
||||
// maintainable code, while at the same time manually optimizing for tiny minified file size,
|
||||
// browser compatibility without additional requirements
|
||||
// and very few assumptions about the user's page layout.
|
||||
var global = typeof window !== 'undefined' ? window : null; |
||||
var ssr = global === null; |
||||
var document = !ssr ? global.document : undefined; |
||||
|
||||
// Save a couple long function names that are used frequently.
|
||||
// This optimization saves around 400 bytes.
|
||||
var addEventListener = 'addEventListener'; |
||||
var removeEventListener = 'removeEventListener'; |
||||
var getBoundingClientRect = 'getBoundingClientRect'; |
||||
var gutterStartDragging = '_a'; |
||||
var aGutterSize = '_b'; |
||||
var bGutterSize = '_c'; |
||||
var HORIZONTAL = 'horizontal'; |
||||
var NOOP = function () { return false; }; |
||||
|
||||
// Helper function determines which prefixes of CSS calc we need.
|
||||
// We only need to do this once on startup, when this anonymous function is called.
|
||||
//
|
||||
// Tests -webkit, -moz and -o prefixes. Modified from StackOverflow:
|
||||
// http://stackoverflow.com/questions/16625140/js-feature-detection-to-detect-the-usage-of-webkit-calc-over-calc/16625167#16625167
|
||||
var calc = ssr |
||||
? 'calc' |
||||
: ((['', '-webkit-', '-moz-', '-o-'] |
||||
.filter(function (prefix) { |
||||
var el = document.createElement('div'); |
||||
el.style.cssText = "width:" + prefix + "calc(9px)"; |
||||
|
||||
return !!el.style.length |
||||
}) |
||||
.shift()) + "calc"); |
||||
|
||||
// Helper function checks if its argument is a string-like type
|
||||
var isString = function (v) { return typeof v === 'string' || v instanceof String; }; |
||||
|
||||
// Helper function allows elements and string selectors to be used
|
||||
// interchangeably. In either case an element is returned. This allows us to
|
||||
// do `Split([elem1, elem2])` as well as `Split(['#id1', '#id2'])`.
|
||||
var elementOrSelector = function (el) { |
||||
if (isString(el)) { |
||||
var ele = document.querySelector(el); |
||||
if (!ele) { |
||||
throw new Error(("Selector " + el + " did not match a DOM element")) |
||||
} |
||||
return ele |
||||
} |
||||
|
||||
return el |
||||
}; |
||||
|
||||
// Helper function gets a property from the properties object, with a default fallback
|
||||
var getOption = function (options, propName, def) { |
||||
var value = options[propName]; |
||||
if (value !== undefined) { |
||||
return value |
||||
} |
||||
return def |
||||
}; |
||||
|
||||
var getGutterSize = function (gutterSize, isFirst, isLast, gutterAlign) { |
||||
if (isFirst) { |
||||
if (gutterAlign === 'end') { |
||||
return 0 |
||||
} |
||||
if (gutterAlign === 'center') { |
||||
return gutterSize / 2 |
||||
} |
||||
} else if (isLast) { |
||||
if (gutterAlign === 'start') { |
||||
return 0 |
||||
} |
||||
if (gutterAlign === 'center') { |
||||
return gutterSize / 2 |
||||
} |
||||
} |
||||
|
||||
return gutterSize |
||||
}; |
||||
|
||||
// Default options
|
||||
var defaultGutterFn = function (i, gutterDirection) { |
||||
var gut = document.createElement('div'); |
||||
gut.className = "gutter gutter-" + gutterDirection; |
||||
return gut |
||||
}; |
||||
|
||||
var defaultElementStyleFn = function (dim, size, gutSize) { |
||||
var style = {}; |
||||
|
||||
if (!isString(size)) { |
||||
style[dim] = calc + "(" + size + "% - " + gutSize + "px)"; |
||||
} else { |
||||
style[dim] = size; |
||||
} |
||||
|
||||
return style |
||||
}; |
||||
|
||||
var defaultGutterStyleFn = function (dim, gutSize) { |
||||
var obj; |
||||
|
||||
return (( obj = {}, obj[dim] = (gutSize + "px"), obj )); |
||||
}; |
||||
|
||||
// The main function to initialize a split. Split.js thinks about each pair
|
||||
// of elements as an independant pair. Dragging the gutter between two elements
|
||||
// only changes the dimensions of elements in that pair. This is key to understanding
|
||||
// how the following functions operate, since each function is bound to a pair.
|
||||
//
|
||||
// A pair object is shaped like this:
|
||||
//
|
||||
// {
|
||||
// a: DOM element,
|
||||
// b: DOM element,
|
||||
// aMin: Number,
|
||||
// bMin: Number,
|
||||
// dragging: Boolean,
|
||||
// parent: DOM element,
|
||||
// direction: 'horizontal' | 'vertical'
|
||||
// }
|
||||
//
|
||||
// The basic sequence:
|
||||
//
|
||||
// 1. Set defaults to something sane. `options` doesn't have to be passed at all.
|
||||
// 2. Initialize a bunch of strings based on the direction we're splitting.
|
||||
// A lot of the behavior in the rest of the library is paramatized down to
|
||||
// rely on CSS strings and classes.
|
||||
// 3. Define the dragging helper functions, and a few helpers to go with them.
|
||||
// 4. Loop through the elements while pairing them off. Every pair gets an
|
||||
// `pair` object and a gutter.
|
||||
// 5. Actually size the pair elements, insert gutters and attach event listeners.
|
||||
var Split = function (idsOption, options) { |
||||
if ( options === void 0 ) options = {}; |
||||
|
||||
if (ssr) { return {} } |
||||
|
||||
var ids = idsOption; |
||||
var dimension; |
||||
var clientAxis; |
||||
var position; |
||||
var positionEnd; |
||||
var clientSize; |
||||
var elements; |
||||
|
||||
// Allow HTMLCollection to be used as an argument when supported
|
||||
if (Array.from) { |
||||
ids = Array.from(ids); |
||||
} |
||||
|
||||
// All DOM elements in the split should have a common parent. We can grab
|
||||
// the first elements parent and hope users read the docs because the
|
||||
// behavior will be whacky otherwise.
|
||||
var firstElement = elementOrSelector(ids[0]); |
||||
var parent = firstElement.parentNode; |
||||
var parentStyle = getComputedStyle ? getComputedStyle(parent) : null; |
||||
var parentFlexDirection = parentStyle ? parentStyle.flexDirection : null; |
||||
|
||||
// Set default options.sizes to equal percentages of the parent element.
|
||||
var sizes = getOption(options, 'sizes') || ids.map(function () { return 100 / ids.length; }); |
||||
|
||||
// Standardize minSize to an array if it isn't already. This allows minSize
|
||||
// to be passed as a number.
|
||||
var minSize = getOption(options, 'minSize', 100); |
||||
var minSizes = Array.isArray(minSize) ? minSize : ids.map(function () { return minSize; }); |
||||
|
||||
// Get other options
|
||||
var expandToMin = getOption(options, 'expandToMin', false); |
||||
var gutterSize = getOption(options, 'gutterSize', 10); |
||||
var gutterAlign = getOption(options, 'gutterAlign', 'center'); |
||||
var snapOffset = getOption(options, 'snapOffset', 30); |
||||
var dragInterval = getOption(options, 'dragInterval', 1); |
||||
var direction = getOption(options, 'direction', HORIZONTAL); |
||||
var cursor = getOption( |
||||
options, |
||||
'cursor', |
||||
direction === HORIZONTAL ? 'col-resize' : 'row-resize' |
||||
); |
||||
var gutter = getOption(options, 'gutter', defaultGutterFn); |
||||
var elementStyle = getOption( |
||||
options, |
||||
'elementStyle', |
||||
defaultElementStyleFn |
||||
); |
||||
var gutterStyle = getOption(options, 'gutterStyle', defaultGutterStyleFn); |
||||
|
||||
// 2. Initialize a bunch of strings based on the direction we're splitting.
|
||||
// A lot of the behavior in the rest of the library is paramatized down to
|
||||
// rely on CSS strings and classes.
|
||||
if (direction === HORIZONTAL) { |
||||
dimension = 'width'; |
||||
clientAxis = 'clientX'; |
||||
position = 'left'; |
||||
positionEnd = 'right'; |
||||
clientSize = 'clientWidth'; |
||||
} else if (direction === 'vertical') { |
||||
dimension = 'height'; |
||||
clientAxis = 'clientY'; |
||||
position = 'top'; |
||||
positionEnd = 'bottom'; |
||||
clientSize = 'clientHeight'; |
||||
} |
||||
|
||||
// 3. Define the dragging helper functions, and a few helpers to go with them.
|
||||
// Each helper is bound to a pair object that contains its metadata. This
|
||||
// also makes it easy to store references to listeners that that will be
|
||||
// added and removed.
|
||||
//
|
||||
// Even though there are no other functions contained in them, aliasing
|
||||
// this to self saves 50 bytes or so since it's used so frequently.
|
||||
//
|
||||
// The pair object saves metadata like dragging state, position and
|
||||
// event listener references.
|
||||
|
||||
function setElementSize(el, size, gutSize, i) { |
||||
// Split.js allows setting sizes via numbers (ideally), or if you must,
|
||||
// by string, like '300px'. This is less than ideal, because it breaks
|
||||
// the fluid layout that `calc(% - px)` provides. You're on your own if you do that,
|
||||
// make sure you calculate the gutter size by hand.
|
||||
var style = elementStyle(dimension, size, gutSize, i); |
||||
|
||||
Object.keys(style).forEach(function (prop) { |
||||
// eslint-disable-next-line no-param-reassign
|
||||
el.style[prop] = style[prop]; |
||||
}); |
||||
} |
||||
|
||||
function setGutterSize(gutterElement, gutSize, i) { |
||||
var style = gutterStyle(dimension, gutSize, i); |
||||
|
||||
Object.keys(style).forEach(function (prop) { |
||||
// eslint-disable-next-line no-param-reassign
|
||||
gutterElement.style[prop] = style[prop]; |
||||
}); |
||||
} |
||||
|
||||
function getSizes() { |
||||
return elements.map(function (element) { return element.size; }) |
||||
} |
||||
|
||||
// Supports touch events, but not multitouch, so only the first
|
||||
// finger `touches[0]` is counted.
|
||||
function getMousePosition(e) { |
||||
if ('touches' in e) { return e.touches[0][clientAxis] } |
||||
return e[clientAxis] |
||||
} |
||||
|
||||
// Actually adjust the size of elements `a` and `b` to `offset` while dragging.
|
||||
// calc is used to allow calc(percentage + gutterpx) on the whole split instance,
|
||||
// which allows the viewport to be resized without additional logic.
|
||||
// Element a's size is the same as offset. b's size is total size - a size.
|
||||
// Both sizes are calculated from the initial parent percentage,
|
||||
// then the gutter size is subtracted.
|
||||
function adjust(offset) { |
||||
var a = elements[this.a]; |
||||
var b = elements[this.b]; |
||||
var percentage = a.size + b.size; |
||||
|
||||
a.size = (offset / this.size) * percentage; |
||||
b.size = percentage - (offset / this.size) * percentage; |
||||
|
||||
setElementSize(a.element, a.size, this[aGutterSize], a.i); |
||||
setElementSize(b.element, b.size, this[bGutterSize], b.i); |
||||
} |
||||
|
||||
// drag, where all the magic happens. The logic is really quite simple:
|
||||
//
|
||||
// 1. Ignore if the pair is not dragging.
|
||||
// 2. Get the offset of the event.
|
||||
// 3. Snap offset to min if within snappable range (within min + snapOffset).
|
||||
// 4. Actually adjust each element in the pair to offset.
|
||||
//
|
||||
// ---------------------------------------------------------------------
|
||||
// | | <- a.minSize || b.minSize -> | |
|
||||
// | | | <- this.snapOffset || this.snapOffset -> | | |
|
||||
// | | | || | | |
|
||||
// | | | || | | |
|
||||
// ---------------------------------------------------------------------
|
||||
// | <- this.start this.size -> |
|
||||
function drag(e) { |
||||
var offset; |
||||
var a = elements[this.a]; |
||||
var b = elements[this.b]; |
||||
|
||||
if (!this.dragging) { return } |
||||
|
||||
// Get the offset of the event from the first side of the
|
||||
// pair `this.start`. Then offset by the initial position of the
|
||||
// mouse compared to the gutter size.
|
||||
offset = |
||||
getMousePosition(e) - |
||||
this.start + |
||||
(this[aGutterSize] - this.dragOffset); |
||||
|
||||
if (dragInterval > 1) { |
||||
offset = Math.round(offset / dragInterval) * dragInterval; |
||||
} |
||||
|
||||
// If within snapOffset of min or max, set offset to min or max.
|
||||
// snapOffset buffers a.minSize and b.minSize, so logic is opposite for both.
|
||||
// Include the appropriate gutter sizes to prevent overflows.
|
||||
if (offset <= a.minSize + snapOffset + this[aGutterSize]) { |
||||
offset = a.minSize + this[aGutterSize]; |
||||
} else if ( |
||||
offset >= |
||||
this.size - (b.minSize + snapOffset + this[bGutterSize]) |
||||
) { |
||||
offset = this.size - (b.minSize + this[bGutterSize]); |
||||
} |
||||
|
||||
// Actually adjust the size.
|
||||
adjust.call(this, offset); |
||||
|
||||
// Call the drag callback continously. Don't do anything too intensive
|
||||
// in this callback.
|
||||
getOption(options, 'onDrag', NOOP)(getSizes()); |
||||
} |
||||
|
||||
// Cache some important sizes when drag starts, so we don't have to do that
|
||||
// continously:
|
||||
//
|
||||
// `size`: The total size of the pair. First + second + first gutter + second gutter.
|
||||
// `start`: The leading side of the first element.
|
||||
//
|
||||
// ------------------------------------------------
|
||||
// | aGutterSize -> ||| |
|
||||
// | ||| |
|
||||
// | ||| |
|
||||
// | ||| <- bGutterSize |
|
||||
// ------------------------------------------------
|
||||
// | <- start size -> |
|
||||
function calculateSizes() { |
||||
// Figure out the parent size minus padding.
|
||||
var a = elements[this.a].element; |
||||
var b = elements[this.b].element; |
||||
|
||||
var aBounds = a[getBoundingClientRect](); |
||||
var bBounds = b[getBoundingClientRect](); |
||||
|
||||
this.size = |
||||
aBounds[dimension] + |
||||
bBounds[dimension] + |
||||
this[aGutterSize] + |
||||
this[bGutterSize]; |
||||
this.start = aBounds[position]; |
||||
this.end = aBounds[positionEnd]; |
||||
} |
||||
|
||||
function innerSize(element) { |
||||
// Return nothing if getComputedStyle is not supported (< IE9)
|
||||
// Or if parent element has no layout yet
|
||||
if (!getComputedStyle) { return null } |
||||
|
||||
var computedStyle = getComputedStyle(element); |
||||
|
||||
if (!computedStyle) { return null } |
||||
|
||||
var size = element[clientSize]; |
||||
|
||||
if (size === 0) { return null } |
||||
|
||||
if (direction === HORIZONTAL) { |
||||
size -= |
||||
parseFloat(computedStyle.paddingLeft) + |
||||
parseFloat(computedStyle.paddingRight); |
||||
} else { |
||||
size -= |
||||
parseFloat(computedStyle.paddingTop) + |
||||
parseFloat(computedStyle.paddingBottom); |
||||
} |
||||
|
||||
return size |
||||
} |
||||
|
||||
// When specifying percentage sizes that are less than the computed
|
||||
// size of the element minus the gutter, the lesser percentages must be increased
|
||||
// (and decreased from the other elements) to make space for the pixels
|
||||
// subtracted by the gutters.
|
||||
function trimToMin(sizesToTrim) { |
||||
// Try to get inner size of parent element.
|
||||
// If it's no supported, return original sizes.
|
||||
var parentSize = innerSize(parent); |
||||
if (parentSize === null) { |
||||
return sizesToTrim |
||||
} |
||||
|
||||
if (minSizes.reduce(function (a, b) { return a + b; }, 0) > parentSize) { |
||||
return sizesToTrim |
||||
} |
||||
|
||||
// Keep track of the excess pixels, the amount of pixels over the desired percentage
|
||||
// Also keep track of the elements with pixels to spare, to decrease after if needed
|
||||
var excessPixels = 0; |
||||
var toSpare = []; |
||||
|
||||
var pixelSizes = sizesToTrim.map(function (size, i) { |
||||
// Convert requested percentages to pixel sizes
|
||||
var pixelSize = (parentSize * size) / 100; |
||||
var elementGutterSize = getGutterSize( |
||||
gutterSize, |
||||
i === 0, |
||||
i === sizesToTrim.length - 1, |
||||
gutterAlign |
||||
); |
||||
var elementMinSize = minSizes[i] + elementGutterSize; |
||||
|
||||
// If element is too smal, increase excess pixels by the difference
|
||||
// and mark that it has no pixels to spare
|
||||
if (pixelSize < elementMinSize) { |
||||
excessPixels += elementMinSize - pixelSize; |
||||
toSpare.push(0); |
||||
return elementMinSize |
||||
} |
||||
|
||||
// Otherwise, mark the pixels it has to spare and return it's original size
|
||||
toSpare.push(pixelSize - elementMinSize); |
||||
return pixelSize |
||||
}); |
||||
|
||||
// If nothing was adjusted, return the original sizes
|
||||
if (excessPixels === 0) { |
||||
return sizesToTrim |
||||
} |
||||
|
||||
return pixelSizes.map(function (pixelSize, i) { |
||||
var newPixelSize = pixelSize; |
||||
|
||||
// While there's still pixels to take, and there's enough pixels to spare,
|
||||
// take as many as possible up to the total excess pixels
|
||||
if (excessPixels > 0 && toSpare[i] - excessPixels > 0) { |
||||
var takenPixels = Math.min( |
||||
excessPixels, |
||||
toSpare[i] - excessPixels |
||||
); |
||||
|
||||
// Subtract the amount taken for the next iteration
|
||||
excessPixels -= takenPixels; |
||||
newPixelSize = pixelSize - takenPixels; |
||||
} |
||||
|
||||
// Return the pixel size adjusted as a percentage
|
||||
return (newPixelSize / parentSize) * 100 |
||||
}) |
||||
} |
||||
|
||||
// stopDragging is very similar to startDragging in reverse.
|
||||
function stopDragging() { |
||||
var self = this; |
||||
var a = elements[self.a].element; |
||||
var b = elements[self.b].element; |
||||
|
||||
if (self.dragging) { |
||||
getOption(options, 'onDragEnd', NOOP)(getSizes()); |
||||
} |
||||
|
||||
self.dragging = false; |
||||
|
||||
// Remove the stored event listeners. This is why we store them.
|
||||
global[removeEventListener]('mouseup', self.stop); |
||||
global[removeEventListener]('touchend', self.stop); |
||||
global[removeEventListener]('touchcancel', self.stop); |
||||
global[removeEventListener]('mousemove', self.move); |
||||
global[removeEventListener]('touchmove', self.move); |
||||
|
||||
// Clear bound function references
|
||||
self.stop = null; |
||||
self.move = null; |
||||
|
||||
a[removeEventListener]('selectstart', NOOP); |
||||
a[removeEventListener]('dragstart', NOOP); |
||||
b[removeEventListener]('selectstart', NOOP); |
||||
b[removeEventListener]('dragstart', NOOP); |
||||
|
||||
a.style.userSelect = ''; |
||||
a.style.webkitUserSelect = ''; |
||||
a.style.MozUserSelect = ''; |
||||
a.style.pointerEvents = ''; |
||||
|
||||
b.style.userSelect = ''; |
||||
b.style.webkitUserSelect = ''; |
||||
b.style.MozUserSelect = ''; |
||||
b.style.pointerEvents = ''; |
||||
|
||||
self.gutter.style.cursor = ''; |
||||
self.parent.style.cursor = ''; |
||||
document.body.style.cursor = ''; |
||||
} |
||||
|
||||
// startDragging calls `calculateSizes` to store the inital size in the pair object.
|
||||
// It also adds event listeners for mouse/touch events,
|
||||
// and prevents selection while dragging so avoid the selecting text.
|
||||
function startDragging(e) { |
||||
// Right-clicking can't start dragging.
|
||||
if ('button' in e && e.button !== 0) { |
||||
return |
||||
} |
||||
|
||||
// Alias frequently used variables to save space. 200 bytes.
|
||||
var self = this; |
||||
var a = elements[self.a].element; |
||||
var b = elements[self.b].element; |
||||
|
||||
// Call the onDragStart callback.
|
||||
if (!self.dragging) { |
||||
getOption(options, 'onDragStart', NOOP)(getSizes()); |
||||
} |
||||
|
||||
// Don't actually drag the element. We emulate that in the drag function.
|
||||
e.preventDefault(); |
||||
|
||||
// Set the dragging property of the pair object.
|
||||
self.dragging = true; |
||||
|
||||
// Create two event listeners bound to the same pair object and store
|
||||
// them in the pair object.
|
||||
self.move = drag.bind(self); |
||||
self.stop = stopDragging.bind(self); |
||||
|
||||
// All the binding. `window` gets the stop events in case we drag out of the elements.
|
||||
global[addEventListener]('mouseup', self.stop); |
||||
global[addEventListener]('touchend', self.stop); |
||||
global[addEventListener]('touchcancel', self.stop); |
||||
global[addEventListener]('mousemove', self.move); |
||||
global[addEventListener]('touchmove', self.move); |
||||
|
||||
// Disable selection. Disable!
|
||||
a[addEventListener]('selectstart', NOOP); |
||||
a[addEventListener]('dragstart', NOOP); |
||||
b[addEventListener]('selectstart', NOOP); |
||||
b[addEventListener]('dragstart', NOOP); |
||||
|
||||
a.style.userSelect = 'none'; |
||||
a.style.webkitUserSelect = 'none'; |
||||
a.style.MozUserSelect = 'none'; |
||||
a.style.pointerEvents = 'none'; |
||||
|
||||
b.style.userSelect = 'none'; |
||||
b.style.webkitUserSelect = 'none'; |
||||
b.style.MozUserSelect = 'none'; |
||||
b.style.pointerEvents = 'none'; |
||||
|
||||
// Set the cursor at multiple levels
|
||||
self.gutter.style.cursor = cursor; |
||||
self.parent.style.cursor = cursor; |
||||
document.body.style.cursor = cursor; |
||||
|
||||
// Cache the initial sizes of the pair.
|
||||
calculateSizes.call(self); |
||||
|
||||
// Determine the position of the mouse compared to the gutter
|
||||
self.dragOffset = getMousePosition(e) - self.end; |
||||
} |
||||
|
||||
// adjust sizes to ensure percentage is within min size and gutter.
|
||||
sizes = trimToMin(sizes); |
||||
|
||||
// 5. Create pair and element objects. Each pair has an index reference to
|
||||
// elements `a` and `b` of the pair (first and second elements).
|
||||
// Loop through the elements while pairing them off. Every pair gets a
|
||||
// `pair` object and a gutter.
|
||||
//
|
||||
// Basic logic:
|
||||
//
|
||||
// - Starting with the second element `i > 0`, create `pair` objects with
|
||||
// `a = i - 1` and `b = i`
|
||||
// - Set gutter sizes based on the _pair_ being first/last. The first and last
|
||||
// pair have gutterSize / 2, since they only have one half gutter, and not two.
|
||||
// - Create gutter elements and add event listeners.
|
||||
// - Set the size of the elements, minus the gutter sizes.
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
// | i=0 | i=1 | i=2 | i=3 |
|
||||
// | | | | |
|
||||
// | pair 0 pair 1 pair 2 |
|
||||
// | | | | |
|
||||
// -----------------------------------------------------------------------
|
||||
var pairs = []; |
||||
elements = ids.map(function (id, i) { |
||||
// Create the element object.
|
||||
var element = { |
||||
element: elementOrSelector(id), |
||||
size: sizes[i], |
||||
minSize: minSizes[i], |
||||
i: i, |
||||
}; |
||||
|
||||
var pair; |
||||
|
||||
if (i > 0) { |
||||
// Create the pair object with its metadata.
|
||||
pair = { |
||||
a: i - 1, |
||||
b: i, |
||||
dragging: false, |
||||
direction: direction, |
||||
parent: parent, |
||||
}; |
||||
|
||||
pair[aGutterSize] = getGutterSize( |
||||
gutterSize, |
||||
i - 1 === 0, |
||||
false, |
||||
gutterAlign |
||||
); |
||||
pair[bGutterSize] = getGutterSize( |
||||
gutterSize, |
||||
false, |
||||
i === ids.length - 1, |
||||
gutterAlign |
||||
); |
||||
|
||||
// if the parent has a reverse flex-direction, switch the pair elements.
|
||||
if ( |
||||
parentFlexDirection === 'row-reverse' || |
||||
parentFlexDirection === 'column-reverse' |
||||
) { |
||||
var temp = pair.a; |
||||
pair.a = pair.b; |
||||
pair.b = temp; |
||||
} |
||||
} |
||||
|
||||
// Determine the size of the current element. IE8 is supported by
|
||||
// staticly assigning sizes without draggable gutters. Assigns a string
|
||||
// to `size`.
|
||||
//
|
||||
// Create gutter elements for each pair.
|
||||
if (i > 0) { |
||||
var gutterElement = gutter(i, direction, element.element); |
||||
setGutterSize(gutterElement, gutterSize, i); |
||||
|
||||
// Save bound event listener for removal later
|
||||
pair[gutterStartDragging] = startDragging.bind(pair); |
||||
|
||||
// Attach bound event listener
|
||||
gutterElement[addEventListener]( |
||||
'mousedown', |
||||
pair[gutterStartDragging] |
||||
); |
||||
gutterElement[addEventListener]( |
||||
'touchstart', |
||||
pair[gutterStartDragging] |
||||
); |
||||
|
||||
parent.insertBefore(gutterElement, element.element); |
||||
|
||||
pair.gutter = gutterElement; |
||||
} |
||||
|
||||
setElementSize( |
||||
element.element, |
||||
element.size, |
||||
getGutterSize( |
||||
gutterSize, |
||||
i === 0, |
||||
i === ids.length - 1, |
||||
gutterAlign |
||||
), |
||||
i |
||||
); |
||||
|
||||
// After the first iteration, and we have a pair object, append it to the
|
||||
// list of pairs.
|
||||
if (i > 0) { |
||||
pairs.push(pair); |
||||
} |
||||
|
||||
return element |
||||
}); |
||||
|
||||
function adjustToMin(element) { |
||||
var isLast = element.i === pairs.length; |
||||
var pair = isLast ? pairs[element.i - 1] : pairs[element.i]; |
||||
|
||||
calculateSizes.call(pair); |
||||
|
||||
var size = isLast |
||||
? pair.size - element.minSize - pair[bGutterSize] |
||||
: element.minSize + pair[aGutterSize]; |
||||
|
||||
adjust.call(pair, size); |
||||
} |
||||
|
||||
elements.forEach(function (element) { |
||||
var computedSize = element.element[getBoundingClientRect]()[dimension]; |
||||
|
||||
if (computedSize < element.minSize) { |
||||
if (expandToMin) { |
||||
adjustToMin(element); |
||||
} else { |
||||
// eslint-disable-next-line no-param-reassign
|
||||
element.minSize = computedSize; |
||||
} |
||||
} |
||||
}); |
||||
|
||||
function setSizes(newSizes) { |
||||
var trimmed = trimToMin(newSizes); |
||||
trimmed.forEach(function (newSize, i) { |
||||
if (i > 0) { |
||||
var pair = pairs[i - 1]; |
||||
|
||||
var a = elements[pair.a]; |
||||
var b = elements[pair.b]; |
||||
|
||||
a.size = trimmed[i - 1]; |
||||
b.size = newSize; |
||||
|
||||
setElementSize(a.element, a.size, pair[aGutterSize], a.i); |
||||
setElementSize(b.element, b.size, pair[bGutterSize], b.i); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
function destroy(preserveStyles, preserveGutter) { |
||||
pairs.forEach(function (pair) { |
||||
if (preserveGutter !== true) { |
||||
pair.parent.removeChild(pair.gutter); |
||||
} else { |
||||
pair.gutter[removeEventListener]( |
||||
'mousedown', |
||||
pair[gutterStartDragging] |
||||
); |
||||
pair.gutter[removeEventListener]( |
||||
'touchstart', |
||||
pair[gutterStartDragging] |
||||
); |
||||
} |
||||
|
||||
if (preserveStyles !== true) { |
||||
var style = elementStyle( |
||||
dimension, |
||||
pair.a.size, |
||||
pair[aGutterSize] |
||||
); |
||||
|
||||
Object.keys(style).forEach(function (prop) { |
||||
elements[pair.a].element.style[prop] = ''; |
||||
elements[pair.b].element.style[prop] = ''; |
||||
}); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
return { |
||||
setSizes: setSizes, |
||||
getSizes: getSizes, |
||||
collapse: function collapse(i) { |
||||
adjustToMin(elements[i]); |
||||
}, |
||||
destroy: destroy, |
||||
parent: parent, |
||||
pairs: pairs, |
||||
} |
||||
}; |
||||
|
||||
return Split; |
||||
|
||||
}))); |
File diff suppressed because one or more lines are too long
@ -1,16 +0,0 @@ |
||||
gMainApp.component( "main-app", { |
||||
|
||||
data() { return { |
||||
isLoaded: false, |
||||
} ; }, |
||||
|
||||
template: ` |
||||
<div> Hello, world! </div> |
||||
<div v-if="isLoaded" id="_mainapp-loaded_" /> |
||||
`,
|
||||
|
||||
mounted() { |
||||
this.isLoaded = true ; |
||||
}, |
||||
|
||||
} ) ; |
@ -0,0 +1,71 @@ |
||||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.TinyEmitter = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ |
||||
function E () { |
||||
// Keep this empty so it's easier to inherit from
|
||||
// (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
|
||||
} |
||||
|
||||
E.prototype = { |
||||
on: function (name, callback, ctx) { |
||||
var e = this.e || (this.e = {}); |
||||
|
||||
(e[name] || (e[name] = [])).push({ |
||||
fn: callback, |
||||
ctx: ctx |
||||
}); |
||||
|
||||
return this; |
||||
}, |
||||
|
||||
once: function (name, callback, ctx) { |
||||
var self = this; |
||||
function listener () { |
||||
self.off(name, listener); |
||||
callback.apply(ctx, arguments); |
||||
}; |
||||
|
||||
listener._ = callback |
||||
return this.on(name, listener, ctx); |
||||
}, |
||||
|
||||
emit: function (name) { |
||||
var data = [].slice.call(arguments, 1); |
||||
var evtArr = ((this.e || (this.e = {}))[name] || []).slice(); |
||||
var i = 0; |
||||
var len = evtArr.length; |
||||
|
||||
for (i; i < len; i++) { |
||||
evtArr[i].fn.apply(evtArr[i].ctx, data); |
||||
} |
||||
|
||||
return this; |
||||
}, |
||||
|
||||
off: function (name, callback) { |
||||
var e = this.e || (this.e = {}); |
||||
var evts = e[name]; |
||||
var liveEvents = []; |
||||
|
||||
if (evts && callback) { |
||||
for (var i = 0, len = evts.length; i < len; i++) { |
||||
if (evts[i].fn !== callback && evts[i].fn._ !== callback) |
||||
liveEvents.push(evts[i]); |
||||
} |
||||
} |
||||
|
||||
// Remove event from queue to prevent memory leak
|
||||
// Suggested by https://github.com/lazd
|
||||
// Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910
|
||||
|
||||
(liveEvents.length) |
||||
? e[name] = liveEvents |
||||
: delete e[name]; |
||||
|
||||
return this; |
||||
} |
||||
}; |
||||
|
||||
module.exports = E; |
||||
module.exports.TinyEmitter = E; |
||||
|
||||
},{}]},{},[1])(1) |
||||
}); |
@ -0,0 +1 @@ |
||||
(function(e){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=e()}else if(typeof define==="function"&&define.amd){define([],e)}else{var n;if(typeof window!=="undefined"){n=window}else if(typeof global!=="undefined"){n=global}else if(typeof self!=="undefined"){n=self}else{n=this}n.TinyEmitter=e()}})(function(){var e,n,t;return function r(e,n,t){function i(o,u){if(!n[o]){if(!e[o]){var s=typeof require=="function"&&require;if(!u&&s)return s(o,!0);if(f)return f(o,!0);var a=new Error("Cannot find module '"+o+"'");throw a.code="MODULE_NOT_FOUND",a}var l=n[o]={exports:{}};e[o][0].call(l.exports,function(n){var t=e[o][1][n];return i(t?t:n)},l,l.exports,r,e,n,t)}return n[o].exports}var f=typeof require=="function"&&require;for(var o=0;o<t.length;o++)i(t[o]);return i}({1:[function(e,n,t){function r(){}r.prototype={on:function(e,n,t){var r=this.e||(this.e={});(r[e]||(r[e]=[])).push({fn:n,ctx:t});return this},once:function(e,n,t){var r=this;function i(){r.off(e,i);n.apply(t,arguments)}i._=n;return this.on(e,i,t)},emit:function(e){var n=[].slice.call(arguments,1);var t=((this.e||(this.e={}))[e]||[]).slice();var r=0;var i=t.length;for(r;r<i;r++){t[r].fn.apply(t[r].ctx,n)}return this},off:function(e,n){var t=this.e||(this.e={});var r=t[e];var i=[];if(r&&n){for(var f=0,o=r.length;f<o;f++){if(r[f].fn!==n&&r[f].fn._!==n)i.push(r[f])}}i.length?t[e]=i:delete t[e];return this}};n.exports=r;n.exports.TinyEmitter=r},{}]},{},[1])(1)}); |
@ -0,0 +1,17 @@ |
||||
export function showInfoMsg( msg ) { _doShowNotificationMsg( "notice", msg ) ; } |
||||
export function showWarningMsg( msg ) { _doShowNotificationMsg( "warning", msg ) ; } |
||||
export function showErrorMsg( msg ) { _doShowNotificationMsg( "error", msg ) ; } |
||||
|
||||
function _doShowNotificationMsg( msgType, msg ) |
||||
{ |
||||
// show the notification message
|
||||
$.growl( { |
||||
style: msgType, |
||||
title: null, |
||||
message: msg, |
||||
location: "br", |
||||
duration: (msgType == "warning") ? 15*1000 : 5*1000, |
||||
fixed: (msgType == "error"), |
||||
} ) ; |
||||
} |
||||
|
Binary file not shown.
@ -0,0 +1,76 @@ |
||||
[ |
||||
|
||||
{ "title": "a", |
||||
"content": "amphibious" |
||||
}, |
||||
|
||||
{ "title": "Advance", |
||||
"ruleids": [ "A4.7" ] |
||||
}, |
||||
|
||||
{ "title": "Backblast", |
||||
"ruleids": [ "C13.8" ], |
||||
"rulerefs": [ |
||||
{ "caption": "Huts", "ruleids": [ "G5.62" ] }, |
||||
{ "caption": "RCL", "ruleids": [ "C12.3-.4" ] } |
||||
] |
||||
}, |
||||
|
||||
{ "title": "CCPh", |
||||
"subtitle": "Close Combat Phase", |
||||
"ruleids": [ "A3.8" ], |
||||
"rulerefs": [ |
||||
{ "caption": "ENEMY Attacks", "ruleids": [ "S11.5" ] }, |
||||
{ "caption": "dropping SW before CC", "ruleids": [ "A4.43" ] } |
||||
] |
||||
}, |
||||
|
||||
{ "title": "Double Time", |
||||
"ruleids": [ "A4.5-.51", "S6.222" ], |
||||
"see_also": [ "CX" ], |
||||
"content": "Also known as \"running <em>really</em> fast.\"", |
||||
"rulerefs": [ |
||||
{ "caption": "ENEMY Guard Automatic Action", "ruleids": [ "S6.303" ] }, |
||||
{ "caption": "Manhandling", "ruleids": [ "C10.3" ] }, |
||||
{ "caption": "NA for Pathfinders", "ruleids": [ "T1.2" ] }, |
||||
{ "caption": "S? NA", "ruleids": [ "S3.321" ] }, |
||||
{ "caption": "Water Shortage", "ruleids": [ "RCG21" ] }, |
||||
{ "caption": "Wire NA", "ruleids": [ "B26.46" ] } |
||||
] |
||||
}, |
||||
|
||||
{ "title": "ELR", |
||||
"subtitle": "Experience Level Rating", |
||||
"ruleids": [ "A19.1" ], |
||||
"rulerefs": [ |
||||
{ "caption": "BRT", "ruleids": [ "TCG17" ] }, |
||||
{ "caption": "Loss", "ruleids": [ "A16.2" ] }, |
||||
{ "caption": "Massacre", "ruleids": [ "A20.4" ] }, |
||||
{ "caption": "Night", "ruleids": [ "E1.22" ] }, |
||||
{ "caption": "in PB", "ruleids": [ "SSR PB12" ] }, |
||||
{ "caption": "Regaining", "ruleids": [ "A16.3" ] }, |
||||
{ "caption": "RePh", "ruleids": [ "O11.617", "PCG4", "QCG3", "R9.6202" ] } |
||||
] |
||||
}, |
||||
|
||||
{ "title": "Firepower", |
||||
"ruleids": [ "A1.21" ], |
||||
"see_also": [ "FP" ] |
||||
}, |
||||
|
||||
{ "title": "Gaps, Convoy", |
||||
"ruleids": [ "E11.21" ] |
||||
}, |
||||
|
||||
{ "title": "H#", |
||||
"subtitle": "HEAT Depletion Number; the number is the Depletion Number, and the superscript following it indicates the first year it applies and a letter indicates the month of that year [EX: A superscript of \"4\" means the vehicle/ordnance has that ammo starting in 1944]", |
||||
"ruleids": [ "C8.3" ], |
||||
"see_also": [ "HEAT" ] |
||||
}, |
||||
|
||||
|
||||
{ "title": "Identity, Vehicular", |
||||
"ruleids": [ "D1.4" ] |
||||
} |
||||
|
||||
] |
Binary file not shown.
@ -0,0 +1,15 @@ |
||||
{ |
||||
|
||||
"A4.7": { "caption": "ADVANCE PHASE", "page_no": 1, "pos": [72,702] }, |
||||
"C13.8": { "caption": "BACKBLAST", "page_no": 1, "pos": [72,404] }, |
||||
"A3.8": { "caption": "CLOSE COMBAT PHASE (CCPh)", "page_no": 1, "pos": [72.97] }, |
||||
|
||||
"A4.5": { "caption": "DOUBLE TIME", "page_no": 2, "pos": [72,702] }, |
||||
"A19.1": { "caption": "EXPERIENCE LEVEL RATING (ELR)", "page_no": 2, "pos": [72.404] }, |
||||
"A1.21": { "caption": "FIREPOWER (FP)", "page_no": 2, "pos": [72,97] }, |
||||
|
||||
"A1.21": { "caption": "FIREPOWER (FP)", "page_no": 3, "pos": [72,702] }, |
||||
"E11.21": { "caption": "GAPS", "page_no": 3, "pos":[72,404] }, |
||||
"C8.3": { "caption": "HEAT (H)", "page_no": 3, "pos": [72,97] } |
||||
|
||||
} |
@ -1,10 +1,20 @@ |
||||
""" Test basic functionality. """ |
||||
|
||||
from asl_rulebook2.webapp.tests.utils import init_webapp |
||||
from asl_rulebook2.webapp.tests.utils import init_webapp, get_nav_panels, get_content_docs |
||||
|
||||
# --------------------------------------------------------------------- |
||||
|
||||
def test_hello( webapp, webdriver ): |
||||
"""Test basic functionality.""" |
||||
|
||||
# initialize |
||||
webapp.control_tests.set_data_dir( "simple" ) |
||||
init_webapp( webapp, webdriver ) |
||||
|
||||
# check that the nav panel loaded correctly |
||||
nav_panels = get_nav_panels() |
||||
assert nav_panels == [ "search" ] |
||||
|
||||
# check that the content docs loaded correctly |
||||
content_docs = get_content_docs() |
||||
assert content_docs == [ "simple" ] |
||||
|
@ -0,0 +1,35 @@ |
||||
""" Run ESLint over the Javascript files. """ |
||||
|
||||
import os.path |
||||
import subprocess |
||||
import pytest |
||||
|
||||
# --------------------------------------------------------------------- |
||||
|
||||
@pytest.mark.skipif( not os.environ.get("ESLINT"), reason="ESLINT not configured." ) |
||||
def test_eslint(): |
||||
"""Run ESLint over the Javascript files.""" |
||||
|
||||
# initialize |
||||
eslint = os.environ[ "ESLINT" ] |
||||
|
||||
# check each Javascript file |
||||
dname = os.path.join( os.path.dirname(__file__), "../static/" ) |
||||
for fname in os.listdir( dname ): |
||||
|
||||
if os.path.splitext( fname )[1] != ".js": |
||||
continue |
||||
|
||||
# run ESLint for the next file |
||||
proc = subprocess.run( |
||||
[ eslint, os.path.join(dname,fname) ], |
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8", |
||||
check=False |
||||
) |
||||
if proc.stdout or proc.stderr: |
||||
print( "=== ESLint failed: {} ===".format( fname ) ) |
||||
if proc.stdout: |
||||
print( proc.stdout ) |
||||
if proc.stderr: |
||||
print( proc.stderr ) |
||||
assert False |
Loading…
Reference in new issue