1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-13 22:12:50 +02:00

Clean up all files

This commit is contained in:
Laurent Cozic
2017-02-03 21:44:51 +00:00
parent 83f5bc2916
commit 73be4d9527
37 changed files with 46 additions and 1625 deletions

View File

@@ -1,3 +1,11 @@
"C:\Qt\5.7\msvc2015\bin\qmake.exe" D:\Web\www\joplin\QtClient\JoplinQtClient\JoplinQtClient.pro -spec win32-msvc2015 "CONFIG+=debug" "CONFIG+=qml_debug"
@echo off
D:
mkdir "D:\Web\www\joplin\QtClient\build-JoplinQtClient-Visual_C_32_bits-Debug\"
cd "D:\Web\www\joplin\QtClient\build-JoplinQtClient-Visual_C_32_bits-Debug\"
"C:\Qt\5.7\msvc2015\bin\qmake.exe" D:\Web\www\joplin\QtClient\JoplinQtClient\JoplinQtClient.pro -spec win32-msvc2015 "CONFIG+=debug" "CONFIG+=qml_debug" "JOP_FRONT_END_GUI=1"
"C:\Qt\Tools\QtCreator\bin\jom.exe" qmake_all
"C:\Qt\Tools\QtCreator\bin\jom.exe"
rem "C:\Qt\5.7\msvc2015\bin\qmake.exe" D:\Web\www\joplin\QtClient\JoplinQtClient\JoplinQtClient.pro -spec win32-msvc2015 "CONFIG+=debug" "CONFIG+=qml_debug"
rem "C:\Qt\Tools\QtCreator\bin\jom.exe" qmake_all
rem "C:\Qt\Tools\QtCreator\bin\jom.exe"

View File

@@ -1,8 +1,24 @@
#!/bin/bash
BUILD_DIR=/home/laurent/src/notes/QtClient/build-JoplinQtClient-Desktop_Qt_5_7_1_GCC_64bit-Debug
mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
/opt/Qt/5.7/gcc_64/bin/qmake /home/laurent/src/notes/QtClient/JoplinQtClient/JoplinQtClient.pro -spec linux-g++ CONFIG+=debug CONFIG+=qml_debug JOP_FRONT_END_CLI=1
/usr/bin/make qmake_all
/usr/bin/make
set -e
mkdir -p /cygdrive/d/Web/www/joplin/QtClient/build-JoplinQtClient-Visual_C_32_bits-Debug
cd /cygdrive/d/Web/www/joplin/QtClient/build-JoplinQtClient-Visual_C_32_bits-Debug
rm -rf debug/ release/ Makefile*
export PATH="/cygdrive/c/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin":$PATH
export PATH=$PATH:"/cygdrive/c/Program Files (x86)/Windows Kits/8.1/bin/x86"
export PATH=$PATH:"/cygdrive/c/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include"
"/cygdrive/c/Qt/5.7/msvc2015/bin/qmake.exe" D:\\Web\\www\\joplin\\QtClient\\JoplinQtClient\\JoplinQtClient.pro -spec win32-msvc2015 "CONFIG+=debug" "CONFIG+=qml_debug" "JOP_FRONT_END_GUI=1"
"/cygdrive/c/Qt/Tools/QtCreator/bin/jom.exe" qmake_all
"/cygdrive/c/Qt/Tools/QtCreator/bin/jom.exe"
rsync -a /cygdrive/d/Web/www/joplin/QtClient/dependencies/dll-debug/ /cygdrive/d/Web/www/joplin/QtClient/build-JoplinQtClient-Visual_C_32_bits-Debug/debug
cd -
# BUILD_DIR=/home/laurent/src/notes/QtClient/build-JoplinQtClient-Desktop_Qt_5_7_1_GCC_64bit-Debug
# mkdir -p "$BUILD_DIR"
# cd "$BUILD_DIR"
# /opt/Qt/5.7/gcc_64/bin/qmake /home/laurent/src/notes/QtClient/JoplinQtClient/JoplinQtClient.pro -spec linux-g++ CONFIG+=debug CONFIG+=qml_debug JOP_FRONT_END_CLI=1
# /usr/bin/make qmake_all
# /usr/bin/make

2
QtClient/JoplinQtClient/cliapplication.cpp Normal file → Executable file
View File

@@ -1,4 +1,4 @@
#include <stab.h>
#include <stable.h>
#include "cliapplication.h"
#include "constants.h"

View File

@@ -2,7 +2,8 @@
set -e
cd /cygdrive/d/Web/www/joplin/QtClient/build-evernote-import-qt-Visual_C_32_bites-Debug
mkdir -p /cygdrive/d/Web/www/joplin/QtClient/build-evernote-import-qt-Visual_C_32_bits-Debug
cd /cygdrive/d/Web/www/joplin/QtClient/build-evernote-import-qt-Visual_C_32_bits-Debug
rm -rf debug/ release/ Makefile*
export PATH="/cygdrive/c/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin":$PATH
export PATH=$PATH:"/cygdrive/c/Program Files (x86)/Windows Kits/8.1/bin/x86"
@@ -10,5 +11,5 @@ export PATH=$PATH:"/cygdrive/c/Program Files (x86)/Microsoft Visual Studio 14.0/
"/cygdrive/c/Qt/5.7/msvc2015/bin/qmake.exe" D:\\Web\\www\\joplin\\QtClient\\evernote-import\\evernote-import-qt.pro -spec win32-msvc2015 "CONFIG+=debug" "CONFIG+=qml_debug"
"/cygdrive/c/Qt/Tools/QtCreator/bin/jom.exe" qmake_all
"/cygdrive/c/Qt/Tools/QtCreator/bin/jom.exe"
rsync -a /cygdrive/d/Web/www/joplin/QtClient/dependencies/dll-debug/ /cygdrive/d/Web/www/joplin/QtClient/build-evernote-import-qt-Visual_C_32_bites-Debug/debug
rsync -a /cygdrive/d/Web/www/joplin/QtClient/dependencies/dll-debug/ /cygdrive/d/Web/www/joplin/QtClient/build-evernote-import-qt-Visual_C_32_bits-Debug/debug
cd -

View File

@@ -1 +0,0 @@
{"last_sync_id":81,"file_map":[],"test":"abcd","client_id":"11111111111111111111111111111111","last_sync_time":1476289357,"session_id":null,"folder_items":[]}

View File

@@ -1 +0,0 @@
{"last_sync_id":80,"file_map":[],"client_id":"22222222222222222222222222222222","last_sync_time":1476289618,"folder_items":[],"session_id":null}

View File

@@ -1,2 +0,0 @@
#!/bin/bash
php main.php --config ~/src/notes/cli-client/.config1 "$@"

View File

@@ -1,2 +0,0 @@
#!/bin/bash
php main.php --config ~/src/notes/cli-client/.config2 "$@"

View File

@@ -1,174 +0,0 @@
package main
import (
//"bufio"
//"errors"
"fmt"
"os"
"io"
"io/ioutil"
// "os/exec"
// "os/user"
"path/filepath"
"path"
"crypto/md5"
// "runtime"
// "strconv"
"strings"
// "time"
"net/url"
"net/http"
"github.com/jessevdk/go-flags"
)
const VERSION = "1.0.0"
type SyncCommandOptions struct {
// FfmpegPath string `long:"ffmpeg" description:"Path to ffmpeg." default:"ffmpeg"`
// FrameDirPath string `short:"d" long:"frame-dir" description:"Path to directory that will contain the captured frames. (default: <PictureDirectory>/pmcctv)"`
// RemoteDir string `short:"r" long:"remote-dir" description:"Remote location where frames will be saved to. Must contain a path compatible with scp (eg. user@someip:~/pmcctv)."`
// RemotePort string `short:"p" long:"remote-port" description:"Port of remote location where frames will be saved to. If not set, whatever is the default scp port will be used (should be 22)."`
// BurstModeDuration int `short:"b" long:"burst-mode-duration" description:"Duration of burst mode, in seconds. Set to 0 to disable burst mode altogether." default:"10"`
// BurstModeFormat string `short:"f" long:"burst-mode-format" description:"Format of burst mode captured files, either \"image\" or \"video\"." default:"video"`
// FramesTtl int `short:"t" long:"time-to-live" description:"For how long captured frames should be kept, in days." default:"7"`
// InputDevice string `short:"i" long:"input-device" description:"Name of capture input device. (default: auto-detect)"`
}
type AppCommandOptions struct {
Version bool `long:"version" description:"Display version information"`
}
type CommandOptions struct {
App AppCommandOptions
Sync SyncCommandOptions
}
func printHelp(flagParser *flags.Parser) {
flagParser.WriteHelp(os.Stdout)
fmt.Printf("\n")
fmt.Printf("For help with a particular command, type \"%s <command> --help\"\n", path.Base(os.Args[0]))
}
func createFlagParser() (CommandOptions, *flags.Parser) {
var opts CommandOptions
flagParser := flags.NewParser(&opts.App, flags.HelpFlag|flags.PassDoubleDash)
flagParser.AddCommand(
"sync",
"Synchronize notes",
"Synchronize local notes with the server.",
&opts.Sync,
)
return opts, flagParser
}
func createId(path string) string {
h := md5.New()
io.WriteString(h, "31208854954776365651")
io.WriteString(h, path)
return fmt.Sprintf("%x", h.Sum(nil))
}
func readFile(path string) (string, error) {
content, err := ioutil.ReadFile(path)
if err != nil {
return "", err
}
return string(content), nil
}
func makeApiCall(method string, path string, data url.Values) (error, []byte) {
baseUrl := "http://127.0.0.1:8000"
fullUrl := baseUrl + "/" + path
client := http.Client{}
request, err := http.NewRequest(method, fullUrl, strings.NewReader(data.Encode()))
if err != nil {
return err, []byte{}
}
request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
response, err := client.Do(request)
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return err, []byte{}
}
return nil, body
}
// type Note struct {
// Id string
// Title string
// Body string
// }
func main() {
var err error
//err, body := makeApiCall("GET", "users/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", url.Values{})
note := url.Values{}
note.Add("title", "from go")
note.Add("body", "body from go")
err, body := makeApiCall("POST", "notes", note)
fmt.Println(err)
fmt.Println(string(body))
os.Exit(0)
opts, flagParser := createFlagParser()
args, err := flagParser.Parse()
if err != nil {
t := err.(*flags.Error).Type
if t == flags.ErrHelp {
printHelp(flagParser)
os.Exit(0)
} else if t == flags.ErrCommandRequired {
// Here handle default flags (which are not associated with any command)
if opts.App.Version {
fmt.Println(VERSION)
os.Exit(0)
}
printHelp(flagParser)
os.Exit(0)
} else {
fmt.Printf("Error: %s\n", err)
fmt.Printf("Type '%s --help' for more information.\n", path.Base(os.Args[0]))
os.Exit(1)
}
}
_ = args
fullPath := "/home/laurent/src/notes/cli-client/test"
walkPath := func (path string, info os.FileInfo, err error) error {
if len(path) - len(fullPath) <= 0 {
return nil
}
p := path[len(fullPath)+1:];
fmt.Println(p)
fmt.Println(createId(p))
if !info.IsDir() {
content, err := readFile(path)
if err != nil {
fmt.Println(err)
return err
}
fmt.Println(content)
}
return nil
}
filepath.Walk(fullPath, walkPath)
}

View File

@@ -1,349 +0,0 @@
<?php
function escapePathElement($element) {
$valid = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ÀàÁáÂâÃãÄäÇçÈèÉéÊêËëÌìÍíÎîÏïÑnÒòÓóÔôÕõÖöŠšÚùÛúÜûÙüÝyŸÿŽz_- ().,';
$output = '';
$chars = preg_split('//u', $element, -1, PREG_SPLIT_NO_EMPTY); // Split a UTF-8 string into characters
foreach ($chars as $c) {
if (strpos($valid, $c) !== false) {
$output .= $c;
} else {
$output .= rawurlencode($c);
}
}
return $output;
}
function escapePath($path) {
$output = '';
$elements = preg_split('/[\\\\\/]/', $path);
for ($i = 0; $i < count($elements); $i++) {
$e = $elements[$i];
if ($i > 0) $output .= '/';
$output .= escapePathElement($e);
}
return $output;
}
class Api {
private $sessionId = null;
private $baseUrl = null;
public function __construct($baseUrl) {
$this->baseUrl = $baseUrl;
}
static public function createId($string) {
// TODO: This needs to be unique per user
return md5('gKcr0 ^L3UL^fJV%1IW~~/Q`.,WRAr</8@$.k|uyK-w^d:k|{h!%(};|)OY9^lu=' . $string);
}
public function setSessionId($sessionId) {
$this->sessionId = $sessionId;
}
public function toCurlCmd($method, $url, $data = null) {
$cmd = 'curl';
$addMethod = true;
if ($method == 'GET') $addMethod = false;
if ($method == 'POST' && count($data)) $addMethod = false;
if ($addMethod) $cmd .= ' -X ' . $method;
if ($data) {
foreach ($data as $k => $v) {
$cmd .= ' -F "' . $k . '=' . rawurlencode($v) . '"';
}
}
$cmd .= ' ' . "'" . $url . "'";
return $cmd;
}
public function exec($method, $path, $query = null, $data = null) {
$url = $this->baseUrl . '/' . $path;
if (!$query) $query = array();
if ($this->sessionId) $query['session'] = $this->sessionId;
if (count($query)) $url .= '?' . http_build_query($query);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
if ($data) curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
if ($method == 'PATCH' || $method == 'PUT' || $method == 'DELETE') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
}
$cmd = $this->toCurlCmd($method, $url, $data);
echo $cmd . "\n";
$content = curl_exec($ch);
curl_close($ch);
$output = json_decode($content, true);
if ($output === null) throw new Exception('Invalid response: ' . $content . "\n\nCommand: " . $cmd . "\n");
if (isset($output['error'])) throw new Exception('API error: ' . $content . "\n\nCommand: " . $cmd . "\n");
return $output;
}
public function login($email, $password, $clientId) {
$method = 'POST';
$path = 'sessions';
$data = array(
'email' => $email,
'password' => $password,
'client_id' => $clientId,
);
return $this->exec($method, $path, null, $data);
}
}
class Config {
protected $dirPath = null;
public function __construct($dirPath) {
$this->dirPath = $dirPath;
}
protected function load() {
$c = @file_get_contents($this->dirPath . '/config.json');
$c = json_decode($c, true);
if ($c === null) $c = array();
if (!isset($c['last_sync_id'])) $c['last_sync_id'] = 0;
if (!isset($c['last_sync_time'])) $c['last_sync_time'] = 0;
if (!isset($c['folder_items'])) $c['folder_items'] = array();
if (!isset($c['client_id'])) $c['client_id'] = null;
if (!isset($c['session_id'])) $c['session_id'] = null;
return $c;
}
protected function save($c) {
file_put_contents($this->dirPath . '/config.json', json_encode($c));
}
public function get($name) {
$c = $this->load();
if (!isset($c[$name])) throw new Exception('Invalid key name: ' . $name);
return $c[$name];
}
public function set($name, $value) {
$c = $this->load();
$c[$name] = $value;
$this->save($c);
}
}
class BaseItem {
private $title = '';
private $body = '';
private $id;
private $parentId;
private $isFolder;
private $modTime;
public function setTitle($v) { $this->title = $v; }
public function setBody($v) { $this->body = $v; }
public function setId($v) { $this->id = $v; }
public function setParentId($v) { $this->parentId = $v; }
public function setIsFolder($v) { $this->isFolder = $v; }
public function setModTime($v) { $this->modTime = $v; }
public function title() { return $this->title; }
public function body() { return $this->body; }
public function id() { return $this->id; }
public function parentId() { return $this->parentId; }
public function isFolder() { return $this->isFolder; }
public function isNote() { return !$this->isFolder(); }
public function modTime() { return $this->modTime; }
public function toApiArray() {
$output = array(
'title' => $this->title(),
'parent_id' => $this->parentId(),
);
if ($this->isNote()) $output['body'] = $this->body();
return $output;
}
public function fromApiArray($type, $array) {
$this->setTitle($array['title']);
if ($type == 'note') $this->setBody($array['body']);
$this->setId($array['id']);
$this->setParentId($array['parent_id']);
$this->setIsFolder($type == 'folder');
}
}
class BaseItems {
private $items = array();
private function getBaseItems($dir, $parentId, &$output) {
$paths = glob($dir . '/*');
foreach ($paths as $path) {
$isFolder = is_dir($path);
$modTime = filemtime($path);
$o = new BaseItem();
$o->setTitle(basename($path));
$o->setId(Api::createId($parentId . '_' . $o->title()));
$o->setParentId($parentId);
$o->setIsFolder($isFolder);
$o->setModTime($modTime);
if (!$isFolder) $o->setBody(file_get_contents($path));
$output[] = $o;
if ($isFolder) $this->getBaseItems($path, $o->id(), $output);
}
}
public function fromPath($path) {
$this->items = array();
$this->getBaseItems($path, null, $this->items);
}
public function all() {
return $this->items;
}
public function add($item) {
$this->items[] = $item;
}
public function setById($id, $item) {
$found = false;
for ($i = 0; $i < count($this->items); $i++) {
$it = $this->items[$i];
if ($it->id() == $id) {
$found = true;
$this->items[$i] = $item;
break;
}
}
if (!$found) {
$this->items[] = $item;
}
}
public function byId($id) {
foreach ($this->all() as $item) {
if ($item->id() == $id) return $item;
}
return null;
}
public function itemFullPath($item) {
if (!$item->parentId()) return $item->title();
$parent = $this->byId($item->parentId());
if (!$parent) throw new Exception('Cannot find parent with ID ' . $item->parentId());
return escapePath($this->itemFullPath($parent) . '/' . $item->title());
}
}
$shortopts = "";
$longopts = array(
"config:",
"sync",
);
$flags = getopt($shortopts, $longopts);
if (!isset($flags['config'])) $flags['config'] = '/home/laurent/src/notes/cli-client/.config';
$config = new Config($flags['config']);
$dataPath = '/home/laurent/src/notes/cli-client/test_' . $config->get('client_id');
$api = new Api('http://127.0.0.1:8000');
$session = $api->login('test@example.com', '12345678', $config->get('client_id'));
$api->setSessionId($session['id']);
if (array_key_exists('sync', $flags)) {
$syncStartTime = time();
$lastSyncTime = $config->get('last_sync_time');
$BaseItems = new BaseItems();
$BaseItems->fromPath($dataPath);
// ------------------------------------------------------------------------------------------
// Get latest changes from API
// ------------------------------------------------------------------------------------------
$response = $api->exec('GET', 'synchronizer', array('last_id' => $config->get('last_sync_id')));
// $response = $api->exec('GET', 'synchronizer', array('last_id' => 80));
$pathMap = array();
$folders = array();
$notes = array();
$maxId = null;
foreach ($response['items'] as $item) {
$BaseItem = new BaseItem();
switch ($item['type']) {
case 'create':
case 'update':
$resource = $api->exec('GET', $item['item_type'] . 's/' . $item['item_id']);
$BaseItem->fromApiArray($item['item_type'], $resource);
break;
default:
throw new Exception('Unsupported action type: ' . $item['type']);
}
$BaseItems->setById($BaseItem->id(), $BaseItem);
$maxId = max($item['id'], $maxId);
}
foreach ($BaseItems->all() as $item) {
$relativePath = $BaseItems->itemFullPath($item);
$path = $dataPath . '/' . $relativePath;
foreach (array('folder', 'note') as $itemType) {
if ($item->isFolder() && $itemType == 'folder') {
@mkdir($path, 0755, true); // Ignore "File exists" warning
if (!is_dir($path)) throw new Exception('Could not create folder at ' . $path);
}
if ($item->isNote() && $itemType == 'note') {
if ($item->body() !== file_get_contents($path)) {
file_put_contents($path, $item->body());
}
}
}
$pathMap[$item->id()] = $relativePath;
}
// ------------------------------------------------------------------------------------------
// Send changed notes and folders to API
// ------------------------------------------------------------------------------------------
foreach ($BaseItems->all() as $item) {
if ($item->modTime() < $lastSyncTime) continue;
if ($item->isFolder()) {
$api->exec('PUT', 'folders/' . $item->id(), null, $item->toApiArray());
} else {
$api->exec('PUT', 'notes/' . $item->id(), null, $item->toApiArray());
}
}
$config->set('last_sync_time', $syncStartTime);
$config->set('folder_items', json_encode($pathMap));
if ($maxId !== null) $config->set('last_sync_id', $maxId);
}

View File

@@ -1,3 +0,0 @@
# FOLDER2/note1
Modified on client 2

View File

@@ -1,3 +0,0 @@
# FOLDER2/note1
Modified on client 2

View File

@@ -4,8 +4,16 @@
{
"path": ".",
"folder_exclude_patterns": [
"var"
]
"var",
"vendor",
"QtClient/build-JoplinQtClient-Visual_C_32_bits-Debug",
"QtClient/data/resources",
"app/data/uploads"
],
"file_exclude_patterns": [
"*.pro.user",
"*.pro.user.*"
]
}
],
"build_systems":

View File

@@ -1,12 +0,0 @@
{
"presets": [
"es2015",
"react",
"stage-0"
],
"plugins": [
[
"transform-decorators-legacy"
]
]
}

39
spa_client/.gitignore vendored
View File

@@ -1,39 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
node_modules
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
# Ignore files for distribution
dist/*
# OSX .DS_Store files
.DS_Store

View File

@@ -1,19 +0,0 @@
React/Redux Example Todo Application
====================================
An example application demonstrating how to build a Todo with React and Redux.
# How to run
Open up a terminal and execute:
```term
$ npm install
$ npm start
```
Then open up your browser and navigate to [http://localhost:3000/](http://localhost:3000/).
# License
Apache 2.0

View File

@@ -1,42 +0,0 @@
{
"name": "stormpath-react-redux-todo-example-application",
"version": "0.0.1",
"description": "React/Redux example todo application.",
"homepage": "https://github.com/typerandom/stormpath-react-redux-todo-example-application",
"author": "Stormpath, 2016",
"license": "Apache-2.0",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "node ./server ./src/ ./webpack.dev.config",
"build": "rm -rf ./dist/ && mkdir ./dist/ && cp -r ./src/* ./dist && rm -rf ./dist/js/* && webpack",
"start": "npm run build && node ./server"
},
"repository": {
"type": "git",
"url": "git+https://github.com/typerandom/stormpath-react-redux-todo-example-application.git"
},
"bugs": {
"url": "https://github.com/typerandom/stormpath-react-redux-todo-example-application/issues"
},
"devDependencies": {
"babel-core": "^6.3.26",
"babel-loader": "^6.2.0",
"babel-plugin-react-transform": "^2.0.0",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-preset-stage-0": "^6.3.13",
"babel-runtime": "^6.3.19",
"express": "^4.13.4",
"morgan": "^1.7.0",
"open": "0.0.5",
"react": "^0.14.7",
"react-dom": "^0.14.7",
"react-redux": "^4.4.6",
"redux": "^3.4.0",
"webpack": "^1.12.13",
"webpack-dev-middleware": "^1.5.1",
"object-path-immutable": "^0.5.1",
"deepcopy": "^0.6.3"
}
}

View File

@@ -1,32 +0,0 @@
var open = require('open');
var path = require('path');
var morgan = require('morgan');
var express = require('express');
var webpack = require('webpack');
var webpackDevMiddleware = require('webpack-dev-middleware');
var config = require('./webpack.config');
var compiler = webpack(config);
var app = express();
app.use(morgan('dev'));
app.use(webpackDevMiddleware(compiler, {
noInfo: true,
publicPath: config.output.publicPath
}));
app.use(express.static('./dist/'));
app.get('*', function (req, res){
res.sendFile(path.resolve(__dirname, './dist/', 'index.html'))
});
app.listen(3000, function (err) {
if (err) {
return console.error(err);
}
console.log('Web listening at http://localhost:3000/.');
});

View File

@@ -1,39 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="utf-8">
<style>
a, a:active, a:visited {
text-decoration: none;
}
.selected {
border: 1px solid black;
}
.level-1 {
margin-left: 0;
}
.level-2 {
margin-left: 20px;
}
.level-3 {
margin-left: 40px;
}
.folder-list {
display: inline-block;
vertical-align: top;
padding: 15px;
}
.note-list {
display: inline-block;
vertical-align: top;
padding: 15px;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="/js/app.js"></script>
</body>
</html>

View File

@@ -1,420 +0,0 @@
import React from 'react';
import { render } from 'react-dom'
import { createStore } from 'redux';
import { Provider } from 'react-redux'
import RootFolderList from 'components/root-folder-list.jsx';
import NoteList from 'components/note-list.jsx';
import { reducer } from './reducer.jsx'
let defaultState = {
'myButtonLabel': 'click',
items: [
{ id: 101, title: 'folder 1', type: 1, parent_id: 0 },
{ id: 102, title: 'folder 2', type: 1, parent_id: 0 },
{ id: 103, title: 'folder 3', type: 1, parent_id: 101 },
{ id: 104, title: 'folder 4', type: 1, parent_id: 101 },
{ id: 105, title: 'folder 5', type: 1, parent_id: 0 },
{ id: 106, title: 'folder 6', type: 1, parent_id: 105 },
{ id: 1, type: 2, parent_id: 101, title: 'one', body: '111 dsqfdsmlk mqkfkdq sfkl qlmskfqm' },
{ id: 2, type: 2, parent_id: 101, title: 'two', body: '222 dsqfdsmlk mqkfkdq sfkl 222 qlmskfqm' },
{ id: 3, type: 2, parent_id: 103, title: 'three', body: '33 dsqfdsmlk mqkfkdq sfkl 33 qlmskfqm' },
{ id: 4, type: 2, parent_id: 103, title: 'four', body: '4222 dsqfdsmlk mqkfkdq sfkl 222 qlmskfqm' },
{ id: 5, type: 2, parent_id: 103, title: 'five', body: '5222 dsqfdsmlk mqkfkdq sfkl 222 qlmskfqm' },
{ id: 6, type: 2, parent_id: 104, title: 'six', body: '6222 dsqfdsmlk mqkfkdq sfkl 222 qlmskfqm' },
{ id: 7, type: 2, parent_id: 104, title: 'seven', body: '7222 dsqfdsmlk mqkfkdq sfkl 222 qlmskfqm' },
],
selectedFolderId: null,
selectedNoteId: null,
expandedFolderIds: [],
}
let store = createStore(reducer, defaultState)
class App extends React.Component {
render() {
return (
<div>
<RootFolderList />
<NoteList />
</div>
)
}
}
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('container')
)
// var defaultState = {
// folders: [],
// todo: {
// items: []
// },
// selectedFolderId: null
// };
// function addTodo(message) {
// return {
// type: 'ADD_TODO',
// message: message,
// completed: false
// };
// }
// function completeTodo(index) {
// return {
// type: 'COMPLETE_TODO',
// index: index
// };
// }
// function deleteTodo(index) {
// return {
// type: 'DELETE_TODO',
// index: index
// };
// }
// function clearTodo() {
// return {
// type: 'CLEAR_TODO'
// };
// }
// function createId() {
// return Math.round(Math.random() * 99999);
// }
// function folderIndex(state, id) {
// for (var i = 0; i < state.folders.length; i++) {
// if (state.folders[i].id == id) return i;
// }
// return -1;
// }
// function folderById(state, id) {
// var i = folderIndex(state, id);
// return i >= 0 ? state.folders[i] : null;
// }
// function todoApp(state, action) {
// switch (action.type) {
// case 'ADD_FOLDER':
// var folder = {
// name: action.name,
// id: createId(),
// selected: false
// };
// state = immutable.push(state, 'folders', folder);
// state = immutable.set(state, 'selectedFolderId', folder.id);
// return state;
// case 'DELETE_FOLDER':
// var folders = deepcopy(state.folders);
// var index = folderIndex(state, action.id);
// if (index < 0) return state;
// folders.splice(index, 1);
// return immutable.set(state, 'folders', folders);
// case 'SET_SELECTED_FOLDER':
// return immutable.set(state, 'selectedFolderId', action.id);
// case 'SET_FOLDER_NAME':
// var idx = folderIndex(state, action.id);
// return immutable.set(state, 'folders.' + idx + '.name', action.name);
// case 'ADD_TODO':
// var items = [].concat(state.todo.items);
// return Object.assign({}, state, {
// todo: {
// items: items.concat([{
// message: action.message,
// completed: false
// }])
// }
// });
// case 'COMPLETE_TODO':
// var items = [].concat(state.todo.items);
// items[action.index].completed = true;
// return Object.assign({}, state, {
// todo: {
// items: items
// }
// });
// case 'DELETE_TODO':
// var items = [].concat(state.todo.items);
// items.splice(action.index, 1);
// return Object.assign({}, state, {
// todo: {
// items: items
// }
// });
// case 'CLEAR_TODO':
// return Object.assign({}, state, {
// todo: {
// items: []
// }
// });
// default:
// return state;
// }
// }
// var store = createStore(todoApp, defaultState);
// class AddTodoForm extends React.Component {
// state = {
// message: ''
// };
// onFormSubmit(e) {
// e.preventDefault();
// store.dispatch(addTodo(this.state.message));
// this.setState({ message: '' });
// }
// onMessageChanged(e) {
// var message = e.target.value;
// this.setState({ message: message });
// }
// render() {
// return (
// <form onSubmit={this.onFormSubmit.bind(this)}>
// <input type="text" placeholder="Todo..." onChange={this.onMessageChanged.bind(this)} value={this.state.message} />
// <input type="submit" value="Add" />
// </form>
// );
// }
// }
// class AddFolderForm extends React.Component {
// state = {
// name: ''
// };
// onFormSubmit(e) {
// e.preventDefault();
// store.dispatch({
// type: 'ADD_FOLDER',
// name: this.state.name
// });
// this.setState({ name: '' });
// }
// onInputChange(e) {
// this.setState({ name: e.target.value });
// }
// render() {
// return (
// <form onSubmit={this.onFormSubmit.bind(this)}>
// <input type="text" placeholder="Folder..." onChange={this.onInputChange.bind(this)} value={this.state.name} />
// <input type="submit" value="Add" />
// </form>
// );
// }
// }
// class TodoItem extends React.Component {
// onDeleteClick() {
// store.dispatch(deleteTodo(this.props.index));
// }
// onCompletedClick() {
// store.dispatch(completeTodo(this.props.index));
// }
// render() {
// return (
// <li>
// <a href="#" onClick={this.onCompletedClick.bind(this)} style={{textDecoration: this.props.completed ? 'line-through' : 'none'}}>{this.props.message.trim()}</a>&nbsp;
// <a href="#" onClick={this.onDeleteClick.bind(this)} style={{textDecoration: 'none'}}>[x]</a>
// </li>
// );
// }
// }
// class FolderItem extends React.Component {
// onDeleteClick() {
// store.dispatch({
// type: 'DELETE_FOLDER',
// id: this.props.item.id
// });
// }
// onSelected() {
// store.dispatch({
// type: 'SET_SELECTED_FOLDER',
// id: this.props.item.id
// });
// }
// render() {
// let selectedClass = this.props.selected ? 'selected' : '';
// return (
// <li>
// <a href="#" className={selectedClass} onClick={this.onSelected.bind(this)}>{this.props.item.name} ({this.props.item.id})</a> <a href="#" onClick={this.onDeleteClick.bind(this)}>[x]</a>
// </li>
// );
// }
// }
// class FolderList extends React.Component {
// state = {
// folders: [],
// selectedFolderId: null,
// folderName: '',
// lastSelectedFolderId: null
// };
// componentWillMount() {
// store.subscribe(() => {
// var state = store.getState();
// this.setState({
// folders: state.folders,
// selectedFolderId: state.selectedFolderId
// });
// });
// }
// folderNameInput_keyPress(e) {
// if (e.key == 'Enter') {
// console.info(this.state.folderName);
// store.dispatch({
// type: 'SET_FOLDER_NAME',
// name: this.state.folderName,
// id: this.state.selectedFolderId
// });
// }
// }
// folderNameInput_onChange(e) {
// this.setState({ folderName: e.target.value });
// }
// render() {
// var items = [];
// this.state.folders.forEach((item, index) => {
// let isSelected = this.state.selectedFolderId == item.id;
// items.push(
// <FolderItem
// key={index}
// index={index}
// item={item}
// selected={isSelected} />
// );
// });
// if (!items.length) {
// return (
// <p>
// <i>No folder.</i>
// </p>
// );
// }
// var selectedFolder = folderById(this.state, this.state.selectedFolderId);
// var selectedFolderId = selectedFolder ? selectedFolder.id : null;
// if (selectedFolderId !== this.state.lastSelectedFolderId) {
// this.state.folderName = selectedFolder ? selectedFolder.name : '';
// this.state.lastSelectedFolderId = selectedFolderId;
// }
// return (
// <div>
// <ol>{ items }</ol>
// <input type="text" onKeyPress={this.folderNameInput_keyPress.bind(this)} onChange={this.folderNameInput_onChange.bind(this)} value={this.state.folderName} />
// </div>
// );
// }
// }
// class TodoList extends React.Component {
// state = {
// items: []
// };
// componentWillMount() {
// store.subscribe(() => {
// var state = store.getState();
// this.setState({
// items: state.todo.items
// });
// });
// }
// render() {
// var items = [];
// this.state.items.forEach((item, index) => {
// items.push(<TodoItem
// key={index}
// index={index}
// message={item.message}
// completed={item.completed}
// />);
// });
// if (!items.length) {
// return (
// <p>
// <i>Please add something to do.</i>
// </p>
// );
// }
// return (
// <ol>{ items }</ol>
// );
// }
// }
// ReactDOM.render(
// <div>
// <h1>Todo</h1>
// <AddTodoForm />
// <AddFolderForm />
// <FolderList />
// <TodoList />
// </div>,
// document.getElementById('container')
// );
// store.dispatch({
// type: 'ADD_FOLDER',
// name: 'aaaa'
// });
// store.dispatch({
// type: 'ADD_FOLDER',
// name: 'bbbb'
// });
// store.dispatch({
// type: 'ADD_FOLDER',
// name: 'cccc'
// });

View File

@@ -1,39 +0,0 @@
import React from 'react';
import Folder from './folder.jsx';
import { connect } from 'react-redux';
import * as fi from 'models/folder-item.jsx';
class FolderListComponent extends React.Component {
render() {
let elements = [];
let level = Number(this.props.level) + 1;
let className = 'level-' + level;
className += ' folder-list';
this.props.items.forEach((item, index) => {
if (this.props.parentId != item.parent_id) return;
let selected = this.props.selectedFolderId == item.id;
let children = fi.children(this.props.items, item.id);
elements.push(<Folder level={level} title={item.title} key={item.id} id={item.id} expandedFolderIds={this.props.expandedFolderIds} selectedFolderId={this.props.selectedFolderId} children={children} />);
});
return <div className={className}>{elements}</div>
}
}
const mapStateToProps = function(state) {
return {}
}
const mapDispatchToProps = function(dispatch) {
return {}
}
const FolderList = connect(
mapStateToProps,
mapDispatchToProps
)(FolderListComponent)
export default FolderList

View File

@@ -1,50 +0,0 @@
import React from 'react';
import { connect } from 'react-redux'
import FolderList from './folder-list.jsx';
class FolderComponent extends React.Component {
render() {
let selectedClass = this.props.selectedFolderId == this.props.id ? 'selected' : '';
let elements = [];
let key = 'note-name-' + this.props.id;
elements.push(
<div key={key} onClick={this.props.onClick.bind(this)} className={selectedClass} id="{this.props.id}">{this.props.title}</div>
);
var showChildren = this.props.children.length && this.props.expandedFolderIds.indexOf(this.props.id) >= 0;
if (showChildren) {
key = 'folder-list-' + this.props.id;
elements.push(
<FolderList key={key} level={this.props.level} parentId={this.props.id} items={this.props.children} selectedFolderId={this.props.selectedFolderId} />
);
}
return <div>{elements}</div>
}
}
const Folder = connect(
function(state) { return {} },
function(dispatch) {
return {
onClick: function(event) {
dispatch({
type: 'SELECT_FOLDER',
id: this.props.id,
});
dispatch({
type: 'TOGGLE_FOLDER',
id: this.props.id,
});
}
}
}
)(FolderComponent)
export default Folder

View File

@@ -1,11 +0,0 @@
import React from 'react';
class MyButton extends React.Component {
render() {
return <button onClick={this.props.onClick}>{this.props.label}</button>
}
}
export default MyButton

View File

@@ -1,28 +0,0 @@
import React from 'react';
import { connect } from 'react-redux'
class NoteListItemComponent extends React.Component {
render() {
let className = this.props.selected ? 'selected' : '';
return <div onClick={this.props.onClick.bind(this)} className={className}>{this.props.title}</div>
}
}
const NoteListItem = connect(
function(state) { return {} },
function(dispatch) {
return {
onClick: function(event) {
console.info(this.props.id);
dispatch({
type: 'SELECT_NOTE',
id: this.props.id,
});
}
}
}
)(NoteListItemComponent)
export default NoteListItem

View File

@@ -1,37 +0,0 @@
import React from 'react';
import NoteListItem from './note-list-item.jsx';
import { connect } from 'react-redux'
import * as fi from 'models/folder-item.jsx';
class NoteListComponent extends React.Component {
render() {
let elements = [];
this.props.items.forEach((item, index) => {
let selected = this.props.selectedNoteId == item.id;
elements.push(<NoteListItem selected={selected} key={item.id} title={item.title} body={item.body} id={item.id} />);
});
return <div className="note-list">{elements}</div>;
}
}
const mapStateToProps = function(state) {
return {
items: fi.notes(state.items, state.selectedFolderId),
selectedNoteId: state.selectedNoteId,
};
}
const mapDispatchToProps = function(dispatch) {
return { }
}
const NoteList = connect(
mapStateToProps,
mapDispatchToProps
)(NoteListComponent)
export default NoteList

View File

@@ -1,31 +0,0 @@
import React from 'react';
import FolderList from './folder-list.jsx';
import { connect } from 'react-redux';
import * as fi from 'models/folder-item.jsx';
class RootFolderListComponent extends React.Component {
render() {
return <FolderList expandedFolderIds={this.props.expandedFolderIds} level="0" parentId="0" items={this.props.items} selectedFolderId={this.props.selectedFolderId} />
}
}
const mapStateToProps = function(state) {
return {
items: fi.folders(state.items),
selectedFolderId: state.selectedFolderId,
expandedFolderIds: state.expandedFolderIds,
};
}
const mapDispatchToProps = function(dispatch) {
return {}
}
const RootFolderList = connect(
mapStateToProps,
mapDispatchToProps
)(RootFolderListComponent)
export default RootFolderList

View File

@@ -1,42 +0,0 @@
export function rootItems(items) {
var output = [];
items.forEach((item, index) => {
if (!item.parent_id) return;
output.push(item);
});
return output;
}
export function folders(items) {
var output = [];
items.forEach((item, index) => {
if (item.type != 1) return;
output.push(item);
});
return output;
}
export function notes(items, folderId) {
var output = [];
items.forEach((item, index) => {
if (item.type != 2) return;
if (item.parent_id != folderId) return;
output.push(item);
});
return output;
}
export function byId(items, id) {
for (let i = 0; i < items.length; i++) {
if (items[i].id == id) return items[i];
}
return null;
}
export function children(items, id) {
var output = [];
for (let i = 0; i < items.length; i++) {
if (items[i].parent_id == id) output.push(items[i]);
}
return output;
}

View File

@@ -1,32 +0,0 @@
import deepcopy from 'deepcopy';
export function reducer(state, action) {
switch (action.type) {
case 'SELECT_FOLDER':
var state = deepcopy(state);
state.selectedFolderId = action.id;
return state;
case 'SELECT_NOTE':
var state = deepcopy(state);
state.selectedNoteId = action.id;
return state;
case 'TOGGLE_FOLDER':
var state = deepcopy(state);
var idx = state.expandedFolderIds.indexOf(action.id);
if (idx < 0) {
state.expandedFolderIds.push(action.id);
} else {
state.expandedFolderIds.splice(idx, 1);
}
return state;
}
return state;
}

View File

@@ -1,128 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { render } from 'react-dom'
import { connect } from 'react-redux'
import { createStore } from 'redux';
import immutable from 'object-path-immutable';
import { Provider } from 'react-redux'
import deepcopy from 'deepcopy';
// This is the default state of the application. Each
// application has only one state, which is an object
// with one or more properties.
let defaultState = {
'myButtonLabel': 'click'
}
// The reducer is what processes the actions of the application, such as
// button clicks, text changes, etc. It takes a state and an action as
// input and must return a state. Important: the state must not be modified
// directly. Create a copy first (see `deepcopy`), modify it and return it.
function reducer(state, action) {
switch (action.type) {
case 'SET_BUTTON_NAME':
var state = deepcopy(state);
state.myButtonLabel = action.name;
return state;
}
return state;
}
// The store is what essentially links the reducer to the state.
let store = createStore(reducer, defaultState)
// Create the button and input components. Those are regular React components.
class MyButton extends React.Component {
render() {
return <button onClick={this.props.onClick}>{this.props.label}</button>
}
}
class MyInput extends React.Component {
render() {
return <input onKeyPress={this.props.onKeyPress} type="text" />
}
}
// Create the connected components. A connected component (often called "container component")
// is a react component that has been connected to the application state. The connection
// happens by mapping the state properties to the component properties, and by mapping the
// dispatches to the component handlers. This allows better separating the view (React
// component) from the model/controller (state and actions).
const mapStateToButtonProps = function(state) {
return { label: state.myButtonLabel };
}
const mapDispatchToButtonProps = function(dispatch) {
return {
onClick: function() {
alert('click');
}
}
}
const MyConnectedButton = connect(
mapStateToButtonProps,
mapDispatchToButtonProps
)(MyButton)
const mapStateToInputProps = function(state) {
return {}
}
const mapDispatchToInputProps = function(dispatch) {
return {
onKeyPress(e) {
if (e.key == 'Enter') {
dispatch({
type: 'SET_BUTTON_NAME',
name: e.target.value
});
}
}
}
}
const MyConnectionInput = connect(
mapStateToInputProps,
mapDispatchToInputProps
)(MyInput)
// Create the application. Note that we display the Connected components,
// which in turn include the Rect components.
class App extends React.Component {
render() {
return (
<div>
<MyConnectedButton />
<MyConnectionInput />
</div>
)
}
}
// Render the application via the <Provider> tag. This is a special React-Redux
// component that "magically" links the store to the application and components.
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('container')
)

View File

@@ -1,38 +0,0 @@
var path = require('path');
var webpack = require('webpack');
module.exports = {
quiet: true,
entry: [
'./src/js/app.jsx'
],
output: {
path: __dirname + '/dist/js/',
filename: 'app.js',
publicPath: '/js/'
},
module: {
loaders: [{
test: /\.(jsx|js)$/,
loaders: ['babel'],
include: path.join(__dirname, './src/js/')
}]
},
resolve: {
alias: {
components: path.resolve(__dirname, './src/js/components'),
models: path.resolve(__dirname, './src/js/models'),
},
},
plugins: [
// new webpack.optimize.UglifyJsPlugin({
// minimize: true,
// compress: {
// warnings: false
// }
// }),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"development"'
})
]
};

View File

@@ -1,32 +0,0 @@
// NOT USED
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: [
'./src/js/app'
],
target: 'web',
devtool: 'eval-source-map',
output: {
path: __dirname + '/src/js/',
filename: 'app.min.js',
publicPath: '/js/'
},
module: {
loaders: [{
test: /\.js$/,
loaders: ['babel'],
include: path.join(__dirname, './src/js/')
}]
},
plugins: [
new webpack.ProvidePlugin({
'Promise': 'es6-promise'
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"development"'
})
]
};