commit
61c5f0cdb1
@ -0,0 +1,3 @@
|
||||
.vscode/
|
||||
nohup.out
|
||||
data/
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,129 @@
|
||||
import json
|
||||
import threading
|
||||
import random
|
||||
import string
|
||||
import dataHandlerPTT as ptt
|
||||
import generalText as gen
|
||||
from queue import Queue
|
||||
from datetime import datetime
|
||||
from pprint import pprint
|
||||
from flask import Flask, render_template, request, jsonify, send_from_directory, Response, stream_with_context, redirect
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
|
||||
|
||||
eventQueue = Queue()
|
||||
|
||||
|
||||
@app.route('/stream')
|
||||
def stream():
|
||||
return Response(stream_with_context(eventStream(eventQueue)), mimetype='text/event-stream')
|
||||
|
||||
|
||||
def eventStream(eventQueue):
|
||||
while (True):
|
||||
eventNode = eventQueue.get(True)
|
||||
data = json.dumps(eventNode['data'], indent=4, ensure_ascii=False)
|
||||
data = data.splitlines()
|
||||
data = ['data:' + i for i in data]
|
||||
data = '\n'.join(data)
|
||||
yield "event:{event}\n{data}\n\n".format(event=eventNode['event'], data=data)
|
||||
|
||||
|
||||
@app.route('/data/<path:path>')
|
||||
def send_data(path):
|
||||
return send_from_directory('data', path)
|
||||
|
||||
|
||||
@app.route('/generalTxt')
|
||||
def generalTxt():
|
||||
return render_template('generalTxt.html', title="泛用文字視覺化工具")
|
||||
|
||||
|
||||
@app.route('/post/generalTxt/addText', methods=['POST'])
|
||||
def generalText_addText():
|
||||
text = request.json['text']
|
||||
stopwords = request.json['stopwords']
|
||||
randId = ''.join(random.choices(
|
||||
string.ascii_uppercase + string.ascii_lowercase, k=15))
|
||||
result = gen.processText(randId, text, stopwords)
|
||||
return jsonify(Result={
|
||||
'path': result
|
||||
})
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return redirect('/ptt')
|
||||
|
||||
|
||||
@app.route('/ptt')
|
||||
def pttSententree():
|
||||
return render_template('ptt.html', title="PTT Sententree")
|
||||
|
||||
|
||||
@app.route('/ptt/keywordFrequency', methods=['POST'])
|
||||
def findKeywordFrequency():
|
||||
print(request.json)
|
||||
content = request.json
|
||||
result = ptt.findKeywordFrequency(content)
|
||||
return jsonify(Result=result)
|
||||
|
||||
|
||||
@app.route('/dev/updateContent')
|
||||
def updateContent():
|
||||
ptt.loadPostContents()
|
||||
return jsonify(Result={
|
||||
'done': True
|
||||
})
|
||||
|
||||
|
||||
@app.route('/askProgressHandler', methods=['POST'])
|
||||
def askProgressHandler():
|
||||
key = request.json['key']
|
||||
randId = ''.join(random.choices(
|
||||
string.ascii_uppercase + string.ascii_lowercase, k=15))
|
||||
threading.Thread(target=ptt.progressListener,
|
||||
args=(key, eventQueue, randId,)).start()
|
||||
return jsonify(Result={
|
||||
'id': randId
|
||||
})
|
||||
|
||||
|
||||
@app.route('/addRequest', methods=['POST'])
|
||||
def addRequest():
|
||||
content = request.json
|
||||
ready = False
|
||||
info = None
|
||||
|
||||
files = ptt.findResult(content)
|
||||
|
||||
info = files
|
||||
ready = True
|
||||
|
||||
key = ptt.calcKey(content['startDate'],
|
||||
content["endDate"], content["keyword"], content["pos"])
|
||||
|
||||
return jsonify(Result={
|
||||
'ready': ready,
|
||||
'info': info,
|
||||
'key': key
|
||||
})
|
||||
|
||||
|
||||
@app.route("/init", methods=['POST'])
|
||||
def initPage():
|
||||
startDate = datetime.today().replace(day=1).strftime('%Y-%m-%d')
|
||||
endDate = datetime.today().strftime('%Y-%m-%d')
|
||||
files = ptt.getDefault(startDate, endDate)
|
||||
return jsonify(Result={
|
||||
"startDate": startDate,
|
||||
'endDate': endDate,
|
||||
'keyword': '',
|
||||
'info': files
|
||||
})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=False, port=4980, host='0.0.0.0', threaded=True)
|
@ -0,0 +1,339 @@
|
||||
import sys
|
||||
import threading
|
||||
import jieba
|
||||
import re
|
||||
import csv
|
||||
import json
|
||||
import _pickle as pickle
|
||||
import os
|
||||
import io
|
||||
import jieba
|
||||
import multiprocessing as mp
|
||||
from time import time, sleep
|
||||
from functools import partial
|
||||
from numpy import prod
|
||||
from jieba import posseg
|
||||
from progressbar import ProgressBar
|
||||
from datetime import datetime
|
||||
from PTTData import PTTData
|
||||
|
||||
|
||||
defaultDate = {
|
||||
'startDate': None,
|
||||
'endDate': None
|
||||
}
|
||||
|
||||
postContents = None
|
||||
with open('/home/vis/pttDatabase/PTTData/Gossiping/content/content.pck', 'rb') as f:
|
||||
postContents = pickle.load(f)
|
||||
f.close()
|
||||
|
||||
defaultStopWords = []
|
||||
data = PTTData('Gossiping', '/home/vis/pttDatabase/PTTData')
|
||||
|
||||
|
||||
with open('resource/stopWords.txt', 'r', encoding='UTF-8') as file:
|
||||
for word in file.readlines():
|
||||
word = word.strip()
|
||||
defaultStopWords.append(word)
|
||||
|
||||
|
||||
def calcKey(startDate, endDate, keyword, pos):
|
||||
hashKey = str(hex(hash(startDate) + sys.maxsize)[2:]) + \
|
||||
str(hex(hash(endDate) + sys.maxsize)
|
||||
[2:]) + str(hex(hash(keyword) + sys.maxsize)[2:])
|
||||
for key, val in pos.items():
|
||||
hashKey += ('1' if val else '0')
|
||||
return hashKey
|
||||
|
||||
|
||||
def contentProcess(content, text):
|
||||
aid = text[0]
|
||||
text = text[1]
|
||||
if (content['keyword'] != ''):
|
||||
if (content['keyword'] not in text):
|
||||
return None
|
||||
cutted = filterPOS(content, aid)
|
||||
sentenses = []
|
||||
if (content['keyword'] != ''):
|
||||
pos = [i for i, n in enumerate(cutted) if n == content['keyword']]
|
||||
resultPos = []
|
||||
for i in pos:
|
||||
for j in range(i - 9, i + 10):
|
||||
if (j >= 0 and j < len(cutted) and j not in resultPos):
|
||||
resultPos.append(j)
|
||||
lastPos = -1
|
||||
result = []
|
||||
for i in sorted(resultPos):
|
||||
if (i - lastPos != 1 and lastPos != -1):
|
||||
sentenses.append(result)
|
||||
result = []
|
||||
result.append(cutted[i])
|
||||
lastPos = i
|
||||
sentenses.append(result)
|
||||
else:
|
||||
result = []
|
||||
for i in cutted:
|
||||
result.append(i)
|
||||
if (len(result) >= 50):
|
||||
sentenses.append(result.copy())
|
||||
result = []
|
||||
if (result != []):
|
||||
sentenses.append(result)
|
||||
print(sentenses)
|
||||
return sentenses
|
||||
|
||||
|
||||
def filterPOS(content, aid):
|
||||
if (prod(list(content['pos'].values())) == False):
|
||||
pos = content['pos']
|
||||
cuttedWithPOS = data.posseg(aid)
|
||||
startTime = time()
|
||||
cutted = []
|
||||
for i in cuttedWithPOS:
|
||||
if (i.flag[0] == 'n' or i.flag[0] == 'N'):
|
||||
if (pos['noun']):
|
||||
cutted.append(i.word)
|
||||
elif (i.flag[0] == 'v' or (i.flag[0] == 'V' and i.flag != 'Vi')):
|
||||
if (pos['verb']):
|
||||
cutted.append(i.word)
|
||||
elif (i.flag == 'Vi'):
|
||||
if (pos['adj']):
|
||||
cutted.append(i.word)
|
||||
elif (i.flag == 'ADV'):
|
||||
if (pos['adv']):
|
||||
cutted.append(i.word)
|
||||
elif (i.flag == 'r'):
|
||||
if (pos['pron']):
|
||||
cutted.append(i.word)
|
||||
elif (i.flag == 'POST' or i.flag == 'T'):
|
||||
if (pos['aux']):
|
||||
cutted.append(i.word)
|
||||
else:
|
||||
if (pos['other']):
|
||||
if(i.flag != 'eng' and i.flag != 'x' and i.flag != 'm'):
|
||||
cutted.append(i.word)
|
||||
else:
|
||||
if (i.word == content['keyword']):
|
||||
cutted.append(i.word)
|
||||
else:
|
||||
cuttedWithPOS = data.posseg(aid)
|
||||
cutted = [i.word for i in cuttedWithPOS if (
|
||||
i.flag != 'eng' and i.flag != 'x' and i.flag != 'm')]
|
||||
if('stopwords' in content):
|
||||
stopwords = content['stopwords']
|
||||
else:
|
||||
stopwords = defaultStopWords
|
||||
stopped = [i for i in cutted if i not in stopwords]
|
||||
return stopped
|
||||
|
||||
|
||||
def findKeywordFrequency(content):
|
||||
startDate = datetime.strptime(
|
||||
content["startDate"], '%Y-%m-%d').strftime('%Y%m%d') + '000000'
|
||||
endDate = datetime.strptime(
|
||||
content['endDate'], '%Y-%m-%d').strftime('%Y%m%d') + '999999'
|
||||
result = {
|
||||
'wordCount': 0,
|
||||
'postCount': 0
|
||||
}
|
||||
for i in postContents:
|
||||
if (i['date'] > endDate or i['date'] < startDate):
|
||||
continue
|
||||
if (content['globKeyword'] not in i['content']):
|
||||
continue
|
||||
counts = len(re.findall(content['keyword'], i['content']))
|
||||
if (counts > 0):
|
||||
result['wordCount'] += counts
|
||||
result['postCount'] += 1
|
||||
return result
|
||||
|
||||
|
||||
def findRange(wordList: list, text: str):
|
||||
top = wordList[0]
|
||||
bot = wordList[-1]
|
||||
mid = wordList[1:-1]
|
||||
pTop = text.find(wordList[0])
|
||||
pBot = text.rfind(wordList[-1])
|
||||
while (True):
|
||||
topTemp = text.find(wordList[0], pTop + 1)
|
||||
if (topTemp == -1):
|
||||
break
|
||||
skip = False
|
||||
for w in mid:
|
||||
if (text.find(w, topTemp, pBot) == -1):
|
||||
skip = True
|
||||
break
|
||||
if (skip):
|
||||
break
|
||||
pTop = topTemp
|
||||
while (True):
|
||||
botTemp = text.rfind(bot, pTop, pBot)
|
||||
if (botTemp == -1):
|
||||
break
|
||||
skip = False
|
||||
for w in mid:
|
||||
if (text.find(w, pTop, botTemp) == -1):
|
||||
skip = True
|
||||
break
|
||||
if (skip):
|
||||
break
|
||||
pBot = botTemp
|
||||
return (pTop, pBot)
|
||||
|
||||
|
||||
def findResult(content):
|
||||
timeStart = time()
|
||||
key = calcKey(content['startDate'],
|
||||
content["endDate"], content["keyword"], content["pos"])
|
||||
startDate = datetime.strptime(
|
||||
content['startDate'], '%Y-%m-%d').strftime('%Y%m%d') + '000000'
|
||||
endDate = datetime.strptime(
|
||||
content['endDate'], '%Y-%m-%d').strftime('%Y%m%d')+'999999'
|
||||
resultPath = 'data/'
|
||||
counter = 0
|
||||
total = len(postContents)
|
||||
result = []
|
||||
result.append(['id', 'text', 'count'])
|
||||
filtered = []
|
||||
titles = {
|
||||
'info': {
|
||||
'keyword': content['keyword'],
|
||||
'count': 0,
|
||||
'posts': 0
|
||||
}
|
||||
}
|
||||
for i in postContents:
|
||||
if (i['date'] > endDate or i['date'] < startDate):
|
||||
continue
|
||||
counter += 1
|
||||
if (content['keyword'] != ''):
|
||||
if (content['keyword'] in i['content']):
|
||||
filtered.append(i)
|
||||
titles['info']['count'] += len(
|
||||
re.findall(content['keyword'], i['content']))
|
||||
|
||||
else:
|
||||
filtered.append(i)
|
||||
titles['info']['posts'] = len(filtered)
|
||||
filtered = [i for i in sorted(
|
||||
filtered, key=lambda x: x['pushes'], reverse=True)[:50]]
|
||||
print('到第一步為止生成花費', int(time()-timeStart), '秒')
|
||||
counter = 0
|
||||
total = len(filtered)
|
||||
sentensesList = []
|
||||
if(os.name == 'posix'):
|
||||
with mp.Pool(mp.cpu_count()) as pool:
|
||||
processes = pool.map_async(
|
||||
partial(contentProcess, content), [(i['aid'], i['content']) for i in filtered])
|
||||
sentensesList = processes.get()
|
||||
else:
|
||||
sentensesList = map(partial(contentProcess, content), [
|
||||
(i['aid'], i['content']) for i in filtered])
|
||||
for index, i in enumerate(filtered):
|
||||
counter += 1
|
||||
sentenses = sentensesList[index]
|
||||
if (sum([len(i) for i in sentenses]) == 0):
|
||||
continue
|
||||
for j in sentenses:
|
||||
cut = findRange(j, i['content'])
|
||||
seq = ' '.join(j)
|
||||
if (seq not in titles):
|
||||
titles[seq] = {
|
||||
'title': i['title'],
|
||||
'url': i['url'],
|
||||
'pushes': i['pushes'],
|
||||
'author': i['author'],
|
||||
'date': datetime.strptime(i['date'], '%Y%m%d%H%M%S').strftime("%a %b %d %H:%M:%S %Y"),
|
||||
'part': i['content'][max(0, cut[0] - 20): min(len(i['content']), cut[1])].replace('\n', '')
|
||||
}
|
||||
result.append([len(result), seq, 1000 + i['pushes']])
|
||||
print('到第二步為止生成花費', int(time()-timeStart), '秒')
|
||||
fileString = io.StringIO()
|
||||
writer = csv.writer(fileString, delimiter='\t')
|
||||
writer.writerows(result)
|
||||
csvString = fileString.getvalue()
|
||||
jsonString = json.dumps(titles, ensure_ascii=False, indent=4)
|
||||
print('tsv', '生成花費', int(time() - timeStart), '秒')
|
||||
return {
|
||||
'tsv': csvString,
|
||||
'json': jsonString,
|
||||
'stopWords': defaultStopWords
|
||||
}
|
||||
|
||||
|
||||
def loadPostContents():
|
||||
global postContents
|
||||
with open('/home/vis/pttDatabase/PTTData/Gossiping/content/content.pck', 'rb') as f:
|
||||
postContents = pickle.load(f)
|
||||
f.close()
|
||||
|
||||
|
||||
def getDefault(startDate, endDate):
|
||||
global defaultDate
|
||||
if (startDate != defaultDate['startDate'] or endDate != defaultDate['endDate']):
|
||||
loadPostContents
|
||||
defaultDate['startDate'] = startDate
|
||||
defaultDate['endDate'] = endDate
|
||||
print('更新預設資料')
|
||||
timeStart = time()
|
||||
resultPath = 'data/'
|
||||
startDate = datetime.strptime(
|
||||
startDate, '%Y-%m-%d').strftime('%Y%m%d') + '000000'
|
||||
endDate = datetime.strptime(
|
||||
endDate, '%Y-%m-%d').strftime('%Y%m%d')+'999999'
|
||||
counter = 0
|
||||
total = len(postContents)
|
||||
result = []
|
||||
result.append(['id', 'text', 'count'])
|
||||
titles = {}
|
||||
titles['info'] = {
|
||||
'keyword': '',
|
||||
'counts': 0,
|
||||
'posts': 0
|
||||
}
|
||||
filtered = []
|
||||
for i in postContents:
|
||||
if (i['date'] > endDate or i['date'] < startDate):
|
||||
continue
|
||||
filtered.append(i)
|
||||
titles['info']['posts'] = len(filtered)
|
||||
filtered = [i for i in sorted(
|
||||
filtered, key=lambda x: x['pushes'], reverse=True)[:50]]
|
||||
counter = 0
|
||||
total = len(postContents)
|
||||
content = {
|
||||
'keyword': '',
|
||||
'pos': {
|
||||
'other': True
|
||||
}
|
||||
}
|
||||
for i in filtered:
|
||||
counter += 1
|
||||
post = data[i]
|
||||
sentenses = contentProcess(content, (i['aid'], i['content']))
|
||||
for j in sentenses:
|
||||
cut = findRange(j, i['content'])
|
||||
seq = ' '.join(j)
|
||||
if (seq not in titles):
|
||||
titles[seq] = {
|
||||
'title': i['title'],
|
||||
'url': i['url'],
|
||||
'pushes': i['pushes'],
|
||||
'author': i['author'],
|
||||
'date': datetime.strptime(i['date'], '%Y%m%d%H%M%S').strftime("%a %b %d %H:%M:%S %Y"),
|
||||
'part': i['content'][max(0, cut[0] - 20): min(len(i['content']), cut[1])].replace('\n', '')
|
||||
}
|
||||
result.append([len(result), seq, 1000 + i['pushes']])
|
||||
|
||||
fileString = io.StringIO()
|
||||
writer = csv.writer(fileString, delimiter='\t')
|
||||
writer.writerows(result)
|
||||
csvString = fileString.getvalue()
|
||||
jsonString = json.dumps(titles, ensure_ascii=False, indent=4)
|
||||
print('tsv', '生成花費', int(time() - timeStart), '秒')
|
||||
return {
|
||||
'tsv': csvString,
|
||||
'json': jsonString,
|
||||
'stopWords': defaultStopWords
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
import jieba
|
||||
import csv
|
||||
import nltk
|
||||
import re
|
||||
from jieba import posseg
|
||||
from nltk import tokenize
|
||||
from langdetect import detect
|
||||
|
||||
stopWords = []
|
||||
stopWords.append('\n')
|
||||
stopWords.append(' ')
|
||||
|
||||
with open('resource/stopWords.txt', 'r', encoding='UTF-8') as file:
|
||||
for word in file.readlines():
|
||||
word = word.strip()
|
||||
stopWords.append(word)
|
||||
|
||||
|
||||
def filterPOS(text):
|
||||
print(text)
|
||||
cuttedWithPOS = posseg.cut(text)
|
||||
cutted = [i.word for i in cuttedWithPOS if (
|
||||
i.flag != 'eng' and i.flag != 'x' and i.flag != 'm')]
|
||||
stopped = [i for i in cutted]
|
||||
print(stopped)
|
||||
return stopped
|
||||
|
||||
|
||||
def processText(randId, text, stopwords):
|
||||
if(text == ''):
|
||||
return ''
|
||||
lang = detect(text)
|
||||
sentenses = []
|
||||
print(lang)
|
||||
if (lang == 'zh-cn' or lang == 'zh-tw' or lang == 'ko'):
|
||||
splitted = re.split('。|[\n]+', text)
|
||||
print(splitted)
|
||||
cutted = []
|
||||
for i in splitted:
|
||||
cutted.append(filterPOS(i))
|
||||
print(cutted)
|
||||
for i in cutted:
|
||||
result = []
|
||||
for j in i:
|
||||
if (j in stopwords):
|
||||
continue
|
||||
result.append(j)
|
||||
if (len(result) >= 20):
|
||||
sentenses.append(' '.join(result.copy()))
|
||||
result = []
|
||||
if (result != []):
|
||||
sentenses.append(' '.join(result))
|
||||
else:
|
||||
sentenses = []
|
||||
for sentence in tokenize.sent_tokenize(text):
|
||||
words = sentence.lower().split(' ')
|
||||
print([''.join([a for a in w1 if a.isalpha()]) for w1 in words])
|
||||
sentence = ' '.join([w for w in [''.join([a for a in w1 if a.isalpha()]) for w1 in words] if w not in [sw.lower() for sw in stopwords]])
|
||||
sentenses.append(sentence)
|
||||
result = []
|
||||
result.append(['id', 'text', 'count'])
|
||||
for index, sentence in enumerate(sentenses):
|
||||
result.append([index, sentence, 1000])
|
||||
with open('data/' + randId + '.tsv', 'w', newline='', encoding="utf-8") as f:
|
||||
writer = csv.writer(f, delimiter='\t')
|
||||
writer.writerows(result)
|
||||
f.close()
|
||||
return ('data/' + randId + '.tsv')
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -0,0 +1,20 @@
|
||||
的
|
||||
是
|
||||
在
|
||||
卻
|
||||
也
|
||||
了
|
||||
而
|
||||
要
|
||||
啊
|
||||
阿
|
||||
又
|
||||
有
|
||||
不
|
||||
我
|
||||
都
|
||||
就
|
||||
完整
|
||||
新聞
|
||||
標題
|
||||
內文
|
@ -0,0 +1,316 @@
|
||||
body {
|
||||
font-family: "Segoe UI", Arial, 'Noto Sans TC', sans-serif;
|
||||
font-weight: 400;
|
||||
font-feature-settings: "palt" 1;
|
||||
}
|
||||
|
||||
html {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#infoWindow {
|
||||
display: block;
|
||||
position: fixed;
|
||||
background-color: #FFF;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 400px;
|
||||
height: 250px;
|
||||
border: lightgray;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-radius: 20px;
|
||||
padding: 10px;
|
||||
z-index: 99;
|
||||
box-shadow: 0px 5px 20px rgba(0, 0, 0, .3);
|
||||
}
|
||||
|
||||
#titleList {
|
||||
display: block;
|
||||
position: fixed;
|
||||
background-color: #FFF;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 653px;
|
||||
height: 600px;
|
||||
border: lightgray;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-radius: 20px;
|
||||
padding: 26px;
|
||||
z-index: 99;
|
||||
box-shadow: 0px 5px 20px rgba(0, 0, 0, .3);
|
||||
}
|
||||
|
||||
#stopWordEditor {
|
||||
display: block;
|
||||
position: fixed;
|
||||
background-color: #FFF;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 653px;
|
||||
height: 600px;
|
||||
border: lightgray;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-radius: 20px;
|
||||
padding: 26px;
|
||||
z-index: 99;
|
||||
box-shadow: 0px 5px 20px rgba(0, 0, 0, .3);
|
||||
}
|
||||
|
||||
#sweContainer {
|
||||
position: relative;
|
||||
height: 77%;
|
||||
overflow: auto;
|
||||
overflow-y: auto;
|
||||
transition-duration: 0.5s;
|
||||
}
|
||||
|
||||
#titleListContainer {
|
||||
position: relative;
|
||||
height: 77%;
|
||||
overflow: auto;
|
||||
overflow-y: auto;
|
||||
transition-duration: 0.5s;
|
||||
}
|
||||
|
||||
.deleteListElement {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
padding: 12px 16px 12px 16px;
|
||||
}
|
||||
|
||||
#titleListContainer::-webkit-scrollbar-track {
|
||||
border-radius: 10px;
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
|
||||
#titleListContainer::-webkit-scrollbar {
|
||||
border-radius: 10px;
|
||||
width: 7px;
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
|
||||
#titleListContainer::-webkit-scrollbar-thumb {
|
||||
border-radius: 10px;
|
||||
background-color: #CCCCCC;
|
||||
}
|
||||
|
||||
li a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.info {
|
||||
background-color: rgba(255, 255, 255, 0.6);
|
||||
animation: fadeIn 0.2s;
|
||||
animation-fill-mode: forwards;
|
||||
position: fixed;
|
||||
display: flex;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.info.hidden {
|
||||
display: none;
|
||||
position: fixed;
|
||||
animation: fadeOut 0.2s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.nodeTitle {
|
||||
display: block;
|
||||
position: absolute;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
opacity: 1;
|
||||
transform: translate(10px, 30px);
|
||||
width: auto;
|
||||
height: auto;
|
||||
border-radius: 15px;
|
||||
padding: 10px 15px;
|
||||
z-index: 99;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.nodeTitle.hidden {
|
||||
display: none;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#nodeTitleContent {
|
||||
color: white;
|
||||
text-align: center;
|
||||
height: auto;
|
||||
width: auto;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%);
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#infoText {
|
||||
left: 50%;
|
||||
top: 24%;
|
||||
transform: translate(-50%, -50%);
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#infoButtonGroup {
|
||||
position: relative;
|
||||
display: flex;
|
||||
left: 50%;
|
||||
top: 44%;
|
||||
transform: translate(-50%, -50%);
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#vis {
|
||||
display: inline-block;
|
||||
background-color: aliceblue;
|
||||
position: relative;
|
||||
border-radius: 30px;
|
||||
resize: both;
|
||||
padding: 30px;
|
||||
height: auto;
|
||||
width: auto;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#heading {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#graph {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#comment {
|
||||
font-size: 12px;
|
||||
color: darkslategray;
|
||||
}
|
||||
|
||||
input[type="date" i] {
|
||||
align-items: center;
|
||||
display: -webkit-inline-flex;
|
||||
font-family: monospace;
|
||||
padding-inline-start: 1px;
|
||||
cursor: default;
|
||||
overflow: hidden;
|
||||
padding: 4px;
|
||||
border-radius: 3px;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: lightslategray;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#keywordBox {
|
||||
padding: 7px;
|
||||
align-content: center;
|
||||
border-radius: 3px;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: lightslategray;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#advancedArea {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.general-button {
|
||||
display: inline-block;
|
||||
background-color: #339977;
|
||||
border: none;
|
||||
text-align: center;
|
||||
height: 31px;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
color: white;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#progBar {
|
||||
position: relative;
|
||||
display: flex;
|
||||
left: 50%;
|
||||
top: 30%;
|
||||
transform: translate(-50%, -50%);
|
||||
align-content: center;
|
||||
width: 80%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#rawText {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#rawTextBox {
|
||||
width: 100%;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.break {
|
||||
flex-basis: 100%;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.blink_me {
|
||||
animation: blinker 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes blinker {
|
||||
50% {
|
||||
color: red;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,162 @@
|
||||
//download.js v4.2, by dandavis; 2008-2016. [CCBY2] see http://danml.com/download.html for tests/usage
|
||||
// v1 landed a FF+Chrome compat way of downloading strings to local un-named files, upgraded to use a hidden frame and optional mime
|
||||
// v2 added named files via a[download], msSaveBlob, IE (10+) support, and window.URL support for larger+faster saves than dataURLs
|
||||
// v3 added dataURL and Blob Input, bind-toggle arity, and legacy dataURL fallback was improved with force-download mime and base64 support. 3.1 improved safari handling.
|
||||
// v4 adds AMD/UMD, commonJS, and plain browser support
|
||||
// v4.1 adds url download capability via solo URL argument (same domain/CORS only)
|
||||
// v4.2 adds semantic variable names, long (over 2MB) dataURL support, and hidden by default temp anchors
|
||||
// https://github.com/rndme/download
|
||||
|
||||
(function (root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define([], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node. Does not work with strict CommonJS, but
|
||||
// only CommonJS-like environments that support module.exports,
|
||||
// like Node.
|
||||
module.exports = factory();
|
||||
} else {
|
||||
// Browser globals (root is window)
|
||||
root.download = factory();
|
||||
}
|
||||
}(this, function () {
|
||||
|
||||
return function download(data, strFileName, strMimeType) {
|
||||
|
||||
var self = window, // this script is only for browsers anyway...
|
||||
defaultMime = "application/octet-stream", // this default mime also triggers iframe downloads
|
||||
mimeType = strMimeType || defaultMime,
|
||||
payload = data,
|
||||
url = !strFileName && !strMimeType && payload,
|
||||
anchor = document.createElement("a"),
|
||||
toString = function(a){return String(a);},
|
||||
myBlob = (self.Blob || self.MozBlob || self.WebKitBlob || toString),
|
||||
fileName = strFileName || "download",
|
||||
blob,
|
||||
reader;
|
||||
myBlob= myBlob.call ? myBlob.bind(self) : Blob ;
|
||||
|
||||
if(String(this)==="true"){ //reverse arguments, allowing download.bind(true, "text/xml", "export.xml") to act as a callback
|
||||
payload=[payload, mimeType];
|
||||
mimeType=payload[0];
|
||||
payload=payload[1];
|
||||
}
|
||||
|
||||
|
||||
if(url && url.length< 2048){ // if no filename and no mime, assume a url was passed as the only argument
|
||||
fileName = url.split("/").pop().split("?")[0];
|
||||
anchor.href = url; // assign href prop to temp anchor
|
||||
if(anchor.href.indexOf(url) !== -1){ // if the browser determines that it's a potentially valid url path:
|
||||
var ajax=new XMLHttpRequest();
|
||||
ajax.open( "GET", url, true);
|
||||
ajax.responseType = 'blob';
|
||||
ajax.onload= function(e){
|
||||
download(e.target.response, fileName, defaultMime);
|
||||
};
|
||||
setTimeout(function(){ ajax.send();}, 0); // allows setting custom ajax headers using the return:
|
||||
return ajax;
|
||||
} // end if valid url?
|
||||
} // end if url?
|
||||
|
||||
|
||||
//go ahead and download dataURLs right away
|
||||
if(/^data\:[\w+\-]+\/[\w+\-]+[,;]/.test(payload)){
|
||||
|
||||
if(payload.length > (1024*1024*1.999) && myBlob !== toString ){
|
||||
payload=dataUrlToBlob(payload);
|
||||
mimeType=payload.type || defaultMime;
|
||||
}else{
|
||||
return navigator.msSaveBlob ? // IE10 can't do a[download], only Blobs:
|
||||
navigator.msSaveBlob(dataUrlToBlob(payload), fileName) :
|
||||
saver(payload) ; // everyone else can save dataURLs un-processed
|
||||
}
|
||||
|
||||
}//end if dataURL passed?
|
||||
|
||||
blob = payload instanceof myBlob ?
|
||||
payload :
|
||||
new myBlob([payload], {type: mimeType}) ;
|
||||
|
||||
|
||||
function dataUrlToBlob(strUrl) {
|
||||
var parts= strUrl.split(/[:;,]/),
|
||||
type= parts[1],
|
||||
decoder= parts[2] == "base64" ? atob : decodeURIComponent,
|
||||
binData= decoder( parts.pop() ),
|
||||
mx= binData.length,
|
||||
i= 0,
|
||||
uiArr= new Uint8Array(mx);
|
||||
|
||||
for(i;i<mx;++i) uiArr[i]= binData.charCodeAt(i);
|
||||
|
||||
return new myBlob([uiArr], {type: type});
|
||||
}
|
||||
|
||||
function saver(url, winMode){
|
||||
|
||||
if ('download' in anchor) { //html5 A[download]
|
||||
anchor.href = url;
|
||||
anchor.setAttribute("download", fileName);
|
||||
anchor.className = "download-js-link";
|
||||
anchor.innerHTML = "downloading...";
|
||||
anchor.style.display = "none";
|
||||
document.body.appendChild(anchor);
|
||||
setTimeout(function() {
|
||||
anchor.click();
|
||||
document.body.removeChild(anchor);
|
||||
if(winMode===true){setTimeout(function(){ self.URL.revokeObjectURL(anchor.href);}, 250 );}
|
||||
}, 66);
|
||||
return true;
|
||||
}
|
||||
|
||||
// handle non-a[download] safari as best we can:
|
||||
if(/(Version)\/(\d+)\.(\d+)(?:\.(\d+))?.*Safari\//.test(navigator.userAgent)) {
|
||||
url=url.replace(/^data:([\w\/\-\+]+)/, defaultMime);
|
||||
if(!window.open(url)){ // popup blocked, offer direct download:
|
||||
if(confirm("Displaying New Document\n\nUse Save As... to download, then click back to return to this page.")){ location.href=url; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//do iframe dataURL download (old ch+FF):
|
||||
var f = document.createElement("iframe");
|
||||
document.body.appendChild(f);
|
||||
|
||||
if(!winMode){ // force a mime that will download:
|
||||
url="data:"+url.replace(/^data:([\w\/\-\+]+)/, defaultMime);
|
||||
}
|
||||
f.src=url;
|
||||
setTimeout(function(){ document.body.removeChild(f); }, 333);
|
||||
|
||||
}//end saver
|
||||
|
||||
|
||||
|
||||
|
||||
if (navigator.msSaveBlob) { // IE10+ : (has Blob, but not a[download] or URL)
|
||||
return navigator.msSaveBlob(blob, fileName);
|
||||
}
|
||||
|
||||
if(self.URL){ // simple fast and modern way using Blob and URL:
|
||||
saver(self.URL.createObjectURL(blob), true);
|
||||
}else{
|
||||
// handle non-Blob()+non-URL browsers:
|
||||
if(typeof blob === "string" || blob.constructor===toString ){
|
||||
try{
|
||||
return saver( "data:" + mimeType + ";base64," + self.btoa(blob) );
|
||||
}catch(y){
|
||||
return saver( "data:" + mimeType + "," + encodeURIComponent(blob) );
|
||||
}
|
||||
}
|
||||
|
||||
// Blob but not URL support:
|
||||
reader=new FileReader();
|
||||
reader.onload=function(e){
|
||||
saver(this.result);
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
}
|
||||
return true;
|
||||
}; /* end download() */
|
||||
}));
|
@ -0,0 +1,128 @@
|
||||
var tsvPath
|
||||
var stopwords = []
|
||||
|
||||
function clearStopWord() {
|
||||
stopwords = []
|
||||
$('#sweContainer').html('')
|
||||
}
|
||||
|
||||
function addStopWord() {
|
||||
newswRaw = $('#newStopWord').val()
|
||||
newswList = newswRaw.split(' ')
|
||||
for (newsw of newswList) {
|
||||
if (newsw != '') {
|
||||
if (stopwords.includes(newsw)) {
|
||||
|
||||
} else {
|
||||
stopwords.push(newsw)
|
||||
$('#sweContainer').append($('<li>').attr('class', 'w3-display-container').append($('<span>').append(newsw)).append($('<span>').attr('class', 'w3-button w3-hover-red w3-transparent w3-display-right').click(function(e) {
|
||||
var index = $(this).parent().index()
|
||||
console.log(stopwords[index])
|
||||
stopwords.splice(index, 1)
|
||||
console.log(stopwords)
|
||||
$('#sweContainer li').eq(index).remove()
|
||||
}).append("×")))
|
||||
console.log(document.getElementById('sweContainer').children[stopwords.indexOf(newsw)])
|
||||
}
|
||||
document.getElementById("sweContainer").scrollTop = document.getElementById('sweContainer').children[stopwords.indexOf(newsw)].offsetTop
|
||||
}
|
||||
}
|
||||
$('#newStopWord').val('')
|
||||
}
|
||||
|
||||
function showStopwordEditor() {
|
||||
console.log(stopwords)
|
||||
$(window).unbind('keydown')
|
||||
$(window).keydown(function(event) {
|
||||
if (event.keyCode == 13) {
|
||||
addStopWord()
|
||||
}
|
||||
})
|
||||
$('#sweContainer').empty()
|
||||
for (word of stopwords) {
|
||||
$('#sweContainer').append($('<li>').attr('class', 'w3-display-container').append($('<span>').append(word)).append($('<span>').attr('class', 'w3-button w3-hover-red w3-transparent w3-display-right').click(function(e) {
|
||||
var index = $(this).parent().index()
|
||||
console.log(stopwords[index])
|
||||
stopwords.splice(index, 1)
|
||||
console.log(stopwords)
|
||||
$('#sweContainer li').eq(index).remove()
|
||||
}).append("×")))
|
||||
}
|
||||
$('#stopWordEditorLayer').removeClass('hidden')
|
||||
}
|
||||
|
||||
function hideStopWordEditor() {
|
||||
$(window).unbind('keydown')
|
||||
$(window).keydown(function(event) {
|
||||
if (event.keyCode == 13) {
|
||||
event.preventDefault()
|
||||
sendRequest()
|
||||
}
|
||||
})
|
||||
$('#stopWordEditorLayer').addClass('hidden')
|
||||
}
|
||||
|
||||
function submit() {
|
||||
text = $('#rawTextBox').val()
|
||||
$('#rawText').addClass('hidden')
|
||||
$('#toggleTextBox').html('顯示文字輸入區')
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/post/generalTxt/addText',
|
||||
data: JSON.stringify({
|
||||
text: text,
|
||||
stopwords: stopwords
|
||||
}),
|
||||
contentType: 'application/json',
|
||||
success: function(data) {
|
||||
tsvPath = data.Result.path
|
||||
destroyCurrentGraph()
|
||||
d3.select('#graph').append('div').attr('id', 'vis')
|
||||
buildSentetree()
|
||||
$('#graph').removeClass('hidden')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function buildSentetree() {
|
||||
console.log("Build.")
|
||||
var model;
|
||||
var tree;
|
||||
var data;
|
||||
const graph = d3.tsv(tsvPath, buildTree);
|
||||
|
||||
function buildTree(error, rawdata) {
|
||||
const data = rawdata.map(d => Object.assign({}, d, { count: +d.count }));
|
||||
model = new SentenTree.SentenTreeBuilder()
|
||||
.tokenize(SentenTree.tokenizer.tokenizeBySpace)
|
||||
.transformToken(token => (/score(d|s)?/.test(token) ? 'score' : token))
|
||||
.buildModel(data, { maxSupportRatio: 1 });
|
||||
tree = new SentenTree.SentenTreeVis('#vis', {
|
||||
fontSize: [15, 40],
|
||||
gapBetweenGraph: 10
|
||||
});
|
||||
tree.data(model.getRenderedGraphs(2))
|
||||
new ResizeSensor(jQuery('#d3kitRoot'), function() {
|
||||
var scale, origin;
|
||||
scale = Math.min(2, ($('#graph').outerWidth()) / ($('#d3kitRoot').outerWidth() + 60))
|
||||
|
||||
$('#vis').css({
|
||||
transform: "scale(" + scale + ")",
|
||||
'transform-origin': 'top left'
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function destroyCurrentGraph() {
|
||||
d3.selectAll('#vis').remove()
|
||||
}
|
||||
|
||||
function switchMessageBox() {
|
||||
$('#rawText').toggleClass('hidden')
|
||||
if ($('#rawText').hasClass('hidden')) {
|
||||
$('#toggleTextBox').html('顯示文字輸入區')
|
||||
} else {
|
||||
$('#toggleTextBox').html('隱藏文字輸入區')
|
||||
}
|
||||
}
|
@ -0,0 +1,348 @@
|
||||
init()
|
||||
var tsvPath = ''
|
||||
var titlePath = ''
|
||||
var defaultStartDate
|
||||
var defaultEndDate
|
||||
var totalPosts
|
||||
var startDate
|
||||
var endDate
|
||||
var wordTitleList
|
||||
var randId
|
||||
var globKeyword = ''
|
||||
var stopwords = []
|
||||
|
||||
function init() {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/init',
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
console.log(data)
|
||||
setDate(data.Result.startDate, data.Result.endDate)
|
||||
document.getElementById('keywordBox').value = data.Result.keyword
|
||||
titlePath = data.Result.titlePath
|
||||
tsvString = data.Result.info.tsv
|
||||
defaultStartDate = data.Result.startDate
|
||||
defaultEndDate = data.Result.endDate
|
||||
json = JSON.parse(data.Result.info.json)
|
||||
console.log(json)
|
||||
wordTitleList = json
|
||||
keywordCountString = ''
|
||||
stopwords = data.Result.info.stopWords
|
||||
if (json.info.keyword != '') {
|
||||
keywordCountString = ' 關鍵字出現次數:' + json.info.count
|
||||
}
|
||||
$('#graphInfo').empty()
|
||||
$('#graphInfo').attr('style', 'margin: 10px;').append('總文章數:' + json.info.posts + ',' + keywordCountString)
|
||||
totalPosts = json.info.posts
|
||||
buildSentetree(tsvString)
|
||||
}
|
||||
})
|
||||
$(document).ready(function() {
|
||||
$(window).keydown(function(event) {
|
||||
if (event.keyCode == 13) {
|
||||
event.preventDefault()
|
||||
sendRequest()
|
||||
}
|
||||
});
|
||||
});
|
||||
$(window).on('mousemove', function(e) {
|
||||
$('#nodeTitle').css({
|
||||
left: e.pageX,
|
||||
top: e.pageY
|
||||
})
|
||||
})
|
||||
$('#titleListContainer').hover(
|
||||
function() { // Run on hover/mouseenter
|
||||
$(this).css('overflow', 'auto')
|
||||
},
|
||||
function() { // Run on mouseleave
|
||||
$(this).css('overflow', 'hidden')
|
||||
}
|
||||
)
|
||||
$('#titleListLayer').click(function(e) {
|
||||
if ($('#titleListLayer').is(e.target)) {
|
||||
hideTitles()
|
||||
}
|
||||
})
|
||||
$('#stopWordEditorLayer').click(function(e) {
|
||||
if ($('#stopWordEditorLayer').is(e.target)) {
|
||||
hideStopWordEditor()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function clearStopWord() {
|
||||
stopwords = []
|
||||
$('#sweContainer').html('')
|
||||
}
|
||||
|
||||
function addStopWord() {
|
||||
newswRaw = $('#newStopWord').val()
|
||||
newswList = newswRaw.split(' ')
|
||||
for (newsw of newswList) {
|
||||
if (newsw != '') {
|
||||
if (stopwords.includes(newsw)) {
|
||||
|
||||
} else {
|
||||
stopwords.push(newsw)
|
||||
$('#sweContainer').append($('<li>').attr('class', 'w3-display-container').append($('<span>').append(newsw)).append($('<span>').attr('class', 'w3-button w3-hover-red w3-transparent w3-display-right').click(function(e) {
|
||||
var index = $(this).parent().index()
|
||||
console.log(stopwords[index])
|
||||
stopwords.splice(index, 1)
|
||||
console.log(stopwords)
|
||||
$('#sweContainer li').eq(index).remove()
|
||||
}).append("×")))
|
||||
console.log(document.getElementById('sweContainer').children[stopwords.indexOf(newsw)])
|
||||
}
|
||||
document.getElementById("sweContainer").scrollTop = document.getElementById('sweContainer').children[stopwords.indexOf(newsw)].offsetTop
|
||||
}
|
||||
}
|
||||
$('#newStopWord').val('')
|
||||
}
|
||||
|
||||
function showStopwordEditor() {
|
||||
console.log(stopwords)
|
||||
$(window).unbind('keydown')
|
||||
$(window).keydown(function(event) {
|
||||
if (event.keyCode == 13) {
|
||||
addStopWord()
|
||||
}
|
||||
})
|
||||
$('#sweContainer').empty()
|
||||
for (word of stopwords) {
|
||||
$('#sweContainer').append($('<li>').attr('class', 'w3-display-container').append($('<span>').append(word)).append($('<span>').attr('class', 'w3-button w3-hover-red w3-transparent w3-display-right').click(function(e) {
|
||||
var index = $(this).parent().index()
|
||||
console.log(stopwords[index])
|
||||
stopwords.splice(index, 1)
|
||||
console.log(stopwords)
|
||||
$('#sweContainer li').eq(index).remove()
|
||||
}).append("×")))
|
||||
}
|
||||
$('#stopWordEditorLayer').removeClass('hidden')
|
||||
}
|
||||
|
||||
function hideStopWordEditor() {
|
||||
$(window).unbind('keydown')
|
||||
$(window).keydown(function(event) {
|
||||
if (event.keyCode == 13) {
|
||||
event.preventDefault()
|
||||
sendRequest()
|
||||
}
|
||||
})
|
||||
$('#stopWordEditorLayer').addClass('hidden')
|
||||
}
|
||||
|
||||
function downloadStopWord() {
|
||||
stopWordString = stopwords.join('\n')
|
||||
download(stopWordString, 'stopwords.txt', 'text/plain')
|
||||
}
|
||||
|
||||
|
||||
function hidePopup() {
|
||||
$('#infoWindowLayer').toggleClass('hidden')
|
||||
$('#progressInfo').html('')
|
||||
$('#progBarInner').css('width', 0 + '%')
|
||||
closeEventListner()
|
||||
}
|
||||
|
||||
function setDate(_startDate, _endDate) {
|
||||
document.getElementById('startDate').value = _startDate
|
||||
document.getElementById("endDate").value = _endDate
|
||||
startDate = _startDate
|
||||
endDate = _endDate
|
||||
}
|
||||
|
||||
function getProgressing(event) {
|
||||
data = JSON.parse(event.data)
|
||||
$('#progressInfo').html(data.comment)
|
||||
$('#progBarInner').css('width', data.progress + '%')
|
||||
}
|
||||
|
||||
function getProgressFinished(event) {
|
||||
data = JSON.parse(event.data)
|
||||
changeGraph(data)
|
||||
hidePopup()
|
||||
}
|
||||
|
||||
function closeEventListner() {
|
||||
progListener.removeEventListener('progressing' + randId, getProgressing)
|
||||
progListener.removeEventListener('progressFinished' + randId, getProgressFinished)
|
||||
}
|
||||
|
||||
function sendRequest() {
|
||||
content = JSON.stringify({
|
||||
startDate: $('#startDate').val(),
|
||||
endDate: $('#endDate').val(),
|
||||
keyword: $('#keywordBox').val(),
|
||||
stopwords: stopwords,
|
||||
pos: {
|
||||
noun: $('#noun').is(':checked'),
|
||||
verb: $('#verb').is(':checked'),
|
||||
adj: $('#adj').is(':checked'),
|
||||
adv: $('#adv').is(':checked'),
|
||||
pron: $('#pron').is(':checked'),
|
||||
aux: $('#aux').is(':checked'),
|
||||
other: $('#other').is(':checked')
|
||||
}
|
||||
})
|
||||
startDate = $('#startDate').val()
|
||||
endDate = $('#endDate').val()
|
||||
console.log(content)
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/addRequest',
|
||||
data: content,
|
||||
contentType: 'application/json',
|
||||
success: function(data) {
|
||||
console.log(data)
|
||||
changeGraph(data.Result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function changeGraph(data) {
|
||||
console.log(data)
|
||||
tsvString = data.info.tsv
|
||||
json = JSON.parse(data.info.json)
|
||||
globKeyword = json.info.keyword
|
||||
console.log(json)
|
||||
wordTitleList = json
|
||||
keywordCountString = ''
|
||||
if (json.info.keyword != '') {
|
||||
keywordCountString = ', 關鍵字出現次數:' + json.info.count
|
||||
}
|
||||
$('#graphInfo').empty()
|
||||
$('#graphInfo').attr('style', 'margin: 10px;').append('總文章數:' + json.info.posts + keywordCountString)
|
||||
totalPosts = json.info.posts
|
||||
destroyCurrentGraph()
|
||||
d3.select('#graph').append('div').attr('id', 'vis')
|
||||
buildSentetree(tsvString)
|
||||
}
|
||||
|
||||
function destroyCurrentGraph() {
|
||||
d3.selectAll('#vis').remove()
|
||||
}
|
||||
|
||||
function hideTitles() {
|
||||
$('#titleListLayer').addClass('hidden')
|
||||
}
|
||||
|
||||
function buildSentetree(tsvString) {
|
||||
console.log("Build.")
|
||||
var model;
|
||||
var tree;
|
||||
var data;
|
||||
if (typeof tsvString === 'undefined') {
|
||||
d3.tsv(tsvPath, buildTree)
|
||||
} else {
|
||||
data = d3.tsvParse(tsvString)
|
||||
buildTree(_, data)
|
||||
}
|
||||
|
||||
function buildTree(error, rawdata) {
|
||||
const data = rawdata.map(d => Object.assign({}, d, { count: +d.count }));
|
||||
model = new SentenTree.SentenTreeBuilder()
|
||||
.tokenize(SentenTree.tokenizer.tokenizeBySpace)
|
||||
.transformToken(token => (/score(d|s)?/.test(token) ? 'score' : token))
|
||||
.buildModel(data, {
|
||||
maxSupportRatio: 0.8,
|
||||
minSupportRatio: 0.001
|
||||
});
|
||||
tree = new SentenTree.SentenTreeVis('#vis', {
|
||||
fontSize: [15, 40],
|
||||
gapBetweenGraph: 10
|
||||
});
|
||||
tree.data(model.getRenderedGraphs(2))
|
||||
.on('nodeClick', node => {
|
||||
$("#keywordBox").val(node.data.entity)
|
||||
$('#titleListLayer').removeClass('hidden')
|
||||
seqList = node.data.seq.DBs.map(function(n) {
|
||||
return n.rawText
|
||||
})
|
||||
titleList = []
|
||||
for (s of seqList) {
|
||||
titleTemp = wordTitleList[s]
|
||||
if ((titleList.map(function(n) {
|
||||
return n.title
|
||||
})).indexOf(titleTemp.title) == -1) {
|
||||
titleList.push(titleTemp)
|
||||
}
|
||||
}
|
||||
console.log(titleList)
|
||||
info = wordTitleList[node.data.entity]
|
||||
$('#titleListKeyword').html(node.data.entity)
|
||||
$('#titleListKeywordInfo').html('')
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/ptt/keywordFrequency',
|
||||
data: JSON.stringify({
|
||||
startDate: startDate,
|
||||
endDate: endDate,
|
||||
keyword: node.data.entity,
|
||||
globKeyword: globKeyword
|
||||
}),
|
||||
contentType: 'application/json',
|
||||
success: function(data) {
|
||||
console.log(data)
|
||||
$('#titleListKeywordInfo').html('單詞出現次數:' + data.Result.wordCount + ', 單詞出現的文章數:' + data.Result.postCount + ', 單詞頻率:' + (data.Result.postCount * 100 / totalPosts).toFixed(2) + '%')
|
||||
}
|
||||
})
|
||||
$('#titleListContainer').empty()
|
||||
for (i of titleList) {
|
||||
$('#titleListContainer').append(
|
||||
$('<li>').attr('class', 'w3-panel').append(
|
||||
$('<a>').attr('href', i.url).attr('target', '_blank').append(
|
||||
$('<h4>').html(i.title)
|
||||
).append(
|
||||
$('<span>').attr('style', 'margin: 0px 10px').html(i.author)
|
||||
).append(
|
||||
$('<span>').attr('style', 'margin: 0px 10px').html(i.date)
|
||||
).append(
|
||||
$('<span>').attr('style', 'margin: 0px 10px').html('推文數:' + i.pushes)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
})
|
||||
.on('nodeMouseenter', node => {
|
||||
console.log(node)
|
||||
titles = node.data.topEntries.map(function(x) {
|
||||
return wordTitleList[x.rawText]
|
||||
})
|
||||
//console.log(titles)
|
||||
infoStr = ''
|
||||
for (index in titles) {
|
||||
if (index == 0) {
|
||||
infoStr += titles[index].title + '<br>'
|
||||
} else {
|
||||
if (titles[index].title != titles[index - 1].title) {
|
||||
infoStr += titles[index].title + '<br>'
|
||||
}
|
||||
}
|
||||
pos = titles[index].part.indexOf(node.data.entity)
|
||||
infoStr += '... ' + titles[index].part.slice(Math.max(0, pos - 20), Math.min(titles[index].part.length - 1, pos + 20)) + ' ...<br>'
|
||||
}
|
||||
$(nodeTitleContent).html(infoStr)
|
||||
$('#nodeTitle').removeClass('hidden')
|
||||
tree.highlightNeighbors(node)
|
||||
})
|
||||
.on('nodeMouseleave', node => {
|
||||
$('#nodeTitle').addClass('hidden')
|
||||
tree.clearHighlightNeighbors()
|
||||
}).on('layoutStart', layout => {
|
||||
console.log(layout)
|
||||
}).on('linkMouseenter', link => {
|
||||
console.log(link)
|
||||
})
|
||||
new ResizeSensor(jQuery('#d3kitRoot'), function() {
|
||||
var scale, origin;
|
||||
scale = Math.min(2, ($('#graph').outerWidth()) / ($('#d3kitRoot').outerWidth() + 60))
|
||||
|
||||
$('#vis').css({
|
||||
transform: "scale(" + scale + ")",
|
||||
'transform-origin': 'top left'
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
"$basedir/node" "$basedir/../d3-dsv/bin/dsv2json" "$@"
|
||||
ret=$?
|
||||
else
|
||||
node "$basedir/../d3-dsv/bin/dsv2json" "$@"
|
||||
ret=$?
|
||||
fi
|
||||
exit $ret
|
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
"%_prog%" "%dp0%\..\d3-dsv\bin\dsv2json" %*
|
||||
ENDLOCAL
|
||||
EXIT /b %errorlevel%
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
& "$basedir/node$exe" "$basedir/../d3-dsv/bin/dsv2json" $args
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
& "node$exe" "$basedir/../d3-dsv/bin/dsv2json" $args
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
"$basedir/node" "$basedir/../d3-dsv/bin/dsv2dsv" "$@"
|
||||
ret=$?
|
||||
else
|
||||
node "$basedir/../d3-dsv/bin/dsv2dsv" "$@"
|
||||
ret=$?
|
||||
fi
|
||||
exit $ret
|
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
"%_prog%" "%dp0%\..\d3-dsv\bin\dsv2dsv" %*
|
||||
ENDLOCAL
|
||||
EXIT /b %errorlevel%
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
& "$basedir/node$exe" "$basedir/../d3-dsv/bin/dsv2dsv" $args
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
& "node$exe" "$basedir/../d3-dsv/bin/dsv2dsv" $args
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
"$basedir/node" "$basedir/../d3-dsv/bin/dsv2dsv" "$@"
|
||||
ret=$?
|
||||
else
|
||||
node "$basedir/../d3-dsv/bin/dsv2dsv" "$@"
|
||||
ret=$?
|
||||
fi
|
||||
exit $ret
|
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
"%_prog%" "%dp0%\..\d3-dsv\bin\dsv2dsv" %*
|
||||
ENDLOCAL
|
||||
EXIT /b %errorlevel%
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
& "$basedir/node$exe" "$basedir/../d3-dsv/bin/dsv2dsv" $args
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
& "node$exe" "$basedir/../d3-dsv/bin/dsv2dsv" $args
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
"$basedir/node" "$basedir/../d3-dsv/bin/dsv2json" "$@"
|
||||
ret=$?
|
||||
else
|
||||
node "$basedir/../d3-dsv/bin/dsv2json" "$@"
|
||||
ret=$?
|
||||
fi
|
||||
exit $ret
|
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
"%_prog%" "%dp0%\..\d3-dsv\bin\dsv2json" %*
|
||||
ENDLOCAL
|
||||
EXIT /b %errorlevel%
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
& "$basedir/node$exe" "$basedir/../d3-dsv/bin/dsv2json" $args
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
& "node$exe" "$basedir/../d3-dsv/bin/dsv2json" $args
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
"$basedir/node" "$basedir/../d3-dsv/bin/json2dsv" "$@"
|
||||
ret=$?
|
||||
else
|
||||
node "$basedir/../d3-dsv/bin/json2dsv" "$@"
|
||||
ret=$?
|
||||
fi
|
||||
exit $ret
|
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
"%_prog%" "%dp0%\..\d3-dsv\bin\json2dsv" %*
|
||||
ENDLOCAL
|
||||
EXIT /b %errorlevel%
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
& "$basedir/node$exe" "$basedir/../d3-dsv/bin/json2dsv" $args
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
& "node$exe" "$basedir/../d3-dsv/bin/json2dsv" $args
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
"$basedir/node" "$basedir/../d3-dsv/bin/json2dsv" "$@"
|
||||
ret=$?
|
||||
else
|
||||
node "$basedir/../d3-dsv/bin/json2dsv" "$@"
|
||||
ret=$?
|
||||
fi
|
||||
exit $ret
|
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
"%_prog%" "%dp0%\..\d3-dsv\bin\json2dsv" %*
|
||||
ENDLOCAL
|
||||
EXIT /b %errorlevel%
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
& "$basedir/node$exe" "$basedir/../d3-dsv/bin/json2dsv" $args
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
& "node$exe" "$basedir/../d3-dsv/bin/json2dsv" $args
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
"$basedir/node" "$basedir/../d3-dsv/bin/json2dsv" "$@"
|
||||
ret=$?
|
||||
else
|
||||
node "$basedir/../d3-dsv/bin/json2dsv" "$@"
|
||||
ret=$?
|
||||
fi
|
||||
exit $ret
|
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
"%_prog%" "%dp0%\..\d3-dsv\bin\json2dsv" %*
|
||||
ENDLOCAL
|
||||
EXIT /b %errorlevel%
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
& "$basedir/node$exe" "$basedir/../d3-dsv/bin/json2dsv" $args
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
& "node$exe" "$basedir/../d3-dsv/bin/json2dsv" $args
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
"$basedir/node" "$basedir/../d3-dsv/bin/dsv2dsv" "$@"
|
||||
ret=$?
|
||||
else
|
||||
node "$basedir/../d3-dsv/bin/dsv2dsv" "$@"
|
||||
ret=$?
|
||||
fi
|
||||
exit $ret
|
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
"%_prog%" "%dp0%\..\d3-dsv\bin\dsv2dsv" %*
|
||||
ENDLOCAL
|
||||
EXIT /b %errorlevel%
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
& "$basedir/node$exe" "$basedir/../d3-dsv/bin/dsv2dsv" $args
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
& "node$exe" "$basedir/../d3-dsv/bin/dsv2dsv" $args
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
"$basedir/node" "$basedir/../d3-dsv/bin/dsv2json" "$@"
|
||||
ret=$?
|
||||
else
|
||||
node "$basedir/../d3-dsv/bin/dsv2json" "$@"
|
||||
ret=$?
|
||||
fi
|
||||
exit $ret
|
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
"%_prog%" "%dp0%\..\d3-dsv\bin\dsv2json" %*
|
||||
ENDLOCAL
|
||||
EXIT /b %errorlevel%
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
& "$basedir/node$exe" "$basedir/../d3-dsv/bin/dsv2json" $args
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
& "node$exe" "$basedir/../d3-dsv/bin/dsv2json" $args
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
@ -0,0 +1,22 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,70 @@
|
||||
{
|
||||
"_from": "commander@2",
|
||||
"_id": "commander@2.20.3",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"_location": "/commander",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "commander@2",
|
||||
"name": "commander",
|
||||
"escapedName": "commander",
|
||||
"rawSpec": "2",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "2"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/d3-dsv"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"_shasum": "fd485e84c03eb4881c20722ba48035e8531aeb33",
|
||||
"_spec": "commander@2",
|
||||
"_where": "C:\\Users\\a5640\\OneDrive - National ChengChi University\\programming\\Python\\flask\\sententree\\static\\node_modules\\d3-dsv",
|
||||
"author": {
|
||||
"name": "TJ Holowaychuk",
|
||||
"email": "tj@vision-media.ca"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/tj/commander.js/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {},
|
||||
"deprecated": false,
|
||||
"description": "the complete solution for node.js command-line programs",
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.7.8",
|
||||
"eslint": "^6.4.0",
|
||||
"should": "^13.2.3",
|
||||
"sinon": "^7.5.0",
|
||||
"standard": "^14.3.1",
|
||||
"ts-node": "^8.4.1",
|
||||
"typescript": "^3.6.3"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"typings/index.d.ts"
|
||||
],
|
||||
"homepage": "https://github.com/tj/commander.js#readme",
|
||||
"keywords": [
|
||||
"commander",
|
||||
"command",
|
||||
"option",
|
||||
"parser"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "index",
|
||||
"name": "commander",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/tj/commander.js.git"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint index.js",
|
||||
"test": "node test/run.js && npm run test-typings",
|
||||
"test-typings": "tsc -p tsconfig.json"
|
||||
},
|
||||
"typings": "typings/index.d.ts",
|
||||
"version": "2.20.3"
|
||||
}
|
@ -0,0 +1,310 @@
|
||||
// Type definitions for commander 2.11
|
||||
// Project: https://github.com/visionmedia/commander.js
|
||||
// Definitions by: Alan Agius <https://github.com/alan-agius4>, Marcelo Dezem <https://github.com/mdezem>, vvakame <https://github.com/vvakame>, Jules Randolph <https://github.com/sveinburne>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
|
||||
declare namespace local {
|
||||
|
||||
class Option {
|
||||
flags: string;
|
||||
required: boolean;
|
||||
optional: boolean;
|
||||
bool: boolean;
|
||||
short?: string;
|
||||
long: string;
|
||||
description: string;
|
||||
|
||||
/**
|
||||
* Initialize a new `Option` with the given `flags` and `description`.
|
||||
*
|
||||
* @param {string} flags
|
||||
* @param {string} [description]
|
||||
*/
|
||||
constructor(flags: string, description?: string);
|
||||
}
|
||||
|
||||
class Command extends NodeJS.EventEmitter {
|
||||
[key: string]: any;
|
||||
|
||||
args: string[];
|
||||
|
||||
/**
|
||||
* Initialize a new `Command`.
|
||||
*
|
||||
* @param {string} [name]
|
||||
*/
|
||||
constructor(name?: string);
|
||||
|
||||
/**
|
||||
* Set the program version to `str`.
|
||||
*
|
||||
* This method auto-registers the "-V, --version" flag
|
||||
* which will print the version number when passed.
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {string} [flags]
|
||||
* @returns {Command} for chaining
|
||||
*/
|
||||
version(str: string, flags?: string): Command;
|
||||
|
||||
/**
|
||||
* Add command `name`.
|
||||
*
|
||||
* The `.action()` callback is invoked when the
|
||||
* command `name` is specified via __ARGV__,
|
||||
* and the remaining arguments are applied to the
|
||||
* function for access.
|
||||
*
|
||||
* When the `name` is "*" an un-matched command
|
||||
* will be passed as the first arg, followed by
|
||||
* the rest of __ARGV__ remaining.
|
||||
*
|
||||
* @example
|
||||
* program
|
||||
* .version('0.0.1')
|
||||
* .option('-C, --chdir <path>', 'change the working directory')
|
||||
* .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
|
||||
* .option('-T, --no-tests', 'ignore test hook')
|
||||
*
|
||||
* program
|
||||
* .command('setup')
|
||||
* .description('run remote setup commands')
|
||||
* .action(function() {
|
||||
* console.log('setup');
|
||||
* });
|
||||
*
|
||||
* program
|
||||
* .command('exec <cmd>')
|
||||
* .description('run the given remote command')
|
||||
* .action(function(cmd) {
|
||||
* console.log('exec "%s"', cmd);
|
||||
* });
|
||||
*
|
||||
* program
|
||||
* .command('teardown <dir> [otherDirs...]')
|
||||
* .description('run teardown commands')
|
||||
* .action(function(dir, otherDirs) {
|
||||
* console.log('dir "%s"', dir);
|
||||
* if (otherDirs) {
|
||||
* otherDirs.forEach(function (oDir) {
|
||||
* console.log('dir "%s"', oDir);
|
||||
* });
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* program
|
||||
* .command('*')
|
||||
* .description('deploy the given env')
|
||||
* .action(function(env) {
|
||||
* console.log('deploying "%s"', env);
|
||||
* });
|
||||
*
|
||||
* program.parse(process.argv);
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {string} [desc] for git-style sub-commands
|
||||
* @param {CommandOptions} [opts] command options
|
||||
* @returns {Command} the new command
|
||||
*/
|
||||
command(name: string, desc?: string, opts?: commander.CommandOptions): Command;
|
||||
|
||||
/**
|
||||
* Define argument syntax for the top-level command.
|
||||
*
|
||||
* @param {string} desc
|
||||
* @returns {Command} for chaining
|
||||
*/
|
||||
arguments(desc: string): Command;
|
||||
|
||||
/**
|
||||
* Parse expected `args`.
|
||||
*
|
||||
* For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`.
|
||||
*
|
||||
* @param {string[]} args
|
||||
* @returns {Command} for chaining
|
||||
*/
|
||||
parseExpectedArgs(args: string[]): Command;
|
||||
|
||||
/**
|
||||
* Register callback `fn` for the command.
|
||||
*
|
||||
* @example
|
||||
* program
|
||||
* .command('help')
|
||||
* .description('display verbose help')
|
||||
* .action(function() {
|
||||
* // output help here
|
||||
* });
|
||||
*
|
||||
* @param {(...args: any[]) => void} fn
|
||||
* @returns {Command} for chaining
|
||||
*/
|
||||
action(fn: (...args: any[]) => void): Command;
|
||||
|
||||
/**
|
||||
* Define option with `flags`, `description` and optional
|
||||
* coercion `fn`.
|
||||
*
|
||||
* The `flags` string should contain both the short and long flags,
|
||||
* separated by comma, a pipe or space. The following are all valid
|
||||
* all will output this way when `--help` is used.
|
||||
*
|
||||
* "-p, --pepper"
|
||||
* "-p|--pepper"
|
||||
* "-p --pepper"
|
||||
*
|
||||
* @example
|
||||
* // simple boolean defaulting to false
|
||||
* program.option('-p, --pepper', 'add pepper');
|
||||
*
|
||||
* --pepper
|
||||
* program.pepper
|
||||
* // => Boolean
|
||||
*
|
||||
* // simple boolean defaulting to true
|
||||
* program.option('-C, --no-cheese', 'remove cheese');
|
||||
*
|
||||
* program.cheese
|
||||
* // => true
|
||||
*
|
||||
* --no-cheese
|
||||
* program.cheese
|
||||
* // => false
|
||||
*
|
||||
* // required argument
|
||||
* program.option('-C, --chdir <path>', 'change the working directory');
|
||||
*
|
||||
* --chdir /tmp
|
||||
* program.chdir
|
||||
* // => "/tmp"
|
||||
*
|
||||
* // optional argument
|
||||
* program.option('-c, --cheese [type]', 'add cheese [marble]');
|
||||
*
|
||||
* @param {string} flags
|
||||
* @param {string} [description]
|
||||
* @param {((arg1: any, arg2: any) => void) | RegExp} [fn] function or default
|
||||
* @param {*} [defaultValue]
|
||||
* @returns {Command} for chaining
|
||||
*/
|
||||
option(flags: string, description?: string, fn?: ((arg1: any, arg2: any) => void) | RegExp, defaultValue?: any): Command;
|
||||
option(flags: string, description?: string, defaultValue?: any): Command;
|
||||
|
||||
/**
|
||||
* Allow unknown options on the command line.
|
||||
*
|
||||
* @param {boolean} [arg] if `true` or omitted, no error will be thrown for unknown options.
|
||||
* @returns {Command} for chaining
|
||||
*/
|
||||
allowUnknownOption(arg?: boolean): Command;
|
||||
|
||||
/**
|
||||
* Parse `argv`, settings options and invoking commands when defined.
|
||||
*
|
||||
* @param {string[]} argv
|
||||
* @returns {Command} for chaining
|
||||
*/
|
||||
parse(argv: string[]): Command;
|
||||
|
||||
/**
|
||||
* Parse options from `argv` returning `argv` void of these options.
|
||||
*
|
||||
* @param {string[]} argv
|
||||
* @returns {ParseOptionsResult}
|
||||
*/
|
||||
parseOptions(argv: string[]): commander.ParseOptionsResult;
|
||||
|
||||
/**
|
||||
* Return an object containing options as key-value pairs
|
||||
*
|
||||
* @returns {{[key: string]: any}}
|
||||
*/
|
||||
opts(): { [key: string]: any };
|
||||
|
||||
/**
|
||||
* Set the description to `str`.
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {{[argName: string]: string}} argsDescription
|
||||
* @return {(Command | string)}
|
||||
*/
|
||||
description(str: string, argsDescription?: {[argName: string]: string}): Command;
|
||||
description(): string;
|
||||
|
||||
/**
|
||||
* Set an alias for the command.
|
||||
*
|
||||
* @param {string} alias
|
||||
* @return {(Command | string)}
|
||||
*/
|
||||
alias(alias: string): Command;
|
||||
alias(): string;
|
||||
|
||||
/**
|
||||
* Set or get the command usage.
|
||||
*
|
||||
* @param {string} str
|
||||
* @return {(Command | string)}
|
||||
*/
|
||||
usage(str: string): Command;
|
||||
usage(): string;
|
||||
|
||||
/**
|
||||
* Set the name of the command.
|
||||
*
|
||||
* @param {string} str
|
||||
* @return {Command}
|
||||
*/
|
||||
name(str: string): Command;
|
||||
|
||||
/**
|
||||
* Get the name of the command.
|
||||
*
|
||||
* @return {string}
|
||||
*/
|
||||
name(): string;
|
||||
|
||||
/**
|
||||
* Output help information for this command.
|
||||
*
|
||||
* @param {(str: string) => string} [cb]
|
||||
*/
|
||||
outputHelp(cb?: (str: string) => string): void;
|
||||
|
||||
/** Output help information and exit.
|
||||
*
|
||||
* @param {(str: string) => string} [cb]
|
||||
*/
|
||||
help(cb?: (str: string) => string): never;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
declare namespace commander {
|
||||
|
||||
type Command = local.Command
|
||||
|
||||
type Option = local.Option
|
||||
|
||||
interface CommandOptions {
|
||||
noHelp?: boolean;
|
||||
isDefault?: boolean;
|
||||
}
|
||||
|
||||
interface ParseOptionsResult {
|
||||
args: string[];
|
||||
unknown: string[];
|
||||
}
|
||||
|
||||
interface CommanderStatic extends Command {
|
||||
Command: typeof local.Command;
|
||||
Option: typeof local.Option;
|
||||
CommandOptions: CommandOptions;
|
||||
ParseOptionsResult: ParseOptionsResult;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
declare const commander: commander.CommanderStatic;
|
||||
export = commander;
|
Binary file not shown.
@ -0,0 +1 @@
|
||||
github: marcj
|
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2013 Marc J. Schmidt
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -0,0 +1,112 @@
|
||||
# CSS Element Queries
|
||||
|
||||
|
||||
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/marcj/css-element-queries?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
Element Queries is a polyfill adding support for element based media-queries to all new browsers (incl. IE7+).
|
||||
It allows not only to define media-queries based on window-size but also adds 'media-queries' functionality depending on element (any selector supported)
|
||||
size while not causing performance lags due to event based implementation.
|
||||
|
||||
It's a proof-of-concept event-based CSS element dimension query with valid CSS selector syntax.
|
||||
|
||||
Features:
|
||||
|
||||
- no performance issues since it listens only on size changes of elements that have element query rules defined through css. Other element query polifills only listen on `window.onresize` which causes performance issues and allows only to detect changes via window.resize event and not inside layout changes like css3 animation, :hover, DOM changes etc.
|
||||
- no interval/timeout detection. Truly event-based through integrated ResizeSensor class.
|
||||
- automatically discovers new DOM elements. No need to call javascript manually.
|
||||
- no CSS modifications. Valid CSS Syntax
|
||||
- all CSS selectors available. Uses regular attribute selector. No need to write rules in HTML/JS.
|
||||
- supports and tested in webkit, gecko and IE(10+)
|
||||
- `min-width`, `min-height`, `max-width` and `max-height` are supported so far
|
||||
- works with any layout modifications: HTML (innerHTML etc), inline styles, DOM mutation, CSS3 transitions, fluid layout changes (also percent changes), pseudo classes (:hover etc.), window resizes and more
|
||||
- no Javascript-Framework dependency (works with jQuery, Mootools, etc.)
|
||||
- Works beautiful for responsive images without FOUC
|
||||
|
||||
More demos and information: http://marcj.github.io/css-element-queries/
|
||||
|
||||
## Examples
|
||||
|
||||
### Element Query
|
||||
|
||||
```css
|
||||
.widget-name h2 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.widget-name[min-width~="400px"] h2 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.widget-name[min-width~="600px"] h2 {
|
||||
padding: 55px;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.widget-name[min-width~="700px"] h2 {
|
||||
font-size: 34px;
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
|
||||
As you can see we use the `~=` [attribute selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors).
|
||||
Since this css-element-queries polyfill adds new element attributes on the DOM element
|
||||
(`<div class="widget-name" min-width="400px 700px"></div>`) depending on your actual CSS and element's dimension,
|
||||
you should always use this attribute selector (especially if you have several element query rules on the same element).
|
||||
|
||||
```html
|
||||
<div class="widget-name">
|
||||
<h2>Element responsiveness FTW!</h2>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Responsive image
|
||||
|
||||
```html
|
||||
<div data-responsive-image>
|
||||
<img data-src="http://placehold.it/350x150"/>
|
||||
<img min-width="350" data-src="http://placehold.it/700x300"/>
|
||||
<img min-width="700" data-src="http://placehold.it/1400x600"/>
|
||||
</div>
|
||||
```
|
||||
|
||||
Include the javascript files at the bottom and you're good to go. No custom javascript calls needed.
|
||||
|
||||
```html
|
||||
<script src="src/ResizeSensor.js"></script>
|
||||
<script src="src/ElementQueries.js"></script>
|
||||
```
|
||||
|
||||
## See it in action:
|
||||
|
||||
Here live http://marcj.github.io/css-element-queries/.
|
||||
|
||||
![Demo](http://marcj.github.io/css-element-queries/images/css-element-queries-demo.gif)
|
||||
|
||||
|
||||
## Module Loader
|
||||
|
||||
If you're using a module loader you need to trigger the event listening or initialization yourself:
|
||||
|
||||
```javascript
|
||||
var ElementQueries = require('css-element-queries/src/ElementQueries');
|
||||
|
||||
//attaches to DOMLoadContent
|
||||
ElementQueries.listen();
|
||||
|
||||
//or if you want to trigger it yourself.
|
||||
// Parse all available CSS and attach ResizeSensor to those elements which have rules attached
|
||||
// (make sure this is called after 'load' event, because CSS files are not ready when domReady is fired.
|
||||
ElementQueries.init();
|
||||
```
|
||||
|
||||
## Issues
|
||||
|
||||
- So far does not work on `img` and other elements that can't contain other elements. Wrapping with a `div` works fine though (See demo).
|
||||
- Adds additional hidden elements into selected target element and forces target element to be relative or absolute.
|
||||
- Local stylesheets do not work (using `file://` protocol).
|
||||
- If you have rules on an element that has a css animation, also add `element-queries`. E.g. `.widget-name { animation: 2sec my-animation, 1s element-queries;}`. We use this to detect new added DOM elements automatically.
|
||||
|
||||
## License
|
||||
|
||||
MIT license. Copyright [Marc J. Schmidt](https://twitter.com/MarcJSchmidt).
|
@ -0,0 +1,2 @@
|
||||
export { ResizeSensor, ResizeSensorCallback } from "./src/ResizeSensor";
|
||||
export { ElementQueries } from './src/ElementQueries';
|
@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
ResizeSensor: require('./src/ResizeSensor'),
|
||||
ElementQueries: require('./src/ElementQueries')
|
||||
};
|
@ -0,0 +1,55 @@
|
||||
{
|
||||
"_from": "css-element-queries",
|
||||
"_id": "css-element-queries@1.2.3",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-QK9uovYmKTsV2GXWQiMOByVNrLn2qz6m3P7vWpOR4IdD6I3iXoDw5qtgJEN3Xq7gIbdHVKvzHjdAtcl+4Arc4Q==",
|
||||
"_location": "/css-element-queries",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "tag",
|
||||
"registry": true,
|
||||
"raw": "css-element-queries",
|
||||
"name": "css-element-queries",
|
||||
"escapedName": "css-element-queries",
|
||||
"rawSpec": "",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "latest"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"#USER",
|
||||
"/"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/css-element-queries/-/css-element-queries-1.2.3.tgz",
|
||||
"_shasum": "e14940b1fcd4bf0da60ea4145d05742d7172e516",
|
||||
"_spec": "css-element-queries",
|
||||
"_where": "C:\\Users\\a5640\\OneDrive - National ChengChi University\\programming\\Python\\flask\\sententree\\static",
|
||||
"author": {
|
||||
"name": "Marc J. Schmidt"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/marcj/css-element-queries/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"deprecated": false,
|
||||
"description": "CSS-Element-Queries Polyfill. Proof-of-concept for high-speed element dimension/media queries in valid css.",
|
||||
"devDependencies": {
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-bump": "^0.3.1"
|
||||
},
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"homepage": "https://github.com/marcj/css-element-queries",
|
||||
"license": "MIT",
|
||||
"main": "index.js",
|
||||
"name": "css-element-queries",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/marcj/css-element-queries.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"typings": "css-element-queries.d.ts",
|
||||
"version": "1.2.3"
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
export declare class ElementQueries {
|
||||
/**
|
||||
* Attaches to DOMLoadContent
|
||||
*/
|
||||
static listen(): void;
|
||||
|
||||
/**
|
||||
* Parses all available CSS and attach ResizeSensor to those elements which have rules attached.
|
||||
* Make sure this is called after 'load' event, because CSS files are not ready when domReady is fired.
|
||||
*/
|
||||
static init(): void;
|
||||
}
|
||||
|
||||
export default ElementQueries;
|
@ -0,0 +1,530 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Copyright Marc J. Schmidt. See the LICENSE file at the top-level
|
||||
* directory of this distribution and at
|
||||
* https://github.com/marcj/css-element-queries/blob/master/LICENSE.
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof define === "function" && define.amd) {
|
||||
define(['./ResizeSensor.js'], factory);
|
||||
} else if (typeof exports === "object") {
|
||||
module.exports = factory(require('./ResizeSensor.js'));
|
||||
} else {
|
||||
root.ElementQueries = factory(root.ResizeSensor);
|
||||
root.ElementQueries.listen();
|
||||
}
|
||||
}(typeof window !== 'undefined' ? window : this, function (ResizeSensor) {
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Function}
|
||||
* @constructor
|
||||
*/
|
||||
var ElementQueries = function () {
|
||||
//<style> element with our dynamically created styles
|
||||
var cssStyleElement;
|
||||
|
||||
//all rules found for element queries
|
||||
var allQueries = {};
|
||||
|
||||
//association map to identify which selector belongs to a element from the animationstart event.
|
||||
var idToSelectorMapping = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param element
|
||||
* @returns {Number}
|
||||
*/
|
||||
function getEmSize(element) {
|
||||
if (!element) {
|
||||
element = document.documentElement;
|
||||
}
|
||||
var fontSize = window.getComputedStyle(element, null).fontSize;
|
||||
return parseFloat(fontSize) || 16;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get element size
|
||||
* @param {HTMLElement} element
|
||||
* @returns {Object} {width, height}
|
||||
*/
|
||||
function getElementSize(element) {
|
||||
if (!element.getBoundingClientRect) {
|
||||
return {
|
||||
width: element.offsetWidth,
|
||||
height: element.offsetHeight
|
||||
}
|
||||
}
|
||||
|
||||
var rect = element.getBoundingClientRect();
|
||||
return {
|
||||
width: Math.round(rect.width),
|
||||
height: Math.round(rect.height)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @copyright https://github.com/Mr0grog/element-query/blob/master/LICENSE
|
||||
*
|
||||
* @param {HTMLElement} element
|
||||
* @param {*} value
|
||||
* @returns {*}
|
||||
*/
|
||||
function convertToPx(element, value) {
|
||||
var numbers = value.split(/\d/);
|
||||
var units = numbers[numbers.length - 1];
|
||||
value = parseFloat(value);
|
||||
switch (units) {
|
||||
case "px":
|
||||
return value;
|
||||
case "em":
|
||||
return value * getEmSize(element);
|
||||
case "rem":
|
||||
return value * getEmSize();
|
||||
// Viewport units!
|
||||
// According to http://quirksmode.org/mobile/tableViewport.html
|
||||
// documentElement.clientWidth/Height gets us the most reliable info
|
||||
case "vw":
|
||||
return value * document.documentElement.clientWidth / 100;
|
||||
case "vh":
|
||||
return value * document.documentElement.clientHeight / 100;
|
||||
case "vmin":
|
||||
case "vmax":
|
||||
var vw = document.documentElement.clientWidth / 100;
|
||||
var vh = document.documentElement.clientHeight / 100;
|
||||
var chooser = Math[units === "vmin" ? "min" : "max"];
|
||||
return value * chooser(vw, vh);
|
||||
default:
|
||||
return value;
|
||||
// for now, not supporting physical units (since they are just a set number of px)
|
||||
// or ex/ch (getting accurate measurements is hard)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {HTMLElement} element
|
||||
* @param {String} id
|
||||
* @constructor
|
||||
*/
|
||||
function SetupInformation(element, id) {
|
||||
this.element = element;
|
||||
var key, option, elementSize, value, actualValue, attrValues, attrValue, attrName;
|
||||
|
||||
var attributes = ['min-width', 'min-height', 'max-width', 'max-height'];
|
||||
|
||||
/**
|
||||
* Extracts the computed width/height and sets to min/max- attribute.
|
||||
*/
|
||||
this.call = function () {
|
||||
// extract current dimensions
|
||||
elementSize = getElementSize(this.element);
|
||||
|
||||
attrValues = {};
|
||||
|
||||
for (key in allQueries[id]) {
|
||||
if (!allQueries[id].hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
option = allQueries[id][key];
|
||||
|
||||
value = convertToPx(this.element, option.value);
|
||||
|
||||
actualValue = option.property === 'width' ? elementSize.width : elementSize.height;
|
||||
attrName = option.mode + '-' + option.property;
|
||||
attrValue = '';
|
||||
|
||||
if (option.mode === 'min' && actualValue >= value) {
|
||||
attrValue += option.value;
|
||||
}
|
||||
|
||||
if (option.mode === 'max' && actualValue <= value) {
|
||||
attrValue += option.value;
|
||||
}
|
||||
|
||||
if (!attrValues[attrName]) attrValues[attrName] = '';
|
||||
if (attrValue && -1 === (' ' + attrValues[attrName] + ' ').indexOf(' ' + attrValue + ' ')) {
|
||||
attrValues[attrName] += ' ' + attrValue;
|
||||
}
|
||||
}
|
||||
|
||||
for (var k in attributes) {
|
||||
if (!attributes.hasOwnProperty(k)) continue;
|
||||
|
||||
if (attrValues[attributes[k]]) {
|
||||
this.element.setAttribute(attributes[k], attrValues[attributes[k]].substr(1));
|
||||
} else {
|
||||
this.element.removeAttribute(attributes[k]);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} element
|
||||
* @param {Object} id
|
||||
*/
|
||||
function setupElement(element, id) {
|
||||
if (!element.elementQueriesSetupInformation) {
|
||||
element.elementQueriesSetupInformation = new SetupInformation(element, id);
|
||||
}
|
||||
|
||||
if (!element.elementQueriesSensor) {
|
||||
element.elementQueriesSensor = new ResizeSensor(element, function () {
|
||||
element.elementQueriesSetupInformation.call();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores rules to the selector that should be applied once resized.
|
||||
*
|
||||
* @param {String} selector
|
||||
* @param {String} mode min|max
|
||||
* @param {String} property width|height
|
||||
* @param {String} value
|
||||
*/
|
||||
function queueQuery(selector, mode, property, value) {
|
||||
if (typeof(allQueries[selector]) === 'undefined') {
|
||||
allQueries[selector] = [];
|
||||
// add animation to trigger animationstart event, so we know exactly when a element appears in the DOM
|
||||
|
||||
var id = idToSelectorMapping.length;
|
||||
cssStyleElement.innerHTML += '\n' + selector + ' {animation: 0.1s element-queries;}';
|
||||
cssStyleElement.innerHTML += '\n' + selector + ' > .resize-sensor {min-width: '+id+'px;}';
|
||||
idToSelectorMapping.push(selector);
|
||||
}
|
||||
|
||||
allQueries[selector].push({
|
||||
mode: mode,
|
||||
property: property,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
|
||||
function getQuery(container) {
|
||||
var query;
|
||||
if (document.querySelectorAll) query = (container) ? container.querySelectorAll.bind(container) : document.querySelectorAll.bind(document);
|
||||
if (!query && 'undefined' !== typeof $$) query = $$;
|
||||
if (!query && 'undefined' !== typeof jQuery) query = jQuery;
|
||||
|
||||
if (!query) {
|
||||
throw 'No document.querySelectorAll, jQuery or Mootools\'s $$ found.';
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* If animationStart didn't catch a new element in the DOM, we can manually search for it
|
||||
*/
|
||||
function findElementQueriesElements(container) {
|
||||
var query = getQuery(container);
|
||||
|
||||
for (var selector in allQueries) if (allQueries.hasOwnProperty(selector)) {
|
||||
// find all elements based on the extract query selector from the element query rule
|
||||
var elements = query(selector, container);
|
||||
|
||||
for (var i = 0, j = elements.length; i < j; i++) {
|
||||
setupElement(elements[i], selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {HTMLElement} element
|
||||
*/
|
||||
function attachResponsiveImage(element) {
|
||||
var children = [];
|
||||
var rules = [];
|
||||
var sources = [];
|
||||
var defaultImageId = 0;
|
||||
var lastActiveImage = -1;
|
||||
var loadedImages = [];
|
||||
|
||||
for (var i in element.children) {
|
||||
if (!element.children.hasOwnProperty(i)) continue;
|
||||
|
||||
if (element.children[i].tagName && element.children[i].tagName.toLowerCase() === 'img') {
|
||||
children.push(element.children[i]);
|
||||
|
||||
var minWidth = element.children[i].getAttribute('min-width') || element.children[i].getAttribute('data-min-width');
|
||||
//var minHeight = element.children[i].getAttribute('min-height') || element.children[i].getAttribute('data-min-height');
|
||||
var src = element.children[i].getAttribute('data-src') || element.children[i].getAttribute('url');
|
||||
|
||||
sources.push(src);
|
||||
|
||||
var rule = {
|
||||
minWidth: minWidth
|
||||
};
|
||||
|
||||
rules.push(rule);
|
||||
|
||||
if (!minWidth) {
|
||||
defaultImageId = children.length - 1;
|
||||
element.children[i].style.display = 'block';
|
||||
} else {
|
||||
element.children[i].style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastActiveImage = defaultImageId;
|
||||
|
||||
function check() {
|
||||
var imageToDisplay = false, i;
|
||||
|
||||
for (i in children) {
|
||||
if (!children.hasOwnProperty(i)) continue;
|
||||
|
||||
if (rules[i].minWidth) {
|
||||
if (element.offsetWidth > rules[i].minWidth) {
|
||||
imageToDisplay = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!imageToDisplay) {
|
||||
//no rule matched, show default
|
||||
imageToDisplay = defaultImageId;
|
||||
}
|
||||
|
||||
if (lastActiveImage !== imageToDisplay) {
|
||||
//image change
|
||||
|
||||
if (!loadedImages[imageToDisplay]) {
|
||||
//image has not been loaded yet, we need to load the image first in memory to prevent flash of
|
||||
//no content
|
||||
|
||||
var image = new Image();
|
||||
image.onload = function () {
|
||||
children[imageToDisplay].src = sources[imageToDisplay];
|
||||
|
||||
children[lastActiveImage].style.display = 'none';
|
||||
children[imageToDisplay].style.display = 'block';
|
||||
|
||||
loadedImages[imageToDisplay] = true;
|
||||
|
||||
lastActiveImage = imageToDisplay;
|
||||
};
|
||||
|
||||
image.src = sources[imageToDisplay];
|
||||
} else {
|
||||
children[lastActiveImage].style.display = 'none';
|
||||
children[imageToDisplay].style.display = 'block';
|
||||
lastActiveImage = imageToDisplay;
|
||||
}
|
||||
} else {
|
||||
//make sure for initial check call the .src is set correctly
|
||||
children[imageToDisplay].src = sources[imageToDisplay];
|
||||
}
|
||||
}
|
||||
|
||||
element.resizeSensorInstance = new ResizeSensor(element, check);
|
||||
check();
|
||||
}
|
||||
|
||||
function findResponsiveImages() {
|
||||
var query = getQuery();
|
||||
|
||||
var elements = query('[data-responsive-image],[responsive-image]');
|
||||
for (var i = 0, j = elements.length; i < j; i++) {
|
||||
attachResponsiveImage(elements[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var regex = /,?[\s\t]*([^,\n]*?)((?:\[[\s\t]*?(?:min|max)-(?:width|height)[\s\t]*?[~$\^]?=[\s\t]*?"[^"]*?"[\s\t]*?])+)([^,\n\s\{]*)/mgi;
|
||||
var attrRegex = /\[[\s\t]*?(min|max)-(width|height)[\s\t]*?[~$\^]?=[\s\t]*?"([^"]*?)"[\s\t]*?]/mgi;
|
||||
|
||||
/**
|
||||
* @param {String} css
|
||||
*/
|
||||
function extractQuery(css) {
|
||||
var match, smatch, attrs, attrMatch;
|
||||
|
||||
css = css.replace(/'/g, '"');
|
||||
while (null !== (match = regex.exec(css))) {
|
||||
smatch = match[1] + match[3];
|
||||
attrs = match[2];
|
||||
|
||||
while (null !== (attrMatch = attrRegex.exec(attrs))) {
|
||||
queueQuery(smatch, attrMatch[1], attrMatch[2], attrMatch[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CssRule[]|String} rules
|
||||
*/
|
||||
function readRules(rules) {
|
||||
var selector = '';
|
||||
|
||||
if (!rules) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ('string' === typeof rules) {
|
||||
rules = rules.toLowerCase();
|
||||
if (-1 !== rules.indexOf('min-width') || -1 !== rules.indexOf('max-width')) {
|
||||
extractQuery(rules);
|
||||
}
|
||||
} else {
|
||||
for (var i = 0, j = rules.length; i < j; i++) {
|
||||
if (1 === rules[i].type) {
|
||||
selector = rules[i].selectorText || rules[i].cssText;
|
||||
if (-1 !== selector.indexOf('min-height') || -1 !== selector.indexOf('max-height')) {
|
||||
extractQuery(selector);
|
||||
} else if (-1 !== selector.indexOf('min-width') || -1 !== selector.indexOf('max-width')) {
|
||||
extractQuery(selector);
|
||||
}
|
||||
} else if (4 === rules[i].type) {
|
||||
readRules(rules[i].cssRules || rules[i].rules);
|
||||
} else if (3 === rules[i].type) {
|
||||
if(rules[i].styleSheet.hasOwnProperty("cssRules")) {
|
||||
readRules(rules[i].styleSheet.cssRules);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var defaultCssInjected = false;
|
||||
|
||||
/**
|
||||
* Searches all css rules and setups the event listener to all elements with element query rules..
|
||||
*/
|
||||
this.init = function () {
|
||||
var animationStart = 'animationstart';
|
||||
if (typeof document.documentElement.style['webkitAnimationName'] !== 'undefined') {
|
||||
animationStart = 'webkitAnimationStart';
|
||||
} else if (typeof document.documentElement.style['MozAnimationName'] !== 'undefined') {
|
||||
animationStart = 'mozanimationstart';
|
||||
} else if (typeof document.documentElement.style['OAnimationName'] !== 'undefined') {
|
||||
animationStart = 'oanimationstart';
|
||||
}
|
||||
|
||||
document.body.addEventListener(animationStart, function (e) {
|
||||
var element = e.target;
|
||||
var styles = element && window.getComputedStyle(element, null);
|
||||
var animationName = styles && styles.getPropertyValue('animation-name');
|
||||
var requiresSetup = animationName && (-1 !== animationName.indexOf('element-queries'));
|
||||
|
||||
if (requiresSetup) {
|
||||
element.elementQueriesSensor = new ResizeSensor(element, function () {
|
||||
if (element.elementQueriesSetupInformation) {
|
||||
element.elementQueriesSetupInformation.call();
|
||||
}
|
||||
});
|
||||
|
||||
var sensorStyles = window.getComputedStyle(element.resizeSensor, null);
|
||||
var id = sensorStyles.getPropertyValue('min-width');
|
||||
id = parseInt(id.replace('px', ''));
|
||||
setupElement(e.target, idToSelectorMapping[id]);
|
||||
}
|
||||
});
|
||||
|
||||
if (!defaultCssInjected) {
|
||||
cssStyleElement = document.createElement('style');
|
||||
cssStyleElement.type = 'text/css';
|
||||
cssStyleElement.innerHTML = '[responsive-image] > img, [data-responsive-image] {overflow: hidden; padding: 0; } [responsive-image] > img, [data-responsive-image] > img {width: 100%;}';
|
||||
|
||||
//safari wants at least one rule in keyframes to start working
|
||||
cssStyleElement.innerHTML += '\n@keyframes element-queries { 0% { visibility: inherit; } }';
|
||||
document.getElementsByTagName('head')[0].appendChild(cssStyleElement);
|
||||
defaultCssInjected = true;
|
||||
}
|
||||
|
||||
for (var i = 0, j = document.styleSheets.length; i < j; i++) {
|
||||
try {
|
||||
if (document.styleSheets[i].href && 0 === document.styleSheets[i].href.indexOf('file://')) {
|
||||
console.warn("CssElementQueries: unable to parse local css files, " + document.styleSheets[i].href);
|
||||
}
|
||||
|
||||
readRules(document.styleSheets[i].cssRules || document.styleSheets[i].rules || document.styleSheets[i].cssText);
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
findResponsiveImages();
|
||||
};
|
||||
|
||||
/**
|
||||
* Go through all collected rules (readRules()) and attach the resize-listener.
|
||||
* Not necessary to call it manually, since we detect automatically when new elements
|
||||
* are available in the DOM. However, sometimes handy for dirty DOM modifications.
|
||||
*
|
||||
* @param {HTMLElement} container only elements of the container are considered (document.body if not set)
|
||||
*/
|
||||
this.findElementQueriesElements = function (container) {
|
||||
findElementQueriesElements(container);
|
||||
};
|
||||
|
||||
this.update = function () {
|
||||
this.init();
|
||||
};
|
||||
};
|
||||
|
||||
ElementQueries.update = function () {
|
||||
ElementQueries.instance.update();
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes all sensor and elementquery information from the element.
|
||||
*
|
||||
* @param {HTMLElement} element
|
||||
*/
|
||||
ElementQueries.detach = function (element) {
|
||||
if (element.elementQueriesSetupInformation) {
|
||||
//element queries
|
||||
element.elementQueriesSensor.detach();
|
||||
delete element.elementQueriesSetupInformation;
|
||||
delete element.elementQueriesSensor;
|
||||
|
||||
} else if (element.resizeSensorInstance) {
|
||||
//responsive image
|
||||
|
||||
element.resizeSensorInstance.detach();
|
||||
delete element.resizeSensorInstance;
|
||||
}
|
||||
};
|
||||
|
||||
ElementQueries.init = function () {
|
||||
if (!ElementQueries.instance) {
|
||||
ElementQueries.instance = new ElementQueries();
|
||||
}
|
||||
|
||||
ElementQueries.instance.init();
|
||||
};
|
||||
|
||||
var domLoaded = function (callback) {
|
||||
/* Mozilla, Chrome, Opera */
|
||||
if (document.addEventListener) {
|
||||
document.addEventListener('DOMContentLoaded', callback, false);
|
||||
}
|
||||
/* Safari, iCab, Konqueror */
|
||||
else if (/KHTML|WebKit|iCab/i.test(navigator.userAgent)) {
|
||||
var DOMLoadTimer = setInterval(function () {
|
||||
if (/loaded|complete/i.test(document.readyState)) {
|
||||
callback();
|
||||
clearInterval(DOMLoadTimer);
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
/* Other web browsers */
|
||||
else window.onload = callback;
|
||||
};
|
||||
|
||||
ElementQueries.findElementQueriesElements = function (container) {
|
||||
ElementQueries.instance.findElementQueriesElements(container);
|
||||
};
|
||||
|
||||
ElementQueries.listen = function () {
|
||||
domLoaded(ElementQueries.init);
|
||||
};
|
||||
|
||||
return ElementQueries;
|
||||
|
||||
}));
|
@ -0,0 +1,33 @@
|
||||
export declare type ResizeSensorCallback = (size: { width: number; height: number; }) => void;
|
||||
|
||||
export declare class ResizeSensor {
|
||||
/**
|
||||
* Creates a new resize sensor on given elements. The provided callback is called max 1 times per requestAnimationFrame and
|
||||
* is called initially.
|
||||
*/
|
||||
constructor(element: Element | Element[], callback: ResizeSensorCallback);
|
||||
|
||||
/**
|
||||
* Removes the resize sensor, and stops listening to resize events.
|
||||
*/
|
||||
detach(callback?: ResizeSensorCallback): void;
|
||||
|
||||
/**
|
||||
* Resets the resize sensors, so for the next element resize is correctly detected. This is rare cases necessary
|
||||
* when the resize sensor isn't initialised correctly or is in a broken state due to DOM modifications.
|
||||
*/
|
||||
reset(): void;
|
||||
|
||||
/**
|
||||
* Removes the resize sensor, and stops listening to resize events.
|
||||
*/
|
||||
static detach(element: Element | Element[], callback?: ResizeSensorCallback): void;
|
||||
|
||||
/**
|
||||
* Resets the resize sensors, so for the next element resize is correctly detected. This is rare cases necessary
|
||||
* when the resize sensor isn't initialised correctly or is in a broken state due to DOM modifications.
|
||||
*/
|
||||
static reset(element: Element | Element[]): void;
|
||||
}
|
||||
|
||||
export default ResizeSensor;
|
@ -0,0 +1,367 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Copyright Marc J. Schmidt. See the LICENSE file at the top-level
|
||||
* directory of this distribution and at
|
||||
* https://github.com/marcj/css-element-queries/blob/master/LICENSE.
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof define === "function" && define.amd) {
|
||||
define(factory);
|
||||
} else if (typeof exports === "object") {
|
||||
module.exports = factory();
|
||||
} else {
|
||||
root.ResizeSensor = factory();
|
||||
}
|
||||
}(typeof window !== 'undefined' ? window : this, function () {
|
||||
|
||||
// Make sure it does not throw in a SSR (Server Side Rendering) situation
|
||||
if (typeof window === "undefined") {
|
||||
return null;
|
||||
}
|
||||
// https://github.com/Semantic-Org/Semantic-UI/issues/3855
|
||||
// https://github.com/marcj/css-element-queries/issues/257
|
||||
var globalWindow = typeof window != 'undefined' && window.Math == Math
|
||||
? window
|
||||
: typeof self != 'undefined' && self.Math == Math
|
||||
? self
|
||||
: Function('return this')();
|
||||
// Only used for the dirty checking, so the event callback count is limited to max 1 call per fps per sensor.
|
||||
// In combination with the event based resize sensor this saves cpu time, because the sensor is too fast and
|
||||
// would generate too many unnecessary events.
|
||||
var requestAnimationFrame = globalWindow.requestAnimationFrame ||
|
||||
globalWindow.mozRequestAnimationFrame ||
|
||||
globalWindow.webkitRequestAnimationFrame ||
|
||||
function (fn) {
|
||||
return globalWindow.setTimeout(fn, 20);
|
||||
};
|
||||
|
||||
var cancelAnimationFrame = globalWindow.cancelAnimationFrame ||
|
||||
globalWindow.mozCancelAnimationFrame ||
|
||||
globalWindow.webkitCancelAnimationFrame ||
|
||||
function (timer) {
|
||||
globalWindow.clearTimeout(timer);
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterate over each of the provided element(s).
|
||||
*
|
||||
* @param {HTMLElement|HTMLElement[]} elements
|
||||
* @param {Function} callback
|
||||
*/
|
||||
function forEachElement(elements, callback){
|
||||
var elementsType = Object.prototype.toString.call(elements);
|
||||
var isCollectionTyped = ('[object Array]' === elementsType
|
||||
|| ('[object NodeList]' === elementsType)
|
||||
|| ('[object HTMLCollection]' === elementsType)
|
||||
|| ('[object Object]' === elementsType)
|
||||
|| ('undefined' !== typeof jQuery && elements instanceof jQuery) //jquery
|
||||
|| ('undefined' !== typeof Elements && elements instanceof Elements) //mootools
|
||||
);
|
||||
var i = 0, j = elements.length;
|
||||
if (isCollectionTyped) {
|
||||
for (; i < j; i++) {
|
||||
callback(elements[i]);
|
||||
}
|
||||
} else {
|
||||
callback(elements);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get element size
|
||||
* @param {HTMLElement} element
|
||||
* @returns {Object} {width, height}
|
||||
*/
|
||||
function getElementSize(element) {
|
||||
if (!element.getBoundingClientRect) {
|
||||
return {
|
||||
width: element.offsetWidth,
|
||||
height: element.offsetHeight
|
||||
}
|
||||
}
|
||||
|
||||
var rect = element.getBoundingClientRect();
|
||||
return {
|
||||
width: Math.round(rect.width),
|
||||
height: Math.round(rect.height)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply CSS styles to element.
|
||||
*
|
||||
* @param {HTMLElement} element
|
||||
* @param {Object} style
|
||||
*/
|
||||
function setStyle(element, style) {
|
||||
Object.keys(style).forEach(function(key) {
|
||||
element.style[key] = style[key];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for dimension change detection.
|
||||
*
|
||||
* @param {Element|Element[]|Elements|jQuery} element
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var ResizeSensor = function(element, callback) {
|
||||
//Is used when checking in reset() only for invisible elements
|
||||
var lastAnimationFrameForInvisibleCheck = 0;
|
||||
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function EventQueue() {
|
||||
var q = [];
|
||||
this.add = function(ev) {
|
||||
q.push(ev);
|
||||
};
|
||||
|
||||
var i, j;
|
||||
this.call = function(sizeInfo) {
|
||||
for (i = 0, j = q.length; i < j; i++) {
|
||||
q[i].call(this, sizeInfo);
|
||||
}
|
||||
};
|
||||
|
||||
this.remove = function(ev) {
|
||||
var newQueue = [];
|
||||
for(i = 0, j = q.length; i < j; i++) {
|
||||
if(q[i] !== ev) newQueue.push(q[i]);
|
||||
}
|
||||
q = newQueue;
|
||||
};
|
||||
|
||||
this.length = function() {
|
||||
return q.length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {HTMLElement} element
|
||||
* @param {Function} resized
|
||||
*/
|
||||
function attachResizeEvent(element, resized) {
|
||||
if (!element) return;
|
||||
if (element.resizedAttached) {
|
||||
element.resizedAttached.add(resized);
|
||||
return;
|
||||
}
|
||||
|
||||
element.resizedAttached = new EventQueue();
|
||||
element.resizedAttached.add(resized);
|
||||
|
||||
element.resizeSensor = document.createElement('div');
|
||||
element.resizeSensor.dir = 'ltr';
|
||||
element.resizeSensor.className = 'resize-sensor';
|
||||
|
||||
var style = {
|
||||
pointerEvents: 'none',
|
||||
position: 'absolute',
|
||||
left: '0px',
|
||||
top: '0px',
|
||||
right: '0px',
|
||||
bottom: '0px',
|
||||
overflow: 'hidden',
|
||||
zIndex: '-1',
|
||||
visibility: 'hidden',
|
||||
maxWidth: '100%'
|
||||
};
|
||||
var styleChild = {
|
||||
position: 'absolute',
|
||||
left: '0px',
|
||||
top: '0px',
|
||||
transition: '0s',
|
||||
};
|
||||
|
||||
setStyle(element.resizeSensor, style);
|
||||
|
||||
var expand = document.createElement('div');
|
||||
expand.className = 'resize-sensor-expand';
|
||||
setStyle(expand, style);
|
||||
|
||||
var expandChild = document.createElement('div');
|
||||
setStyle(expandChild, styleChild);
|
||||
expand.appendChild(expandChild);
|
||||
|
||||
var shrink = document.createElement('div');
|
||||
shrink.className = 'resize-sensor-shrink';
|
||||
setStyle(shrink, style);
|
||||
|
||||
var shrinkChild = document.createElement('div');
|
||||
setStyle(shrinkChild, styleChild);
|
||||
setStyle(shrinkChild, { width: '200%', height: '200%' });
|
||||
shrink.appendChild(shrinkChild);
|
||||
|
||||
element.resizeSensor.appendChild(expand);
|
||||
element.resizeSensor.appendChild(shrink);
|
||||
element.appendChild(element.resizeSensor);
|
||||
|
||||
var computedStyle = window.getComputedStyle(element);
|
||||
var position = computedStyle ? computedStyle.getPropertyValue('position') : null;
|
||||
if ('absolute' !== position && 'relative' !== position && 'fixed' !== position && 'sticky' !== position) {
|
||||
element.style.position = 'relative';
|
||||
}
|
||||
|
||||
var dirty = false;
|
||||
|
||||
//last request animation frame id used in onscroll event
|
||||
var rafId = 0;
|
||||
var size = getElementSize(element);
|
||||
var lastWidth = 0;
|
||||
var lastHeight = 0;
|
||||
var initialHiddenCheck = true;
|
||||
lastAnimationFrameForInvisibleCheck = 0;
|
||||
|
||||
var resetExpandShrink = function () {
|
||||
var width = element.offsetWidth;
|
||||
var height = element.offsetHeight;
|
||||
|
||||
expandChild.style.width = (width + 10) + 'px';
|
||||
expandChild.style.height = (height + 10) + 'px';
|
||||
|
||||
expand.scrollLeft = width + 10;
|
||||
expand.scrollTop = height + 10;
|
||||
|
||||
shrink.scrollLeft = width + 10;
|
||||
shrink.scrollTop = height + 10;
|
||||
};
|
||||
|
||||
var reset = function() {
|
||||
// Check if element is hidden
|
||||
if (initialHiddenCheck) {
|
||||
var invisible = element.offsetWidth === 0 && element.offsetHeight === 0;
|
||||
if (invisible) {
|
||||
// Check in next frame
|
||||
if (!lastAnimationFrameForInvisibleCheck){
|
||||
lastAnimationFrameForInvisibleCheck = requestAnimationFrame(function(){
|
||||
lastAnimationFrameForInvisibleCheck = 0;
|
||||
reset();
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
} else {
|
||||
// Stop checking
|
||||
initialHiddenCheck = false;
|
||||
}
|
||||
}
|
||||
|
||||
resetExpandShrink();
|
||||
};
|
||||
element.resizeSensor.resetSensor = reset;
|
||||
|
||||
var onResized = function() {
|
||||
rafId = 0;
|
||||
|
||||
if (!dirty) return;
|
||||
|
||||
lastWidth = size.width;
|
||||
lastHeight = size.height;
|
||||
|
||||
if (element.resizedAttached) {
|
||||
element.resizedAttached.call(size);
|
||||
}
|
||||
};
|
||||
|
||||
var onScroll = function() {
|
||||
size = getElementSize(element);
|
||||
dirty = size.width !== lastWidth || size.height !== lastHeight;
|
||||
|
||||
if (dirty && !rafId) {
|
||||
rafId = requestAnimationFrame(onResized);
|
||||
}
|
||||
|
||||
reset();
|
||||
};
|
||||
|
||||
var addEvent = function(el, name, cb) {
|
||||
if (el.attachEvent) {
|
||||
el.attachEvent('on' + name, cb);
|
||||
} else {
|
||||
el.addEventListener(name, cb);
|
||||
}
|
||||
};
|
||||
|
||||
addEvent(expand, 'scroll', onScroll);
|
||||
addEvent(shrink, 'scroll', onScroll);
|
||||
|
||||
// Fix for custom Elements and invisible elements
|
||||
lastAnimationFrameForInvisibleCheck = requestAnimationFrame(function(){
|
||||
lastAnimationFrameForInvisibleCheck = 0;
|
||||
reset();
|
||||
});
|
||||
}
|
||||
|
||||
forEachElement(element, function(elem){
|
||||
attachResizeEvent(elem, callback);
|
||||
});
|
||||
|
||||
this.detach = function(ev) {
|
||||
// clean up the unfinished animation frame to prevent a potential endless requestAnimationFrame of reset
|
||||
if (!lastAnimationFrameForInvisibleCheck) {
|
||||
cancelAnimationFrame(lastAnimationFrameForInvisibleCheck);
|
||||
lastAnimationFrameForInvisibleCheck = 0;
|
||||
}
|
||||
ResizeSensor.detach(element, ev);
|
||||
};
|
||||
|
||||
this.reset = function() {
|
||||
element.resizeSensor.resetSensor();
|
||||
};
|
||||
};
|
||||
|
||||
ResizeSensor.reset = function(element) {
|
||||
forEachElement(element, function(elem){
|
||||
elem.resizeSensor.resetSensor();
|
||||
});
|
||||
};
|
||||
|
||||
ResizeSensor.detach = function(element, ev) {
|
||||
forEachElement(element, function(elem){
|
||||
if (!elem) return;
|
||||
if(elem.resizedAttached && typeof ev === "function"){
|
||||
elem.resizedAttached.remove(ev);
|
||||
if(elem.resizedAttached.length()) return;
|
||||
}
|
||||
if (elem.resizeSensor) {
|
||||
if (elem.contains(elem.resizeSensor)) {
|
||||
elem.removeChild(elem.resizeSensor);
|
||||
}
|
||||
delete elem.resizeSensor;
|
||||
delete elem.resizedAttached;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (typeof MutationObserver !== "undefined") {
|
||||
var observer = new MutationObserver(function (mutations) {
|
||||
for (var i in mutations) {
|
||||
if (mutations.hasOwnProperty(i)) {
|
||||
var items = mutations[i].addedNodes;
|
||||
for (var j = 0; j < items.length; j++) {
|
||||
if (items[j].resizeSensor) {
|
||||
ResizeSensor.reset(items[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function (event) {
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return ResizeSensor;
|
||||
|
||||
}));
|
@ -0,0 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"downlevelIteration": true,
|
||||
"lib" : ["dom","es6","dom.iterable","scripthost", "es2015.iterable", "es2015.collection"]
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
parserOptions:
|
||||
sourceType: module
|
||||
|
||||
env:
|
||||
es6: true
|
||||
browser: true
|
||||
|
||||
extends:
|
||||
"eslint:recommended"
|
||||
|
||||
rules:
|
||||
no-cond-assign: 0
|
@ -0,0 +1,3 @@
|
||||
*.sublime-*
|
||||
build/*.zip
|
||||
test/
|
@ -0,0 +1,27 @@
|
||||
Copyright 2010-2016 Mike Bostock
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the author nor the names of contributors may be used to
|
||||
endorse or promote products derived from this software without specific prior
|
||||
written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -0,0 +1,590 @@
|
||||
// https://d3js.org/d3-array/ Version 1.2.1. Copyright 2017 Mike Bostock.
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
||||
(factory((global.d3 = global.d3 || {})));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
|
||||
var ascending = function(a, b) {
|
||||
return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
|
||||
};
|
||||
|
||||
var bisector = function(compare) {
|
||||
if (compare.length === 1) compare = ascendingComparator(compare);
|
||||
return {
|
||||
left: function(a, x, lo, hi) {
|
||||
if (lo == null) lo = 0;
|
||||
if (hi == null) hi = a.length;
|
||||
while (lo < hi) {
|
||||
var mid = lo + hi >>> 1;
|
||||
if (compare(a[mid], x) < 0) lo = mid + 1;
|
||||
else hi = mid;
|
||||
}
|
||||
return lo;
|
||||
},
|
||||
right: function(a, x, lo, hi) {
|
||||
if (lo == null) lo = 0;
|
||||
if (hi == null) hi = a.length;
|
||||
while (lo < hi) {
|
||||
var mid = lo + hi >>> 1;
|
||||
if (compare(a[mid], x) > 0) hi = mid;
|
||||
else lo = mid + 1;
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
function ascendingComparator(f) {
|
||||
return function(d, x) {
|
||||
return ascending(f(d), x);
|
||||
};
|
||||
}
|
||||
|
||||
var ascendingBisect = bisector(ascending);
|
||||
var bisectRight = ascendingBisect.right;
|
||||
var bisectLeft = ascendingBisect.left;
|
||||
|
||||
var pairs = function(array, f) {
|
||||
if (f == null) f = pair;
|
||||
var i = 0, n = array.length - 1, p = array[0], pairs = new Array(n < 0 ? 0 : n);
|
||||
while (i < n) pairs[i] = f(p, p = array[++i]);
|
||||
return pairs;
|
||||
};
|
||||
|
||||
function pair(a, b) {
|
||||
return [a, b];
|
||||
}
|
||||
|
||||
var cross = function(values0, values1, reduce) {
|
||||
var n0 = values0.length,
|
||||
n1 = values1.length,
|
||||
values = new Array(n0 * n1),
|
||||
i0,
|
||||
i1,
|
||||
i,
|
||||
value0;
|
||||
|
||||
if (reduce == null) reduce = pair;
|
||||
|
||||
for (i0 = i = 0; i0 < n0; ++i0) {
|
||||
for (value0 = values0[i0], i1 = 0; i1 < n1; ++i1, ++i) {
|
||||
values[i] = reduce(value0, values1[i1]);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
};
|
||||
|
||||
var descending = function(a, b) {
|
||||
return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
|
||||
};
|
||||
|
||||
var number = function(x) {
|
||||
return x === null ? NaN : +x;
|
||||
};
|
||||
|
||||
var variance = function(values, valueof) {
|
||||
var n = values.length,
|
||||
m = 0,
|
||||
i = -1,
|
||||
mean = 0,
|
||||
value,
|
||||
delta,
|
||||
sum = 0;
|
||||
|
||||
if (valueof == null) {
|
||||
while (++i < n) {
|
||||
if (!isNaN(value = number(values[i]))) {
|
||||
delta = value - mean;
|
||||
mean += delta / ++m;
|
||||
sum += delta * (value - mean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
while (++i < n) {
|
||||
if (!isNaN(value = number(valueof(values[i], i, values)))) {
|
||||
delta = value - mean;
|
||||
mean += delta / ++m;
|
||||
sum += delta * (value - mean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m > 1) return sum / (m - 1);
|
||||
};
|
||||
|
||||
var deviation = function(array, f) {
|
||||
var v = variance(array, f);
|
||||
return v ? Math.sqrt(v) : v;
|
||||
};
|
||||
|
||||
var extent = function(values, valueof) {
|
||||
var n = values.length,
|
||||
i = -1,
|
||||
value,
|
||||
min,
|
||||
max;
|
||||
|
||||
if (valueof == null) {
|
||||
while (++i < n) { // Find the first comparable value.
|
||||
if ((value = values[i]) != null && value >= value) {
|
||||
min = max = value;
|
||||
while (++i < n) { // Compare the remaining values.
|
||||
if ((value = values[i]) != null) {
|
||||
if (min > value) min = value;
|
||||
if (max < value) max = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
while (++i < n) { // Find the first comparable value.
|
||||
if ((value = valueof(values[i], i, values)) != null && value >= value) {
|
||||
min = max = value;
|
||||
while (++i < n) { // Compare the remaining values.
|
||||
if ((value = valueof(values[i], i, values)) != null) {
|
||||
if (min > value) min = value;
|
||||
if (max < value) max = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [min, max];
|
||||
};
|
||||
|
||||
var array = Array.prototype;
|
||||
|
||||
var slice = array.slice;
|
||||
var map = array.map;
|
||||
|
||||
var constant = function(x) {
|
||||
return function() {
|
||||
return x;
|
||||
};
|
||||
};
|
||||
|
||||
var identity = function(x) {
|
||||
return x;
|
||||
};
|
||||
|
||||
var range = function(start, stop, step) {
|
||||
start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;
|
||||
|
||||
var i = -1,
|
||||
n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
|
||||
range = new Array(n);
|
||||
|
||||
while (++i < n) {
|
||||
range[i] = start + i * step;
|
||||
}
|
||||
|
||||
return range;
|
||||
};
|
||||
|
||||
var e10 = Math.sqrt(50);
|
||||
var e5 = Math.sqrt(10);
|
||||
var e2 = Math.sqrt(2);
|
||||
|
||||
var ticks = function(start, stop, count) {
|
||||
var reverse,
|
||||
i = -1,
|
||||
n,
|
||||
ticks,
|
||||
step;
|
||||
|
||||
stop = +stop, start = +start, count = +count;
|
||||
if (start === stop && count > 0) return [start];
|
||||
if (reverse = stop < start) n = start, start = stop, stop = n;
|
||||
if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
|
||||
|
||||
if (step > 0) {
|
||||
start = Math.ceil(start / step);
|
||||
stop = Math.floor(stop / step);
|
||||
ticks = new Array(n = Math.ceil(stop - start + 1));
|
||||
while (++i < n) ticks[i] = (start + i) * step;
|
||||
} else {
|
||||
start = Math.floor(start * step);
|
||||
stop = Math.ceil(stop * step);
|
||||
ticks = new Array(n = Math.ceil(start - stop + 1));
|
||||
while (++i < n) ticks[i] = (start - i) / step;
|
||||
}
|
||||
|
||||
if (reverse) ticks.reverse();
|
||||
|
||||
return ticks;
|
||||
};
|
||||
|
||||
function tickIncrement(start, stop, count) {
|
||||
var step = (stop - start) / Math.max(0, count),
|
||||
power = Math.floor(Math.log(step) / Math.LN10),
|
||||
error = step / Math.pow(10, power);
|
||||
return power >= 0
|
||||
? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)
|
||||
: -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);
|
||||
}
|
||||
|
||||
function tickStep(start, stop, count) {
|
||||
var step0 = Math.abs(stop - start) / Math.max(0, count),
|
||||
step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
|
||||
error = step0 / step1;
|
||||
if (error >= e10) step1 *= 10;
|
||||
else if (error >= e5) step1 *= 5;
|
||||
else if (error >= e2) step1 *= 2;
|
||||
return stop < start ? -step1 : step1;
|
||||
}
|
||||
|
||||
var sturges = function(values) {
|
||||
return Math.ceil(Math.log(values.length) / Math.LN2) + 1;
|
||||
};
|
||||
|
||||
var histogram = function() {
|
||||
var value = identity,
|
||||
domain = extent,
|
||||
threshold = sturges;
|
||||
|
||||
function histogram(data) {
|
||||
var i,
|
||||
n = data.length,
|
||||
x,
|
||||
values = new Array(n);
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
values[i] = value(data[i], i, data);
|
||||
}
|
||||
|
||||
var xz = domain(values),
|
||||
x0 = xz[0],
|
||||
x1 = xz[1],
|
||||
tz = threshold(values, x0, x1);
|
||||
|
||||
// Convert number of thresholds into uniform thresholds.
|
||||
if (!Array.isArray(tz)) {
|
||||
tz = tickStep(x0, x1, tz);
|
||||
tz = range(Math.ceil(x0 / tz) * tz, Math.floor(x1 / tz) * tz, tz); // exclusive
|
||||
}
|
||||
|
||||
// Remove any thresholds outside the domain.
|
||||
var m = tz.length;
|
||||
while (tz[0] <= x0) tz.shift(), --m;
|
||||
while (tz[m - 1] > x1) tz.pop(), --m;
|
||||
|
||||
var bins = new Array(m + 1),
|
||||
bin;
|
||||
|
||||
// Initialize bins.
|
||||
for (i = 0; i <= m; ++i) {
|
||||
bin = bins[i] = [];
|
||||
bin.x0 = i > 0 ? tz[i - 1] : x0;
|
||||
bin.x1 = i < m ? tz[i] : x1;
|
||||
}
|
||||
|
||||
// Assign data to bins by value, ignoring any outside the domain.
|
||||
for (i = 0; i < n; ++i) {
|
||||
x = values[i];
|
||||
if (x0 <= x && x <= x1) {
|
||||
bins[bisectRight(tz, x, 0, m)].push(data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return bins;
|
||||
}
|
||||
|
||||
histogram.value = function(_) {
|
||||
return arguments.length ? (value = typeof _ === "function" ? _ : constant(_), histogram) : value;
|
||||
};
|
||||
|
||||
histogram.domain = function(_) {
|
||||
return arguments.length ? (domain = typeof _ === "function" ? _ : constant([_[0], _[1]]), histogram) : domain;
|
||||
};
|
||||
|
||||
histogram.thresholds = function(_) {
|
||||
return arguments.length ? (threshold = typeof _ === "function" ? _ : Array.isArray(_) ? constant(slice.call(_)) : constant(_), histogram) : threshold;
|
||||
};
|
||||
|
||||
return histogram;
|
||||
};
|
||||
|
||||
var quantile = function(values, p, valueof) {
|
||||
if (valueof == null) valueof = number;
|
||||
if (!(n = values.length)) return;
|
||||
if ((p = +p) <= 0 || n < 2) return +valueof(values[0], 0, values);
|
||||
if (p >= 1) return +valueof(values[n - 1], n - 1, values);
|
||||
var n,
|
||||
i = (n - 1) * p,
|
||||
i0 = Math.floor(i),
|
||||
value0 = +valueof(values[i0], i0, values),
|
||||
value1 = +valueof(values[i0 + 1], i0 + 1, values);
|
||||
return value0 + (value1 - value0) * (i - i0);
|
||||
};
|
||||
|
||||
var freedmanDiaconis = function(values, min, max) {
|
||||
values = map.call(values, number).sort(ascending);
|
||||
return Math.ceil((max - min) / (2 * (quantile(values, 0.75) - quantile(values, 0.25)) * Math.pow(values.length, -1 / 3)));
|
||||
};
|
||||
|
||||
var scott = function(values, min, max) {
|
||||
return Math.ceil((max - min) / (3.5 * deviation(values) * Math.pow(values.length, -1 / 3)));
|
||||
};
|
||||
|
||||
var max = function(values, valueof) {
|
||||
var n = values.length,
|
||||
i = -1,
|
||||
value,
|
||||
max;
|
||||
|
||||
if (valueof == null) {
|
||||
while (++i < n) { // Find the first comparable value.
|
||||
if ((value = values[i]) != null && value >= value) {
|
||||
max = value;
|
||||
while (++i < n) { // Compare the remaining values.
|
||||
if ((value = values[i]) != null && value > max) {
|
||||
max = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
while (++i < n) { // Find the first comparable value.
|
||||
if ((value = valueof(values[i], i, values)) != null && value >= value) {
|
||||
max = value;
|
||||
while (++i < n) { // Compare the remaining values.
|
||||
if ((value = valueof(values[i], i, values)) != null && value > max) {
|
||||
max = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return max;
|
||||
};
|
||||
|
||||
var mean = function(values, valueof) {
|
||||
var n = values.length,
|
||||
m = n,
|
||||
i = -1,
|
||||
value,
|
||||
sum = 0;
|
||||
|
||||
if (valueof == null) {
|
||||
while (++i < n) {
|
||||
if (!isNaN(value = number(values[i]))) sum += value;
|
||||
else --m;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
while (++i < n) {
|
||||
if (!isNaN(value = number(valueof(values[i], i, values)))) sum += value;
|
||||
else --m;
|
||||
}
|
||||
}
|
||||
|
||||
if (m) return sum / m;
|
||||
};
|
||||
|
||||
var median = function(values, valueof) {
|
||||
var n = values.length,
|
||||
i = -1,
|
||||
value,
|
||||
numbers = [];
|
||||
|
||||
if (valueof == null) {
|
||||
while (++i < n) {
|
||||
if (!isNaN(value = number(values[i]))) {
|
||||
numbers.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
while (++i < n) {
|
||||
if (!isNaN(value = number(valueof(values[i], i, values)))) {
|
||||
numbers.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return quantile(numbers.sort(ascending), 0.5);
|
||||
};
|
||||
|
||||
var merge = function(arrays) {
|
||||
var n = arrays.length,
|
||||
m,
|
||||
i = -1,
|
||||
j = 0,
|
||||
merged,
|
||||
array;
|
||||
|
||||
while (++i < n) j += arrays[i].length;
|
||||
merged = new Array(j);
|
||||
|
||||
while (--n >= 0) {
|
||||
array = arrays[n];
|
||||
m = array.length;
|
||||
while (--m >= 0) {
|
||||
merged[--j] = array[m];
|
||||
}
|
||||
}
|
||||
|
||||
return merged;
|
||||
};
|
||||
|
||||
var min = function(values, valueof) {
|
||||
var n = values.length,
|
||||
i = -1,
|
||||
value,
|
||||
min;
|
||||
|
||||
if (valueof == null) {
|
||||
while (++i < n) { // Find the first comparable value.
|
||||
if ((value = values[i]) != null && value >= value) {
|
||||
min = value;
|
||||
while (++i < n) { // Compare the remaining values.
|
||||
if ((value = values[i]) != null && min > value) {
|
||||
min = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
while (++i < n) { // Find the first comparable value.
|
||||
if ((value = valueof(values[i], i, values)) != null && value >= value) {
|
||||
min = value;
|
||||
while (++i < n) { // Compare the remaining values.
|
||||
if ((value = valueof(values[i], i, values)) != null && min > value) {
|
||||
min = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return min;
|
||||
};
|
||||
|
||||
var permute = function(array, indexes) {
|
||||
var i = indexes.length, permutes = new Array(i);
|
||||
while (i--) permutes[i] = array[indexes[i]];
|
||||
return permutes;
|
||||
};
|
||||
|
||||
var scan = function(values, compare) {
|
||||
if (!(n = values.length)) return;
|
||||
var n,
|
||||
i = 0,
|
||||
j = 0,
|
||||
xi,
|
||||
xj = values[j];
|
||||
|
||||
if (compare == null) compare = ascending;
|
||||
|
||||
while (++i < n) {
|
||||
if (compare(xi = values[i], xj) < 0 || compare(xj, xj) !== 0) {
|
||||
xj = xi, j = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (compare(xj, xj) === 0) return j;
|
||||
};
|
||||
|
||||
var shuffle = function(array, i0, i1) {
|
||||
var m = (i1 == null ? array.length : i1) - (i0 = i0 == null ? 0 : +i0),
|
||||
t,
|
||||
i;
|
||||
|
||||
while (m) {
|
||||
i = Math.random() * m-- | 0;
|
||||
t = array[m + i0];
|
||||
array[m + i0] = array[i + i0];
|
||||
array[i + i0] = t;
|
||||
}
|
||||
|
||||
return array;
|
||||
};
|
||||
|
||||
var sum = function(values, valueof) {
|
||||
var n = values.length,
|
||||
i = -1,
|
||||
value,
|
||||
sum = 0;
|
||||
|
||||
if (valueof == null) {
|
||||
while (++i < n) {
|
||||
if (value = +values[i]) sum += value; // Note: zero and null are equivalent.
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
while (++i < n) {
|
||||
if (value = +valueof(values[i], i, values)) sum += value;
|
||||
}
|
||||
}
|
||||
|
||||
return sum;
|
||||
};
|
||||
|
||||
var transpose = function(matrix) {
|
||||
if (!(n = matrix.length)) return [];
|
||||
for (var i = -1, m = min(matrix, length), transpose = new Array(m); ++i < m;) {
|
||||
for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n;) {
|
||||
row[j] = matrix[j][i];
|
||||
}
|
||||
}
|
||||
return transpose;
|
||||
};
|
||||
|
||||
function length(d) {
|
||||
return d.length;
|
||||
}
|
||||
|
||||
var zip = function() {
|
||||
return transpose(arguments);
|
||||
};
|
||||
|
||||
exports.bisect = bisectRight;
|
||||
exports.bisectRight = bisectRight;
|
||||
exports.bisectLeft = bisectLeft;
|
||||
exports.ascending = ascending;
|
||||
exports.bisector = bisector;
|
||||
exports.cross = cross;
|
||||
exports.descending = descending;
|
||||
exports.deviation = deviation;
|
||||
exports.extent = extent;
|
||||
exports.histogram = histogram;
|
||||
exports.thresholdFreedmanDiaconis = freedmanDiaconis;
|
||||
exports.thresholdScott = scott;
|
||||
exports.thresholdSturges = sturges;
|
||||
exports.max = max;
|
||||
exports.mean = mean;
|
||||
exports.median = median;
|
||||
exports.merge = merge;
|
||||
exports.min = min;
|
||||
exports.pairs = pairs;
|
||||
exports.permute = permute;
|
||||
exports.quantile = quantile;
|
||||
exports.range = range;
|
||||
exports.scan = scan;
|
||||
exports.shuffle = shuffle;
|
||||
exports.sum = sum;
|
||||
exports.ticks = ticks;
|
||||
exports.tickIncrement = tickIncrement;
|
||||
exports.tickStep = tickStep;
|
||||
exports.transpose = transpose;
|
||||
exports.variance = variance;
|
||||
exports.zip = zip;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
})));
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 9.9 KiB |
@ -0,0 +1,27 @@
|
||||
export {default as bisect, bisectRight, bisectLeft} from "./src/bisect";
|
||||
export {default as ascending} from "./src/ascending";
|
||||
export {default as bisector} from "./src/bisector";
|
||||
export {default as cross} from "./src/cross";
|
||||
export {default as descending} from "./src/descending";
|
||||
export {default as deviation} from "./src/deviation";
|
||||
export {default as extent} from "./src/extent";
|
||||
export {default as histogram} from "./src/histogram";
|
||||
export {default as thresholdFreedmanDiaconis} from "./src/threshold/freedmanDiaconis";
|
||||
export {default as thresholdScott} from "./src/threshold/scott";
|
||||
export {default as thresholdSturges} from "./src/threshold/sturges";
|
||||
export {default as max} from "./src/max";
|
||||
export {default as mean} from "./src/mean";
|
||||
export {default as median} from "./src/median";
|
||||
export {default as merge} from "./src/merge";
|
||||
export {default as min} from "./src/min";
|
||||
export {default as pairs} from "./src/pairs";
|
||||
export {default as permute} from "./src/permute";
|
||||
export {default as quantile} from "./src/quantile";
|
||||
export {default as range} from "./src/range";
|
||||
export {default as scan} from "./src/scan";
|
||||
export {default as shuffle} from "./src/shuffle";
|
||||
export {default as sum} from "./src/sum";
|
||||
export {default as ticks, tickIncrement, tickStep} from "./src/ticks";
|
||||
export {default as transpose} from "./src/transpose";
|
||||
export {default as variance} from "./src/variance";
|
||||
export {default as zip} from "./src/zip";
|
@ -0,0 +1,74 @@
|
||||
{
|
||||
"_from": "d3-array@1.2.1",
|
||||
"_id": "d3-array@1.2.1",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-CyINJQ0SOUHojDdFDH4JEM0552vCR1utGyLHegJHyYH0JyCpSeTPxi4OBqHMA2jJZq4NH782LtaJWBImqI/HBw==",
|
||||
"_location": "/d3-array",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "d3-array@1.2.1",
|
||||
"name": "d3-array",
|
||||
"escapedName": "d3-array",
|
||||
"rawSpec": "1.2.1",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "1.2.1"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/d3",
|
||||
"/d3-chord",
|
||||
"/d3-geo",
|
||||
"/d3-scale"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz",
|
||||
"_shasum": "d1ca33de2f6ac31efadb8e050a021d7e2396d5dc",
|
||||
"_spec": "d3-array@1.2.1",
|
||||
"_where": "C:\\Users\\a5640\\OneDrive - National ChengChi University\\programming\\Python\\flask\\sententree\\static\\node_modules\\d3",
|
||||
"author": {
|
||||
"name": "Mike Bostock",
|
||||
"url": "http://bost.ocks.org/mike"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/d3/d3-array/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"deprecated": false,
|
||||
"description": "Array manipulation, ordering, searching, summarizing, etc.",
|
||||
"devDependencies": {
|
||||
"eslint": "3",
|
||||
"package-preamble": "0.0",
|
||||
"rollup": "0.41",
|
||||
"seedrandom": "2",
|
||||
"tape": "4",
|
||||
"uglify-js": "^2.8.11"
|
||||
},
|
||||
"homepage": "https://d3js.org/d3-array/",
|
||||
"jsnext:main": "index",
|
||||
"keywords": [
|
||||
"d3",
|
||||
"d3-module",
|
||||
"histogram",
|
||||
"bisect",
|
||||
"shuffle",
|
||||
"statistics",
|
||||
"search",
|
||||
"sort",
|
||||
"array"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"main": "build/d3-array.js",
|
||||
"module": "index",
|
||||
"name": "d3-array",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/d3/d3-array.git"
|
||||
},
|
||||
"scripts": {
|
||||
"postpublish": "git push && git push --tags && cd ../d3.github.com && git pull && cp ../d3-array/build/d3-array.js d3-array.v1.js && cp ../d3-array/build/d3-array.min.js d3-array.v1.min.js && git add d3-array.v1.js d3-array.v1.min.js && git commit -m \"d3-array ${npm_package_version}\" && git push && cd - && zip -j build/d3-array.zip -- LICENSE README.md build/d3-array.js build/d3-array.min.js",
|
||||
"prepublish": "npm run test && uglifyjs --preamble \"$(preamble)\" build/d3-array.js -c -m -o build/d3-array.min.js",
|
||||
"pretest": "rm -rf build && mkdir build && rollup --banner \"$(preamble)\" -f umd -n d3 -o build/d3-array.js -- index.js",
|
||||
"test": "tape 'test/**/*-test.js' && eslint index.js src"
|
||||
},
|
||||
"version": "1.2.1"
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
var array = Array.prototype;
|
||||
|
||||
export var slice = array.slice;
|
||||
export var map = array.map;
|
@ -0,0 +1,3 @@
|
||||
export default function(a, b) {
|
||||
return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import ascending from "./ascending";
|
||||
import bisector from "./bisector";
|
||||
|
||||
var ascendingBisect = bisector(ascending);
|
||||
export var bisectRight = ascendingBisect.right;
|
||||
export var bisectLeft = ascendingBisect.left;
|
||||
export default bisectRight;
|
@ -0,0 +1,33 @@
|
||||
import ascending from "./ascending";
|
||||
|
||||
export default function(compare) {
|
||||
if (compare.length === 1) compare = ascendingComparator(compare);
|
||||
return {
|
||||
left: function(a, x, lo, hi) {
|
||||
if (lo == null) lo = 0;
|
||||
if (hi == null) hi = a.length;
|
||||
while (lo < hi) {
|
||||
var mid = lo + hi >>> 1;
|
||||
if (compare(a[mid], x) < 0) lo = mid + 1;
|
||||
else hi = mid;
|
||||
}
|
||||
return lo;
|
||||
},
|
||||
right: function(a, x, lo, hi) {
|
||||
if (lo == null) lo = 0;
|
||||
if (hi == null) hi = a.length;
|
||||
while (lo < hi) {
|
||||
var mid = lo + hi >>> 1;
|
||||
if (compare(a[mid], x) > 0) hi = mid;
|
||||
else lo = mid + 1;
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function ascendingComparator(f) {
|
||||
return function(d, x) {
|
||||
return ascending(f(d), x);
|
||||
};
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
export default function(x) {
|
||||
return function() {
|
||||
return x;
|
||||
};
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import {pair} from "./pairs";
|
||||
|
||||
export default function(values0, values1, reduce) {
|
||||
var n0 = values0.length,
|
||||
n1 = values1.length,
|
||||
values = new Array(n0 * n1),
|
||||
i0,
|
||||
i1,
|
||||
i,
|
||||
value0;
|
||||
|
||||
if (reduce == null) reduce = pair;
|
||||
|
||||
for (i0 = i = 0; i0 < n0; ++i0) {
|
||||
for (value0 = values0[i0], i1 = 0; i1 < n1; ++i1, ++i) {
|
||||
values[i] = reduce(value0, values1[i1]);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
export default function(a, b) {
|
||||
return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import variance from "./variance";
|
||||
|
||||
export default function(array, f) {
|
||||
var v = variance(array, f);
|
||||
return v ? Math.sqrt(v) : v;
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
export default function(values, valueof) {
|
||||
var n = values.length,
|
||||
i = -1,
|
||||
value,
|
||||
min,
|
||||
max;
|
||||
|
||||
if (valueof == null) {
|
||||
while (++i < n) { // Find the first comparable value.
|
||||
if ((value = values[i]) != null && value >= value) {
|
||||
min = max = value;
|
||||
while (++i < n) { // Compare the remaining values.
|
||||
if ((value = values[i]) != null) {
|
||||
if (min > value) min = value;
|
||||
if (max < value) max = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
while (++i < n) { // Find the first comparable value.
|
||||
if ((value = valueof(values[i], i, values)) != null && value >= value) {
|
||||
min = max = value;
|
||||
while (++i < n) { // Compare the remaining values.
|
||||
if ((value = valueof(values[i], i, values)) != null) {
|
||||
if (min > value) min = value;
|
||||
if (max < value) max = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [min, max];
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
import {slice} from "./array";
|
||||
import bisect from "./bisect";
|
||||
import constant from "./constant";
|
||||
import extent from "./extent";
|
||||
import identity from "./identity";
|
||||
import range from "./range";
|
||||
import {tickStep} from "./ticks";
|
||||
import sturges from "./threshold/sturges";
|
||||
|
||||
export default function() {
|
||||
var value = identity,
|
||||
domain = extent,
|
||||
threshold = sturges;
|
||||
|
||||
function histogram(data) {
|
||||
var i,
|
||||
n = data.length,
|
||||
x,
|
||||
values = new Array(n);
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
values[i] = value(data[i], i, data);
|
||||
}
|
||||
|
||||
var xz = domain(values),
|
||||
x0 = xz[0],
|
||||
x1 = xz[1],
|
||||
tz = threshold(values, x0, x1);
|
||||
|
||||
// Convert number of thresholds into uniform thresholds.
|
||||
if (!Array.isArray(tz)) {
|
||||
tz = tickStep(x0, x1, tz);
|
||||
tz = range(Math.ceil(x0 / tz) * tz, Math.floor(x1 / tz) * tz, tz); // exclusive
|
||||
}
|
||||
|
||||
// Remove any thresholds outside the domain.
|
||||
var m = tz.length;
|
||||
while (tz[0] <= x0) tz.shift(), --m;
|
||||
while (tz[m - 1] > x1) tz.pop(), --m;
|
||||
|
||||
var bins = new Array(m + 1),
|
||||
bin;
|
||||
|
||||
// Initialize bins.
|
||||
for (i = 0; i <= m; ++i) {
|
||||
bin = bins[i] = [];
|
||||
bin.x0 = i > 0 ? tz[i - 1] : x0;
|
||||
bin.x1 = i < m ? tz[i] : x1;
|
||||
}
|
||||
|
||||
// Assign data to bins by value, ignoring any outside the domain.
|
||||
for (i = 0; i < n; ++i) {
|
||||
x = values[i];
|
||||
if (x0 <= x && x <= x1) {
|
||||
bins[bisect(tz, x, 0, m)].push(data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return bins;
|
||||
}
|
||||
|
||||
histogram.value = function(_) {
|
||||
return arguments.length ? (value = typeof _ === "function" ? _ : constant(_), histogram) : value;
|
||||
};
|
||||
|
||||
histogram.domain = function(_) {
|
||||
return arguments.length ? (domain = typeof _ === "function" ? _ : constant([_[0], _[1]]), histogram) : domain;
|
||||
};
|
||||
|
||||
histogram.thresholds = function(_) {
|
||||
return arguments.length ? (threshold = typeof _ === "function" ? _ : Array.isArray(_) ? constant(slice.call(_)) : constant(_), histogram) : threshold;
|
||||
};
|
||||
|
||||
return histogram;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
export default function(x) {
|
||||
return x;
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
export default function(values, valueof) {
|
||||
var n = values.length,
|
||||
i = -1,
|
||||
value,
|
||||
max;
|
||||
|
||||
if (valueof == null) {
|
||||
while (++i < n) { // Find the first comparable value.
|
||||
if ((value = values[i]) != null && value >= value) {
|
||||
max = value;
|
||||
while (++i < n) { // Compare the remaining values.
|
||||
if ((value = values[i]) != null && value > max) {
|
||||
max = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
while (++i < n) { // Find the first comparable value.
|
||||
if ((value = valueof(values[i], i, values)) != null && value >= value) {
|
||||
max = value;
|
||||
while (++i < n) { // Compare the remaining values.
|
||||
if ((value = valueof(values[i], i, values)) != null && value > max) {
|
||||
max = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import number from "./number";
|
||||
|
||||
export default function(values, valueof) {
|
||||
var n = values.length,
|
||||
m = n,
|
||||
i = -1,
|
||||
value,
|
||||
sum = 0;
|
||||
|
||||
if (valueof == null) {
|
||||
while (++i < n) {
|
||||
if (!isNaN(value = number(values[i]))) sum += value;
|
||||
else --m;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
while (++i < n) {
|
||||
if (!isNaN(value = number(valueof(values[i], i, values)))) sum += value;
|
||||
else --m;
|
||||
}
|
||||
}
|
||||
|
||||
if (m) return sum / m;
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
import ascending from "./ascending";
|
||||
import number from "./number";
|
||||
import quantile from "./quantile";
|
||||
|
||||
export default function(values, valueof) {
|
||||
var n = values.length,
|
||||
i = -1,
|
||||
value,
|
||||
numbers = [];
|
||||
|
||||
if (valueof == null) {
|
||||
while (++i < n) {
|
||||
if (!isNaN(value = number(values[i]))) {
|
||||
numbers.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
while (++i < n) {
|
||||
if (!isNaN(value = number(valueof(values[i], i, values)))) {
|
||||
numbers.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return quantile(numbers.sort(ascending), 0.5);
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
export default function(arrays) {
|
||||
var n = arrays.length,
|
||||
m,
|
||||
i = -1,
|
||||
j = 0,
|
||||
merged,
|
||||
array;
|
||||
|
||||
while (++i < n) j += arrays[i].length;
|
||||
merged = new Array(j);
|
||||
|
||||
while (--n >= 0) {
|
||||
array = arrays[n];
|
||||
m = array.length;
|
||||
while (--m >= 0) {
|
||||
merged[--j] = array[m];
|
||||
}
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
export default function(values, valueof) {
|
||||
var n = values.length,
|
||||
i = -1,
|
||||
value,
|
||||
min;
|
||||
|
||||
if (valueof == null) {
|
||||
while (++i < n) { // Find the first comparable value.
|
||||
if ((value = values[i]) != null && value >= value) {
|
||||
min = value;
|
||||
while (++i < n) { // Compare the remaining values.
|
||||
if ((value = values[i]) != null && min > value) {
|
||||
min = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
while (++i < n) { // Find the first comparable value.
|
||||
if ((value = valueof(values[i], i, values)) != null && value >= value) {
|
||||
min = value;
|
||||
while (++i < n) { // Compare the remaining values.
|
||||
if ((value = valueof(values[i], i, values)) != null && min > value) {
|
||||
min = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
export default function(x) {
|
||||
return x === null ? NaN : +x;
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
export default function(array, f) {
|
||||
if (f == null) f = pair;
|
||||
var i = 0, n = array.length - 1, p = array[0], pairs = new Array(n < 0 ? 0 : n);
|
||||
while (i < n) pairs[i] = f(p, p = array[++i]);
|
||||
return pairs;
|
||||
}
|
||||
|
||||
export function pair(a, b) {
|
||||
return [a, b];
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
export default function(array, indexes) {
|
||||
var i = indexes.length, permutes = new Array(i);
|
||||
while (i--) permutes[i] = array[indexes[i]];
|
||||
return permutes;
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
import number from "./number";
|
||||
|
||||
export default function(values, p, valueof) {
|
||||
if (valueof == null) valueof = number;
|
||||
if (!(n = values.length)) return;
|
||||
if ((p = +p) <= 0 || n < 2) return +valueof(values[0], 0, values);
|
||||
if (p >= 1) return +valueof(values[n - 1], n - 1, values);
|
||||
var n,
|
||||
i = (n - 1) * p,
|
||||
i0 = Math.floor(i),
|
||||
value0 = +valueof(values[i0], i0, values),
|
||||
value1 = +valueof(values[i0 + 1], i0 + 1, values);
|
||||
return value0 + (value1 - value0) * (i - i0);
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
export default function(start, stop, step) {
|
||||
start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;
|
||||
|
||||
var i = -1,
|
||||
n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
|
||||
range = new Array(n);
|
||||
|
||||
while (++i < n) {
|
||||
range[i] = start + i * step;
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import ascending from "./ascending";
|
||||
|
||||
export default function(values, compare) {
|
||||
if (!(n = values.length)) return;
|
||||
var n,
|
||||
i = 0,
|
||||
j = 0,
|
||||
xi,
|
||||
xj = values[j];
|
||||
|
||||
if (compare == null) compare = ascending;
|
||||
|
||||
while (++i < n) {
|
||||
if (compare(xi = values[i], xj) < 0 || compare(xj, xj) !== 0) {
|
||||
xj = xi, j = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (compare(xj, xj) === 0) return j;
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
export default function(array, i0, i1) {
|
||||
var m = (i1 == null ? array.length : i1) - (i0 = i0 == null ? 0 : +i0),
|
||||
t,
|
||||
i;
|
||||
|
||||
while (m) {
|
||||
i = Math.random() * m-- | 0;
|
||||
t = array[m + i0];
|
||||
array[m + i0] = array[i + i0];
|
||||
array[i + i0] = t;
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
export default function(values, valueof) {
|
||||
var n = values.length,
|
||||
i = -1,
|
||||
value,
|
||||
sum = 0;
|
||||
|
||||
if (valueof == null) {
|
||||
while (++i < n) {
|
||||
if (value = +values[i]) sum += value; // Note: zero and null are equivalent.
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
while (++i < n) {
|
||||
if (value = +valueof(values[i], i, values)) sum += value;
|
||||
}
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
import {map} from "../array";
|
||||
import ascending from "../ascending";
|
||||
import number from "../number";
|
||||
import quantile from "../quantile";
|
||||
|
||||
export default function(values, min, max) {
|
||||
values = map.call(values, number).sort(ascending);
|
||||
return Math.ceil((max - min) / (2 * (quantile(values, 0.75) - quantile(values, 0.25)) * Math.pow(values.length, -1 / 3)));
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
import deviation from "../deviation";
|
||||
|
||||
export default function(values, min, max) {
|
||||
return Math.ceil((max - min) / (3.5 * deviation(values) * Math.pow(values.length, -1 / 3)));
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
export default function(values) {
|
||||
return Math.ceil(Math.log(values.length) / Math.LN2) + 1;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
var e10 = Math.sqrt(50),
|
||||
e5 = Math.sqrt(10),
|
||||
e2 = Math.sqrt(2);
|
||||
|
||||
export default function(start, stop, count) {
|
||||
var reverse,
|
||||
i = -1,
|
||||
n,
|
||||
ticks,
|
||||
step;
|
||||
|
||||
stop = +stop, start = +start, count = +count;
|
||||
if (start === stop && count > 0) return [start];
|
||||
if (reverse = stop < start) n = start, start = stop, stop = n;
|
||||
if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
|
||||
|
||||
if (step > 0) {
|
||||
start = Math.ceil(start / step);
|
||||
stop = Math.floor(stop / step);
|
||||
ticks = new Array(n = Math.ceil(stop - start + 1));
|
||||
while (++i < n) ticks[i] = (start + i) * step;
|
||||
} else {
|
||||
start = Math.floor(start * step);
|
||||
stop = Math.ceil(stop * step);
|
||||
ticks = new Array(n = Math.ceil(start - stop + 1));
|
||||
while (++i < n) ticks[i] = (start - i) / step;
|
||||
}
|
||||
|
||||
if (reverse) ticks.reverse();
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
export function tickIncrement(start, stop, count) {
|
||||
var step = (stop - start) / Math.max(0, count),
|
||||
power = Math.floor(Math.log(step) / Math.LN10),
|
||||
error = step / Math.pow(10, power);
|
||||
return power >= 0
|
||||
? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)
|
||||
: -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);
|
||||
}
|
||||
|
||||
export function tickStep(start, stop, count) {
|
||||
var step0 = Math.abs(stop - start) / Math.max(0, count),
|
||||
step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
|
||||
error = step0 / step1;
|
||||
if (error >= e10) step1 *= 10;
|
||||
else if (error >= e5) step1 *= 5;
|
||||
else if (error >= e2) step1 *= 2;
|
||||
return stop < start ? -step1 : step1;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue