mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-10-16 17:32:43 +00:00
Compare commits
471 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
544a1381fc | ||
|
bc17ef896e | ||
|
0bcbf999fb | ||
|
0c4c44f4b6 | ||
|
486956b762 | ||
|
8d1ef5291f | ||
|
611627ca97 | ||
|
732225b781 | ||
|
58f88075c8 | ||
|
e17ef5eccf | ||
|
70c697f094 | ||
|
4b5625e0b4 | ||
|
7df48870df | ||
|
4a309c49e5 | ||
|
b45899bf90 | ||
|
5ae3c8acfe | ||
|
ef5df5f01b | ||
|
9af7451a61 | ||
|
7048c550b7 | ||
|
f035627f42 | ||
|
7659ff1f77 | ||
|
3df4062a55 | ||
|
02bbbe668b | ||
|
2b82c8fdc9 | ||
|
93abaed0c2 | ||
|
b7fff6d452 | ||
|
d09f01db90 | ||
|
e0c1a58b84 | ||
|
1b82649cca | ||
|
9317e2a36b | ||
|
51aff8ccdc | ||
|
bdd3ea45af | ||
|
3eb5c77ac4 | ||
|
ccc85784c6 | ||
|
46806e8607 | ||
|
95f914d6bd | ||
|
0f86011106 | ||
|
acdd3c0602 | ||
|
05b7237add | ||
|
51f4e9e160 | ||
|
7bbac35d6b | ||
|
d280fe457c | ||
|
b5772af4c3 | ||
|
7ab79bb76e | ||
|
00e3cc26de | ||
|
4b491c43ff | ||
|
41233a933e | ||
|
b84e553d34 | ||
|
19b9fa1996 | ||
|
6abdc4172b | ||
|
5e0c0137d3 | ||
|
3e468d1348 | ||
|
051af521b2 | ||
|
19b6ad3568 | ||
|
5ae514e9b8 | ||
|
826ee98165 | ||
|
f4c021a634 | ||
|
717ae1903e | ||
|
3cd7cdd4fb | ||
|
ff612799c0 | ||
|
d364b04cad | ||
|
212aef79aa | ||
|
9ca2b08a0e | ||
|
fc3ac88806 | ||
|
c550c4c212 | ||
|
f0df8a4a09 | ||
|
65923abcae | ||
|
c4f3009bb5 | ||
|
405a81c8f0 | ||
|
7a067a0f6e | ||
|
32c9f197b1 | ||
|
85151b23d3 | ||
|
fc0a4d90df | ||
|
9558616f59 | ||
|
5be5741ac3 | ||
|
80c0e6a4e0 | ||
|
9bc8f54233 | ||
|
a4c02cef54 | ||
|
935f4132ab | ||
|
a1cb82e7db | ||
|
146d782729 | ||
|
ac8bf5e939 | ||
|
2a70620844 | ||
|
7494b867a7 | ||
|
2874ee8609 | ||
|
e61145eabe | ||
|
4f01c574d2 | ||
|
06a471033c | ||
|
3761bfd26f | ||
|
07ad1b1de4 | ||
|
5725af55a8 | ||
|
d0f75ffda9 | ||
|
4117938140 | ||
|
f50c206d4f | ||
|
ed95cb791c | ||
|
9d0bf6d409 | ||
|
5e68a8200d | ||
|
15887994fe | ||
|
8bec8281d2 | ||
|
2e5a5812a9 | ||
|
744f9316a9 | ||
|
a6da9e8943 | ||
|
14d3bb273c | ||
|
37f8c0783d | ||
|
05a946e2bf | ||
|
a1b3e0778a | ||
|
93f8ade350 | ||
|
2a3a942b8e | ||
|
796583c0be | ||
|
df31b56cf3 | ||
|
e5f4446c82 | ||
|
0252495eca | ||
|
5e75a6b4a0 | ||
|
3002a4f9b3 | ||
|
f627c5e5a1 | ||
|
4d0a93fc3b | ||
|
3f040e7744 | ||
|
e572d544ef | ||
|
ab3f585a2b | ||
|
8b792c5fe0 | ||
|
f184116824 | ||
|
ab2c91ba20 | ||
|
f2a786254e | ||
|
7cbc8a0ede | ||
|
08bc644de1 | ||
|
f12125f2a4 | ||
|
4e86de7fc2 | ||
|
38796a0d40 | ||
|
8562bdfc4a | ||
|
74c96d8521 | ||
|
5f8ef64ca4 | ||
|
5a5f5318d1 | ||
|
db9e615e4d | ||
|
2aae0528b5 | ||
|
8d29bd5c32 | ||
|
77cb8530e5 | ||
|
bfd7404c85 | ||
|
836148a335 | ||
|
1ab00ac804 | ||
|
7a3db8f670 | ||
|
fd3676067a | ||
|
de61e4bf8c | ||
|
3d0de834e0 | ||
|
a897772df2 | ||
|
5726d796f9 | ||
|
8381960737 | ||
|
b61d41f569 | ||
|
af827b5645 | ||
|
13a01d76f7 | ||
|
2007f5ece4 | ||
|
a91a2c2609 | ||
|
0890fd521e | ||
|
4338cb305d | ||
|
84e02c828f | ||
|
be8707570c | ||
|
39602e0a98 | ||
|
bc2f2f5323 | ||
|
66e71282a0 | ||
|
bca56f88af | ||
|
35068631ec | ||
|
bd9557b11e | ||
|
0f2c19f40f | ||
|
4248ae7792 | ||
|
535a4205b1 | ||
|
ab14fc8440 | ||
|
86eea16271 | ||
|
52a9caa96e | ||
|
d8fe1a6e44 | ||
|
ae86090a03 | ||
|
87aa887b2f | ||
|
81378b5e25 | ||
|
6f615b8202 | ||
|
44c1266a8c | ||
|
920f720699 | ||
|
a7c394de6c | ||
|
4529675ca6 | ||
|
935909f24b | ||
|
7b61a0253c | ||
|
64e039cf8f | ||
|
65ae1f6f5a | ||
|
fa38ad3a74 | ||
|
fd500ac411 | ||
|
c6b5bb84e9 | ||
|
3b1f1a41dc | ||
|
d778f2dd8b | ||
|
287452169b | ||
|
6fdcd7f7e9 | ||
|
b16a49109f | ||
|
7d8cde2f0f | ||
|
a3fedb47cb | ||
|
095f08d443 | ||
|
a7948ee1da | ||
|
54eeb27f09 | ||
|
d0e57df1d8 | ||
|
b4df63f37f | ||
|
16a7ea2b75 | ||
|
3892ce3ed4 | ||
|
6c47de0170 | ||
|
8793196bb3 | ||
|
a3a9a8e3f1 | ||
|
bef5b48fce | ||
|
bc0ba2f524 | ||
|
1b387dabde | ||
|
fe8cb7c461 | ||
|
bfbf62d398 | ||
|
b22b845d57 | ||
|
104a62e24d | ||
|
4639911a04 | ||
|
6677f2f5d4 | ||
|
8adb16e1f6 | ||
|
9c9e93974e | ||
|
44a6604fe8 | ||
|
47a18666b6 | ||
|
a231ebc408 | ||
|
33ab2f9cc5 | ||
|
e56c1a5853 | ||
|
27a47d4e4a | ||
|
614e5e62af | ||
|
f45a2e7806 | ||
|
c03485b93e | ||
|
900e668cd6 | ||
|
2657e1194c | ||
|
ec5ad089a3 | ||
|
69a3354617 | ||
|
6e60d30970 | ||
|
d6fa1f9cbc | ||
|
bc8dccd4b5 | ||
|
14f48ec440 | ||
|
fd1b847077 | ||
|
ed3c0e42fb | ||
|
d72d6d925e | ||
|
4ae034b21c | ||
|
bbc7a1fff0 | ||
|
68ed3e4996 | ||
|
03ae6366ea | ||
|
af2f469b84 | ||
|
a64699d064 | ||
|
ac96baae66 | ||
|
15d9569fa4 | ||
|
bd99affb13 | ||
|
88166b7c73 | ||
|
3543ebb9a6 | ||
|
924f239d1b | ||
|
b641285cf4 | ||
|
c22ce3b9a2 | ||
|
64091b5463 | ||
|
09f0b251ab | ||
|
9bf22e5e13 | ||
|
360e1e327e | ||
|
4b8bc65fb7 | ||
|
1b9d91936b | ||
|
7457432c2a | ||
|
30bcc8e7f0 | ||
|
6232bf070d | ||
|
978b0ed811 | ||
|
24acd14da0 | ||
|
4950b69152 | ||
|
f25da27c58 | ||
|
12a5c4a311 | ||
|
3fcc163261 | ||
|
18a670ae29 | ||
|
f443fef091 | ||
|
58adf75c18 | ||
|
bee04bec44 | ||
|
01cc32004a | ||
|
5ede0d35f2 | ||
|
a1b8d951c1 | ||
|
880e2401e1 | ||
|
3434bd8460 | ||
|
239db35ba0 | ||
|
6310750168 | ||
|
133e41e3be | ||
|
b616cfa75c | ||
|
86a0b5f59a | ||
|
becf029583 | ||
|
f36f2357ef | ||
|
faa6d91f7c | ||
|
be5ecc6d0d | ||
|
00a18d54bb | ||
|
bad20c3e0b | ||
|
14a72dec15 | ||
|
7ef43caa1e | ||
|
244c4c6201 | ||
|
8d29947e32 | ||
|
29fc705672 | ||
|
2859db15fe | ||
|
9ab020518e | ||
|
838635ae4d | ||
|
a88e4932e6 | ||
|
56c94a2986 | ||
|
a4c3d9cb17 | ||
|
c31d392e19 | ||
|
a34a5c4db0 | ||
|
0441b9fb50 | ||
|
3e871ea33d | ||
|
54dda91726 | ||
|
0a19570f2c | ||
|
f280aa227e | ||
|
af34dc4d4d | ||
|
0ab1079c9c | ||
|
d2cb0bde91 | ||
|
5b34f7cf8c | ||
|
9e6e34eda0 | ||
|
cfd6cdc50f | ||
|
78346cda46 | ||
|
c547ab4570 | ||
|
939b54787e | ||
|
7b5b81504b | ||
|
5578a274f6 | ||
|
9e0c4a89f3 | ||
|
426728058c | ||
|
18390503b5 | ||
|
6e79b8404a | ||
|
4567272baa | ||
|
6ff3a37665 | ||
|
5420314784 | ||
|
f1dad4c9fb | ||
|
aba0b29c77 | ||
|
638263e8eb | ||
|
e2040b7194 | ||
|
0006e3b4bd | ||
|
06efd01032 | ||
|
1a20bd2f4d | ||
|
598cf61eb4 | ||
|
7b6772e77b | ||
|
c4bfa6d4d8 | ||
|
700ebc53f8 | ||
|
4db83293e5 | ||
|
0fcdc2e118 | ||
|
4b8d44aa2e | ||
|
915e716936 | ||
|
bb63232f55 | ||
|
43288b2e97 | ||
|
824eade8b2 | ||
|
df26058172 | ||
|
2f3219704e | ||
|
5c0532e6c6 | ||
|
a4d5b74b57 | ||
|
864106d336 | ||
|
5a612e792e | ||
|
e473dfc15e | ||
|
f931316dc8 | ||
|
e323a315e1 | ||
|
a55a2bcca0 | ||
|
f0c739c94c | ||
|
60db5abb54 | ||
|
9ab7356730 | ||
|
7f2537f9b1 | ||
|
6fe3ca1058 | ||
|
99dbb3abed | ||
|
053bdd0467 | ||
|
5554dce924 | ||
|
ec7a4734df | ||
|
40e3e71b48 | ||
|
5eed80c28e | ||
|
e2436ad796 | ||
|
7f0d82e960 | ||
|
5ecdca28fb | ||
|
67bd6eac99 | ||
|
f8ce8ece1e | ||
|
679521c8a0 | ||
|
f8c1644dee | ||
|
882e9731ac | ||
|
3cd882f618 | ||
|
98e333cef9 | ||
|
21a0063756 | ||
|
76f1a82746 | ||
|
e38dd346d9 | ||
|
a8184ec17d | ||
|
5fb751222b | ||
|
9910af04b0 | ||
|
6f5c86775b | ||
|
9c78af65e1 | ||
|
e14010da35 | ||
|
c61278f1ad | ||
|
576c668d84 | ||
|
f93a464b7d | ||
|
06271678a6 | ||
|
af0ea4d555 | ||
|
e448e87252 | ||
|
e4c54cc655 | ||
|
671338425a | ||
|
5237cf9eb1 | ||
|
33586f4bdf | ||
|
2cc05e1878 | ||
|
44c0f409a9 | ||
|
dde6d8e623 | ||
|
f387e28ed4 | ||
|
e0072630f3 | ||
|
fdf80c4584 | ||
|
68b3f1a8e6 | ||
|
f044f3ece2 | ||
|
46312cb44c | ||
|
167a548112 | ||
|
a381c91af7 | ||
|
eb0446bfe1 | ||
|
62fcf8402c | ||
|
f320f6e9b6 | ||
|
2c387f6ae6 | ||
|
702145dada | ||
|
a1973faa06 | ||
|
ac7fc587ee | ||
|
240487c258 | ||
|
4e07c7ef4b | ||
|
147791dbcd | ||
|
caf7056ce0 | ||
|
38e42cf71e | ||
|
584a12e1f5 | ||
|
d601919dc4 | ||
|
e5351c573b | ||
|
ccff5c5921 | ||
|
c3ef992219 | ||
|
c698d27a29 | ||
|
05162ebd59 | ||
|
6f991b3238 | ||
|
a14373a3e6 | ||
|
6a61dc47ac | ||
|
971729dfdd | ||
|
5c1feb2848 | ||
|
4455bf13c9 | ||
|
f28b0316f4 | ||
|
9999b3aa0c | ||
|
f9734dbc8b | ||
|
c1bd469086 | ||
|
900ba886ed | ||
|
ab0dce5dea | ||
|
6327670b6f | ||
|
7717b295bd | ||
|
b243d25399 | ||
|
59b339aba5 | ||
|
0d6ada13c4 | ||
|
dd32e61fcc | ||
|
cbf533aa7d | ||
|
5bccb1a555 | ||
|
139a364da6 | ||
|
60fcafa3b8 | ||
|
d7ef4adbc8 | ||
|
c89e5785d8 | ||
|
af910aa3f7 | ||
|
7e873880f5 | ||
|
1be0edaf03 | ||
|
609e498973 | ||
|
1585b7a142 | ||
|
c55ac659c1 | ||
|
1de52f7b64 | ||
|
26a6eb506e | ||
|
ede3e669c7 | ||
|
d00aa21c96 | ||
|
6181098f68 | ||
|
1d4c1624c4 | ||
|
899d05bc32 | ||
|
15856574d7 | ||
|
c09f8e97f7 | ||
|
4fb7f099de | ||
|
90c6283742 | ||
|
872d8584fb | ||
|
74afe433fb | ||
|
e85d6a39f8 | ||
|
2a41e55ff2 | ||
|
1e30811112 | ||
|
22f0d752ea | ||
|
4675bae804 | ||
|
57bd3c5a4e | ||
|
29db2dcd14 | ||
|
df153ee1a1 | ||
|
331c12269c | ||
|
d7844a1e39 | ||
|
d96b82500a | ||
|
74f1da81f2 | ||
|
472bc529f9 | ||
|
4266c0b279 |
7
.eslintignore
Normal file
7
.eslintignore
Normal file
@@ -0,0 +1,7 @@
|
||||
vendor/
|
||||
!/vendor/vendor.js
|
||||
/modules/**
|
||||
!/modules/default/**
|
||||
!/modules/node_helper
|
||||
!/modules/node_helper/**
|
||||
!/modules/default/defaultmodules.js
|
14
.eslintrc.json
Normal file
14
.eslintrc.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"rules": {
|
||||
"indent": ["error", "tab"],
|
||||
"quotes": ["error", "double"],
|
||||
"max-len": ["error", 250],
|
||||
"curly": "error",
|
||||
"camelcase": ["error", {"properties": "never"}]
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"es6": true
|
||||
}
|
||||
}
|
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
> Please send your PR's the develop branch.
|
||||
> Don't forget to add the change to changelog.md.
|
||||
|
||||
* Does the pull request solve a **related** issue? [yes | no]
|
||||
* If so, can you reference the issue?
|
||||
* What does the pull request accomplish? (please list)
|
||||
* If it includes major visual changes please add screenshots.
|
||||
|
||||
|
BIN
.github/header.png
vendored
Normal file
BIN
.github/header.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
.github/header.psd
vendored
Normal file
BIN
.github/header.psd
vendored
Normal file
Binary file not shown.
66
.gitignore
vendored
Normal file
66
.gitignore
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
# Various Node ignoramuses.
|
||||
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
lib-cov
|
||||
coverage
|
||||
.grunt
|
||||
.lock-wscript
|
||||
build/Release
|
||||
node_modules
|
||||
jspm_modules
|
||||
.npm
|
||||
.node_repl_history
|
||||
|
||||
# Various Windows ignoramuses.
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
$RECYCLE.BIN/
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
*.lnk
|
||||
|
||||
# Various OSX ignoramuses.
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
Icon
|
||||
._*
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# Various Linux ignoramuses.
|
||||
|
||||
.fuse_hidden*
|
||||
.directory
|
||||
.Trash-*
|
||||
|
||||
# Various Magic Mirror ignoramuses and anti-ignoramuses.
|
||||
|
||||
# Don't ignore the node_helper nore module.
|
||||
!/modules/node_helper
|
||||
!/modules/node_helper/**
|
||||
|
||||
# Ignore all modules except the default modules.
|
||||
/modules/**
|
||||
!/modules/default/**
|
||||
!/modules/README.md**
|
||||
|
||||
# Ignore changes to the custom css files.
|
||||
/css/custom.css
|
4
.stylelintrc
Normal file
4
.stylelintrc
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "stylelint-config-standard",
|
||||
"font-family-name-quotes": "double-where-recommended"
|
||||
}
|
9
.travis.yml
Normal file
9
.travis.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "6"
|
||||
- "5.1"
|
||||
- "4"
|
||||
- "0.12"
|
||||
before_script:
|
||||
- npm install grunt-cli -g
|
||||
script: grunt
|
37
CHANGELOG.md
Normal file
37
CHANGELOG.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# MagicMirror² Change Log
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [2.0.2] - 2016-06-05
|
||||
### Added
|
||||
- Norwegian Translations (nb and nn)
|
||||
- Portuguese Translation
|
||||
- Swedish Translation
|
||||
|
||||
### Fixed
|
||||
- Added reference to Italian Translation.
|
||||
- Added the missing NE translation to all languages. [#334](https://github.com/MichMich/MagicMirror/issues/344)
|
||||
- Added proper User-Agent string to calendar call.
|
||||
|
||||
### Changed
|
||||
- Add option to use locationID in weather modules.
|
||||
|
||||
## [2.0.1] - 2016-05-18
|
||||
### Added
|
||||
- Changelog
|
||||
- Italian Translation
|
||||
|
||||
### Changed
|
||||
- Improve the installer by fetching the latest Node.js without any 3rd party interferences.
|
||||
|
||||
## [2.0.0] - 2016-05-03
|
||||
### Initial release of MagicMirror²
|
||||
It includes (but is not limited to) the following features:
|
||||
- Modular system allowing 3rd party plugins.
|
||||
- An Node/Electron based application taking away the need for external servers or browsers.
|
||||
- A complete development API documentation.
|
||||
- Small cute fairies that kiss you while you sleep.
|
||||
|
||||
## [1.0.0] - 2014-02-16
|
||||
### Initial release of MagicMirror.
|
||||
This was part of the blogpost: http://michaelteeuw.nl/post/83916869600/magic-mirror-part-vi-production-of-the
|
28
Gruntfile.js
Normal file
28
Gruntfile.js
Normal file
@@ -0,0 +1,28 @@
|
||||
module.exports = function(grunt) {
|
||||
require("time-grunt")(grunt);
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON("package.json"),
|
||||
eslint: {
|
||||
options: {
|
||||
configFile: ".eslintrc.json"
|
||||
},
|
||||
target: ["js/*.js", "modules/default/*.js", "serveronly/*.js", "*.js"]
|
||||
},
|
||||
postcss: {
|
||||
lint: {
|
||||
options: {
|
||||
processors: [
|
||||
require("stylelint")({"extends": "stylelint-config-standard", "font-family-name-quotes": "double-where-recommended"}),
|
||||
require("postcss-reporter")({ clearMessages: true })
|
||||
]
|
||||
},
|
||||
dist: {
|
||||
src: "**/**/**/**/**/**/**/**.css"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
grunt.loadNpmTasks("grunt-eslint");
|
||||
grunt.loadNpmTasks("grunt-postcss");
|
||||
grunt.registerTask("default", ["eslint", "postcss:lint"]);
|
||||
};
|
18
LICENSE.md
Normal file
18
LICENSE.md
Normal file
@@ -0,0 +1,18 @@
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © 2016 Michael Teeuw
|
||||
|
||||
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.**
|
146
README.md
146
README.md
@@ -1,61 +1,129 @@
|
||||
MagicMirror
|
||||
===========
|
||||

|
||||
|
||||
**Note:** Please check out the [v2-beta](https://github.com/MichMich/MagicMirror/tree/v2-beta) branch if you are looking for a modular system with simple installer.
|
||||
<p align="center">
|
||||
<a href="https://david-dm.org/MichMich/MagicMirror"><img src="https://david-dm.org/MichMich/MagicMirror.svg" alt="Dependency Status"></a>
|
||||
<a href="https://david-dm.org/MichMich/MagicMirror#info=devDependencies"><img src="https://david-dm.org/MichMich/MagicMirror/dev-status.svg" alt="devDependency Status"></a>
|
||||
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-v5.10.1-brightgreen.svg" alt="Node Version"></a>
|
||||
<a href="http://choosealicense.com/licenses/mit"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
|
||||
<a href="https://travis-ci.org/MichMich/MagicMirror"><img src="https://travis-ci.org/MichMich/MagicMirror.svg" alt="Travis"></a>
|
||||
<a href="https://snyk.io/test/github/MichMich/MagicMirror"><img src="https://snyk.io/test/github/MichMich/MagicMirror/badge.svg" alt="Known Vulnerabilities" data-canonical-src="https://snyk.io/test/github/MichMich/MagicMirror" style="max-width:100%;"></a>
|
||||
</p>
|
||||
|
||||
##Introduction
|
||||
**MagicMirror²** is an open source modular smart mirror platform. With a growing list of installable modules, the **MagicMirror²** allows you to convert your hallway or bathroom mirror into your personal assistant. **MagicMirror²** is built by the creator of [the original MagicMirror](http://michaelteeuw.nl/tagged/magicmirror) with the incredible help of a [growing community of contributors](https://github.com/MichMich/MagicMirror/graphs/contributors).
|
||||
|
||||
The super magic interface of my personal Magic Mirror. More information about this project can be found on my [blog](http://michaelteeuw.nl/tagged/magicmirror).
|
||||
MagicMirror² focuses on a modular plugin system and uses [Electron](http://electron.atom.io/) as an application wrapper. So no more web server or browser installs necessary!
|
||||
|
||||
Runs as a php script on a web server with basically no external dependencies. *Can use socket.io for XBEE integration, but isn't required for basic functionality*.
|
||||
## Table Of Contents
|
||||
|
||||
- [Usage](#usage)
|
||||
- [Configuration](#configuration)
|
||||
- [Modules](#modules)
|
||||
- [Known Issues](#known-issues)
|
||||
- [community](#community)
|
||||
- [Contributing Guidelines](#contributing-guidelines)
|
||||
|
||||
## Usage
|
||||
|
||||
#### Raspberry Pi Support
|
||||
Electron, the app wrapper around MagicMirror², only supports the Raspberry Pi 2 & 3. The Raspberry Pi 1 is currently **not** supported. If you want to run this on a Raspberry Pi 1, use the [server only](#server-only) feature and setup a fullscreen browser yourself.
|
||||
|
||||
#### Automatic Installer (Raspberry Pi Only!)
|
||||
|
||||
Execute the following command on your Raspberry Pi to install MagicMirror²:
|
||||
````
|
||||
curl -sL https://raw.githubusercontent.com/MichMich/MagicMirror/master/installers/raspberry.sh | bash
|
||||
````
|
||||
|
||||
#### Manual Installation
|
||||
|
||||
1. Download and install the latest Node.js version.
|
||||
2. Clone the repository and check out the beta branch: `git clone https://github.com/MichMich/MagicMirror`
|
||||
3. Enter the repository: `cd ~/MagicMirror`
|
||||
4. Install and run the app: `npm install && npm start`
|
||||
|
||||
**Important:** `npm start` does **not** work via SSH, use `DISPLAY=:0 nohup npm start &` instead. This starts the mirror on the remote display.
|
||||
|
||||
#### Server Only
|
||||
|
||||
In some cases, you want to start the application without an actual app window. In this case, execute the following command from the MagicMirror folder: `node serveronly`. This will start the server, after which you can open the application in your browser of choice.
|
||||
|
||||
#### Raspberry Configuration & Auto Start.
|
||||
|
||||
The following wiki links are helpful in the configuration of your MagicMirror² operating system:
|
||||
- [Configuring the Raspberry Pi](https://github.com/MichMich/MagicMirror/wiki/Configuring-the-Raspberry-Pi)
|
||||
- [Auto Starting MagicMirror](https://github.com/MichMich/MagicMirror/wiki/Auto-Starting-MagicMirror)
|
||||
|
||||
#### Updating you MagicMirror²
|
||||
|
||||
If you want to update your MagicMirror² to the latest version, use your terminal to go to your Magic Mirror folder and type the following command:
|
||||
|
||||
````
|
||||
git pull
|
||||
````
|
||||
|
||||
If you changed nothing more than the config or the modules, this should work without any problems.
|
||||
Type `git status` to see your changes, if there are any, you can reset them with `git reset --hard`. After that, git pull should be possible.
|
||||
|
||||
## Configuration
|
||||
|
||||
1. Duplicate `config/config.js.sample` to `config/config.js`.
|
||||
2. Modify your required settings.
|
||||
|
||||
The following properties can be configured:
|
||||
|
||||
|
||||
##Configuration
|
||||
| **Option** | **Description** |
|
||||
| --- | --- |
|
||||
| `port` | The port on which the MagicMirror² server will run on. The default value is `8080`. |
|
||||
| `language` | The language of the interface. (Note: Not all elements will be localized.) Possible values are `en`, `nl`, `ru`, `fr`, etc., but the default value is `en`. |
|
||||
| `timeFormat` | The form of time notation that will be used. Possible values are `12` or `24`. The default is `24`. |
|
||||
| `units` | The units that will be used in the default weather modules. Possible values are `metric` or `imperial`. The default is `metric`. |
|
||||
| `modules` | An array of active modules. **The array must contain objects. See the next table below for more information.** |
|
||||
|
||||
Modify `js/config.js` to change some general variables (language, weather location, compliments, news feed RSS and to add your own ICS calendars)
|
||||
Module configuration:
|
||||
|
||||
To use the OpenWeatherMap API, you'll need a free API key. Checkout [this blogpost](http://michaelteeuw.nl/post/131504229357/what-happened-to-the-weather) for more information.
|
||||
| **Option** | **Description** |
|
||||
| --- | --- |
|
||||
| `module` | The name of the module. This can also contain the subfolder. Valid examples include `clock`, `default/calendar` and `custommodules/mymodule`. |
|
||||
| `position` | The location of the module in which the module will be loaded. Possible values are `top_ bar`, `top_left`, `top_center`, `top_right`, `upper_third`, `middle_center`, `lower_third`, `bottom_left`, `bottom_center`, `bottom_right`, `bottom_bar`, `fullscreen_above`, and `fullscreen_below`. This field is optional but most modules require this field to set. Check the documentation of the module for more information. Multiple modules with the same position will be ordered based on the order in the configuration file. |
|
||||
| `classes` | Additional classes which are passed to the module. The field is optional. |
|
||||
| `header` | To display a header text above the module, add the header property. This field is optional. |
|
||||
| `config` | An object with the module configuration properties. Check the documentation of the module for more information. This field is optional, unless the module requires extra configuration. |
|
||||
|
||||
##Code
|
||||
## Modules
|
||||
|
||||
###[main.js](js/main.js)
|
||||
The following modules are installed by default.
|
||||
|
||||
This file initiates the separate pieces of functionality that will appear in the view. It also includes various utility functions that are used to update what is visible.
|
||||
- [**Clock**](modules/default/clock)
|
||||
- [**Calendar**](modules/default/calendar)
|
||||
- [**Current Weather**](modules/default/currentweather)
|
||||
- [**Weather Forecast**](modules/default/weatherforecast)
|
||||
- [**News Feed**](modules/default/newsfeed)
|
||||
- [**Compliments**](modules/default/compliments)
|
||||
- [**Hello World**](modules/default/helloworld)
|
||||
- [**Alert**](modules/default/alert)
|
||||
|
||||
###[Calendar](js/calendar)
|
||||
For more available modules, check out out the wiki page: [MagicMirror² Modules](https://github.com/MichMich/MagicMirror/wiki/MagicMirror²-Modules). If you want to build your own modules, check out the [MagicMirror² Module Development Documentation](modules) and don't forget to add it to the wiki and the [forum](https://forum.magicmirror.builders/category/7/showcase)!
|
||||
|
||||
Parsing functionality for the calendar that retrieves and updates the calendar based on the interval set at the top of the [calendar.js](js/calendar/calendar.js) file. This was actually a straight pull from the original main.js file but the parsing code may deserve an upgrade.
|
||||
## Known issues
|
||||
|
||||
###[Compliments](js/compliments)
|
||||
- Electron seems to have some issues on certain Raspberry Pi 2's. See [#145](https://github.com/MichMich/MagicMirror/issues/145).
|
||||
- MagicMirror² (Electron) sometimes quits without an error after an extended period of use. See [#150](https://github.com/MichMich/MagicMirror/issues/150).
|
||||
|
||||
Functionality related to inserting compliments into the view and rotating them based on a specific interval set at the top of the [compliments.js](js/compliments/compliments.js) file.
|
||||
## Community
|
||||
|
||||
###[News](js/news)
|
||||
The community around the MagicMirror² is constantly growing. We even have a [forum](https://forum.magicmirror.builders) now where you can share your ideas, ask questions, help others and get inspired by other builders. We would love to see you there!
|
||||
|
||||
Takes an array of news feeds (or a single string) from the config file and retrieves each one so that it can be displayed in a loop based on the interval set at the top of the [news.js](js/news/news.js) file.
|
||||
## Contributing Guidelines
|
||||
|
||||
###[Time](js/time)
|
||||
Contributions of all kinds are welcome, not only in the form of code but also with regards bug reports and documentation.
|
||||
|
||||
Updates the time on the screen on one second interval. Can be changed to omit displaying seconds by adding the config option ```displaySeconds = false``` in [config.js](js/config.js). When the seconds are disabled the interval is set to 60 seconds on the full minute.
|
||||
Please keep the following in mind:
|
||||
|
||||
With the option ```digitFade = true```, changing digits are faded. This looks best if the seconds are omitted.
|
||||
- **Bug Reports**: Make sure you're running the latest version. If the issue(s) still persist: please open a clearly documented issue with a clear title.
|
||||
- **Minor Bug Fixes**: Please send a pull request with a clear explanation of the issue or a link to the issue it solves.
|
||||
- **Major Bug Fixes**: please discuss your approach in an GitHub issue before you start to alter a big part of the code.
|
||||
- **New Features**: please please discuss in a GitHub issue before you start to alter a big part of the code. Without discussion upfront, the pull request will not be accepted / merged.
|
||||
|
||||
###[Version](js/version)
|
||||
Thanks for your help in making MagicMirror² better!
|
||||
|
||||
Checks the git version and refreshes if a new version has been pulled.
|
||||
|
||||
###[Weather](js/weather)
|
||||
|
||||
Takes the user's inserted location, language, unit type, and OpenWeatherMap API key and grabs the five day weather forecast from OpenWeatherMap. You need to set the API key in the config for this to work. (See *configuration*.)
|
||||
|
||||
##Modules
|
||||
|
||||
###[MagicMirror-Modules by PaViRo](https://github.com/paviro/MagicMirror-Modules)
|
||||
|
||||
**Current features:** FRITZ!Box Callmonitor <br>
|
||||
**Future features:** Faceregognition, personalized views, online banking through HBCI and multiple calenders based on faceregognition.
|
||||
|
||||
###[MagicMirror-Netatmo-Module by cfenner](https://github.com/CFenner/MagicMirror-Netatmo-Module)
|
||||
|
||||
**Current features:** display data of Netatmo weather station (inside/outside)<br>
|
||||
**Future features:** display of local warnings (severe weather)
|
||||
|
2
config/.gitignore
vendored
Normal file
2
config/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!config.js.sample
|
77
config/config.js.sample
Normal file
77
config/config.js.sample
Normal file
@@ -0,0 +1,77 @@
|
||||
/* Magic Mirror Config Sample
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
var config = {
|
||||
port: 8080,
|
||||
|
||||
language: 'en',
|
||||
timeFormat: 24,
|
||||
units: 'metric',
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: 'alert',
|
||||
},
|
||||
{
|
||||
module: 'clock',
|
||||
position: 'top_left'
|
||||
},
|
||||
{
|
||||
module: 'calendar',
|
||||
header: 'US Holidays',
|
||||
position: 'top_left',
|
||||
config: {
|
||||
calendars: [
|
||||
{
|
||||
symbol: 'calendar-check-o ',
|
||||
url: 'webcal://www.calendarlabs.com/templates/ical/US-Holidays.ics'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
module: 'compliments',
|
||||
position: 'lower_third'
|
||||
},
|
||||
{
|
||||
module: 'currentweather',
|
||||
position: 'top_right',
|
||||
config: {
|
||||
location: 'New York',
|
||||
locationID: '', //ID from bulk.openweather.org/sample/
|
||||
appid: 'YOUR_OPENWEATHER_API_KEY'
|
||||
}
|
||||
},
|
||||
{
|
||||
module: 'weatherforecast',
|
||||
position: 'top_right',
|
||||
header: 'Weather Forecast',
|
||||
config: {
|
||||
location: 'New York',
|
||||
locationID: '5128581', //ID from bulk.openweather.org/sample/
|
||||
appid: 'YOUR_OPENWEATHER_API_KEY'
|
||||
}
|
||||
},
|
||||
{
|
||||
module: 'newsfeed',
|
||||
position: 'bottom_bar',
|
||||
config: {
|
||||
feeds: [
|
||||
{
|
||||
title: "New York Times",
|
||||
url: "http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml"
|
||||
}
|
||||
],
|
||||
showSourceTitle: true,
|
||||
showPublishDate: true
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== 'undefined') {module.exports = config;}
|
@@ -1,5 +0,0 @@
|
||||
<?php
|
||||
include "functions/gzip.php";
|
||||
$url = $_GET["url"];
|
||||
echo get_url($url);
|
||||
?>
|
@@ -1,42 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* @function get_url
|
||||
* @purpose To fetch GZipped web content.
|
||||
* @author Michael Teeuw
|
||||
*/
|
||||
function get_url($url) {
|
||||
/*
|
||||
* @array
|
||||
* Prepare the options that we need for our GZip request.
|
||||
*/
|
||||
$opts = array(
|
||||
"http" => array(
|
||||
"method" => "GET",
|
||||
"header" => "Accept-Language: en-US,en;q=0.8rn" . "Accept-Encoding: gzip,deflate,sdchrn" . "Accept-Charset:UTF-8,*;q=0.5rn" . "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:19.0) Gecko/20100101 Firefox/19.0 FirePHP/0.4rn",
|
||||
"ignore_errors" => true
|
||||
),
|
||||
/*
|
||||
* @array
|
||||
* Put a Band-Aid over some SSL issues.
|
||||
*/
|
||||
"ssl" => array(
|
||||
"verify_peer" => false,
|
||||
"verify_peer_name" => false
|
||||
)
|
||||
);
|
||||
$context = stream_context_create($opts);
|
||||
$content = file_get_contents($url, false, $context);
|
||||
/*
|
||||
* @note If http response header mentions that content is gzipped, then uncompress it.
|
||||
*/
|
||||
foreach($http_response_header as $c => $h) {
|
||||
if(stristr($h, "content-encoding") and stristr($h, "gzip")) {
|
||||
/*
|
||||
* @note Now, let's begin the actual purpose of this function:
|
||||
*/
|
||||
$content = gzinflate(substr($content, 10, -8));
|
||||
}
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
?>
|
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
echo json_encode(
|
||||
array(
|
||||
"gitHash" => trim(`git rev-parse HEAD`)
|
||||
)
|
||||
);
|
||||
?>
|
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
$modules_folder = 'modules/';
|
||||
$modules = array_filter(glob($modules_folder."*"), 'is_dir');
|
||||
foreach ($modules as $module) {
|
||||
//Add container arround module
|
||||
print_r( '<div id="'.substr($module, strlen($modules_folder)).'">' );
|
||||
|
||||
//Load files to include
|
||||
$include_files = include($module."/include.php");
|
||||
//Add Javascript files
|
||||
foreach ($include_files["js_files"] as $file) {
|
||||
//Check if js file is hosted on a remote server
|
||||
if (preg_match('#^https?://#i', $file) === 1) {
|
||||
print_r('<script src="'.$file.'"></script>'."\xA");
|
||||
}
|
||||
//add local path to module folder
|
||||
else{
|
||||
print_r('<script src="modules/'.$module.'/'.$file.'"></script>'."\xA");
|
||||
}
|
||||
};
|
||||
//Add CSS files
|
||||
foreach ($include_files["css_files"] as $file) {
|
||||
//Check if css file is hosted on a remote server
|
||||
if (preg_match('#^https?://#i', $file) === 1) {
|
||||
print_r('<link rel="stylesheet" type="text/css" href="'.$file.'">'."\xA");
|
||||
}
|
||||
//add local path to module folder
|
||||
else{
|
||||
print_r('<link rel="stylesheet" type="text/css" href="/modules/'.$module.'/'.$file.'">'."\xA");
|
||||
}
|
||||
};
|
||||
|
||||
//Add the modules JS file
|
||||
print_r('<script src="'.$module.'/main.js" type="text/javascript"></script>'."\xA");
|
||||
//Add the modules CSS file
|
||||
print_r('<link rel="stylesheet" type="text/css" href="'.$module.'/style.css">'."\xA");
|
||||
//Get and add HTML Elements
|
||||
print_r(str_replace("[module]",$module ,file_get_contents($module.'/elements.html')));
|
||||
|
||||
//Close module container
|
||||
print_r("</div>");
|
||||
}
|
||||
?>
|
14
css/custom.css
Normal file
14
css/custom.css
Normal file
@@ -0,0 +1,14 @@
|
||||
/*****************************************************
|
||||
* Magic Mirror *
|
||||
* Custom CSS *
|
||||
* *
|
||||
* By Michael Teeuw http://michaelteeuw.nl *
|
||||
* MIT Licensed. *
|
||||
* *
|
||||
* Add any custom CSS below. *
|
||||
* Changes to this files will be ignored by GIT. *
|
||||
*****************************************************/
|
||||
|
||||
body {
|
||||
|
||||
}
|
392
css/main.css
392
css/main.css
@@ -1,231 +1,229 @@
|
||||
body,
|
||||
html {
|
||||
cursor: none;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 60px;
|
||||
position: absolute;
|
||||
height: calc(100% - 120px);
|
||||
width: calc(100% - 120px);
|
||||
background: #000;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-family: "HelveticaNeue-Light", sans-serif;
|
||||
letter-spacing: -2px;
|
||||
color: #fff;
|
||||
font-size: 75px;
|
||||
color: #aaa;
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
font-weight: 400;
|
||||
font-size: 2em;
|
||||
line-height: 1.5em;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: geometricprecision;
|
||||
}
|
||||
|
||||
.wi {
|
||||
line-height: 75px;
|
||||
/**
|
||||
* Default styles.
|
||||
*/
|
||||
|
||||
.dimmed {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.top {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
.normal {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.left {
|
||||
position: absolute;
|
||||
left: 50px;
|
||||
}
|
||||
|
||||
.right {
|
||||
position: absolute;
|
||||
right: 50px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.center-ver {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
height: 200px;
|
||||
margin-top: -100px;
|
||||
line-height: 100px;
|
||||
}
|
||||
|
||||
.lower-third {
|
||||
position: absolute;
|
||||
top: 66.666%;
|
||||
height: 200px;
|
||||
margin-top: -100px;
|
||||
line-height: 100px;
|
||||
}
|
||||
|
||||
.center-hor {
|
||||
position: absolute;
|
||||
right: 50px;
|
||||
left: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
}
|
||||
|
||||
.xxsmall,
|
||||
.xsmall,
|
||||
.small {
|
||||
font-family: "HelveticaNeue-Medium", sans-serif;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.xxsmall {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.xxsmall .wi {
|
||||
line-height: 15px;
|
||||
.bright {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.xsmall {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.xsmall .wi {
|
||||
font-size: 15px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.small .wi {
|
||||
font-size: 20px;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
.medium {
|
||||
font-size: 35px;
|
||||
letter-spacing: -1px;
|
||||
font-family: "HelveticaNeue-Light", sans-serif;
|
||||
}
|
||||
|
||||
.medium .wi {
|
||||
font-size: 30px;
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
.xdimmed {
|
||||
color: #666;
|
||||
.large {
|
||||
font-size: 65px;
|
||||
line-height: 65px;
|
||||
}
|
||||
|
||||
.dimmed {
|
||||
color: #aaa;
|
||||
.xlarge {
|
||||
font-size: 75px;
|
||||
line-height: 75px;
|
||||
letter-spacing: -3px;
|
||||
}
|
||||
|
||||
.thin {
|
||||
font-family: Roboto, sans-serif;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
.light {
|
||||
font-family: "HelveticaNeue-UltraLight", sans-serif;
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: relative;
|
||||
top: -10px;
|
||||
display: inline-block;
|
||||
font-size: 45px;
|
||||
padding-right: 5px;
|
||||
font-weight: 100;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.icon-small {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
padding-left: 10px;
|
||||
padding-right: -10px;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
.time .sec {
|
||||
font-size: 25px;
|
||||
color: #666;
|
||||
padding-left: 5px;
|
||||
position: relative;
|
||||
top: -35px;
|
||||
}
|
||||
|
||||
.forecast-table {
|
||||
float: right;
|
||||
text-align: right;
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.forecast-table .day,
|
||||
.forecast-table .temp-min,
|
||||
.forecast-table .temp-max {
|
||||
width: 50px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.forecast-table .temp-max {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.forecast-table .day {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.calendar-table {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.calendar-table .calendar-icon {
|
||||
width: 1em;
|
||||
min-width: 1em;
|
||||
margin-right: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.calendar-table .days {
|
||||
padding-left: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.dishwasher {
|
||||
background-color: white;
|
||||
color: black;
|
||||
margin: 0 200px;
|
||||
font-size: 60px;
|
||||
border-radius: 1000px;
|
||||
border-radius: 1200px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'HelveticaNeue-UltraLight';
|
||||
src: url('font/HelveticaNeue-UltraLight.eot');
|
||||
/* IE9 Compat Modes */
|
||||
src: url('font/HelveticaNeue-UltraLight.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||
url('font/HelveticaNeue-UltraLight.woff') format('woff'), /* Modern Browsers */
|
||||
url('font/HelveticaNeue-UltraLight.ttf') format('truetype'), /* Safari, Android, iOS */
|
||||
url('font/HelveticaNeue-UltraLight.svg#9453ea8da727d260bcdbfa605bdbb5d2') format('svg');
|
||||
/* Legacy iOS */
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'HelveticaNeue-Medium';
|
||||
src: url('font/HelveticaNeue-Medium.eot');
|
||||
/* IE9 Compat Modes */
|
||||
src: url('font/HelveticaNeue-Medium.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||
url('font/HelveticaNeue-Medium.woff') format('woff'), /* Modern Browsers */
|
||||
url('font/HelveticaNeue-Medium.ttf') format('truetype'), /* Safari, Android, iOS */
|
||||
url('font/HelveticaNeue-Medium.svg#d7af0fd9278f330eed98b60dddea7bd6') format('svg');
|
||||
/* Legacy iOS */
|
||||
font-style: normal;
|
||||
.regular {
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'HelveticaNeue-Light';
|
||||
src: url('font/HelveticaNeue-Light.eot');
|
||||
/* IE9 Compat Modes */
|
||||
src: url('font/HelveticaNeue-Light.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||
url('font/HelveticaNeue-Light.woff') format('woff'), /* Modern Browsers */
|
||||
url('font/HelveticaNeue-Light.ttf') format('truetype'), /* Safari, Android, iOS */
|
||||
url('font/HelveticaNeue-Light.svg#7384ecabcada72f0e077cd45d8e1c705') format('svg');
|
||||
/* Legacy iOS */
|
||||
font-style: normal;
|
||||
font-weight: 200;
|
||||
.bold {
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
header {
|
||||
text-transform: uppercase;
|
||||
font-size: 15px;
|
||||
font-family: "Roboto Condensed";
|
||||
font-weight: 400;
|
||||
border-bottom: 1px solid #666;
|
||||
line-height: 15px;
|
||||
padding-bottom: 5px;
|
||||
margin-bottom: 10px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
sup {
|
||||
font-size: 50%;
|
||||
line-height: 50%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Module styles.
|
||||
*/
|
||||
|
||||
.module {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.module:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Region Definitions.
|
||||
*/
|
||||
|
||||
.region {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.region.fullscreen {
|
||||
position: absolute;
|
||||
top: -60px;
|
||||
left: -60px;
|
||||
right: -60px;
|
||||
bottom: -60px;
|
||||
}
|
||||
|
||||
.region.right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.region.top {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.region.top .container {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.region.top .container:empty {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.region.top.center,
|
||||
.region.bottom.center {
|
||||
left: 50%;
|
||||
-moz-transform: translateX(-50%);
|
||||
-o-transform: translateX(-50%);
|
||||
-webkit-transform: translateX(-50%);
|
||||
-ms-transform: translateX(-50%);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.region.top.right,
|
||||
.region.top.left,
|
||||
.region.top.center {
|
||||
top: 100%;
|
||||
}
|
||||
|
||||
.region.bottom {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.region.bottom .container {
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.region.bottom .container:empty {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.region.bottom.right,
|
||||
.region.bottom.center,
|
||||
.region.bottom.left {
|
||||
bottom: 100%;
|
||||
}
|
||||
|
||||
.region.bar {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.region.third,
|
||||
.region.middle.center {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
-moz-transform: translateY(-50%);
|
||||
-o-transform: translateY(-50%);
|
||||
-webkit-transform: translateY(-50%);
|
||||
-ms-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.region.upper.third {
|
||||
top: 33%;
|
||||
}
|
||||
|
||||
.region.middle.center {
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.region.lower.third {
|
||||
top: 66%;
|
||||
}
|
||||
|
||||
.region.left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.region.right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.region table {
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
border-collapse: separate;
|
||||
}
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 338 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 383 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 401 KiB |
Binary file not shown.
Binary file not shown.
202
fonts/LICENSE.txt
Normal file
202
fonts/LICENSE.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
BIN
fonts/Roboto-Black/Roboto-Black.ttf
Normal file
BIN
fonts/Roboto-Black/Roboto-Black.ttf
Normal file
Binary file not shown.
BIN
fonts/Roboto-Black/Roboto-Black.woff
Normal file
BIN
fonts/Roboto-Black/Roboto-Black.woff
Normal file
Binary file not shown.
BIN
fonts/Roboto-Black/Roboto-Black.woff2
Normal file
BIN
fonts/Roboto-Black/Roboto-Black.woff2
Normal file
Binary file not shown.
BIN
fonts/Roboto-BlackItalic/Roboto-BlackItalic.ttf
Normal file
BIN
fonts/Roboto-BlackItalic/Roboto-BlackItalic.ttf
Normal file
Binary file not shown.
BIN
fonts/Roboto-BlackItalic/Roboto-BlackItalic.woff
Normal file
BIN
fonts/Roboto-BlackItalic/Roboto-BlackItalic.woff
Normal file
Binary file not shown.
BIN
fonts/Roboto-BlackItalic/Roboto-BlackItalic.woff2
Normal file
BIN
fonts/Roboto-BlackItalic/Roboto-BlackItalic.woff2
Normal file
Binary file not shown.
BIN
fonts/Roboto-Bold/Roboto-Bold.ttf
Normal file
BIN
fonts/Roboto-Bold/Roboto-Bold.ttf
Normal file
Binary file not shown.
BIN
fonts/Roboto-Bold/Roboto-Bold.woff
Normal file
BIN
fonts/Roboto-Bold/Roboto-Bold.woff
Normal file
Binary file not shown.
BIN
fonts/Roboto-Bold/Roboto-Bold.woff2
Normal file
BIN
fonts/Roboto-Bold/Roboto-Bold.woff2
Normal file
Binary file not shown.
BIN
fonts/Roboto-BoldItalic/Roboto-BoldItalic.ttf
Normal file
BIN
fonts/Roboto-BoldItalic/Roboto-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
fonts/Roboto-BoldItalic/Roboto-BoldItalic.woff
Normal file
BIN
fonts/Roboto-BoldItalic/Roboto-BoldItalic.woff
Normal file
Binary file not shown.
BIN
fonts/Roboto-BoldItalic/Roboto-BoldItalic.woff2
Normal file
BIN
fonts/Roboto-BoldItalic/Roboto-BoldItalic.woff2
Normal file
Binary file not shown.
BIN
fonts/Roboto-Italic/Roboto-Italic.ttf
Normal file
BIN
fonts/Roboto-Italic/Roboto-Italic.ttf
Normal file
Binary file not shown.
BIN
fonts/Roboto-Italic/Roboto-Italic.woff
Normal file
BIN
fonts/Roboto-Italic/Roboto-Italic.woff
Normal file
Binary file not shown.
BIN
fonts/Roboto-Italic/Roboto-Italic.woff2
Normal file
BIN
fonts/Roboto-Italic/Roboto-Italic.woff2
Normal file
Binary file not shown.
BIN
fonts/Roboto-Light/Roboto-Light.ttf
Normal file
BIN
fonts/Roboto-Light/Roboto-Light.ttf
Normal file
Binary file not shown.
BIN
fonts/Roboto-Light/Roboto-Light.woff
Normal file
BIN
fonts/Roboto-Light/Roboto-Light.woff
Normal file
Binary file not shown.
BIN
fonts/Roboto-Light/Roboto-Light.woff2
Normal file
BIN
fonts/Roboto-Light/Roboto-Light.woff2
Normal file
Binary file not shown.
BIN
fonts/Roboto-LightItalic/Roboto-LightItalic.ttf
Normal file
BIN
fonts/Roboto-LightItalic/Roboto-LightItalic.ttf
Normal file
Binary file not shown.
BIN
fonts/Roboto-LightItalic/Roboto-LightItalic.woff
Normal file
BIN
fonts/Roboto-LightItalic/Roboto-LightItalic.woff
Normal file
Binary file not shown.
BIN
fonts/Roboto-LightItalic/Roboto-LightItalic.woff2
Normal file
BIN
fonts/Roboto-LightItalic/Roboto-LightItalic.woff2
Normal file
Binary file not shown.
BIN
fonts/Roboto-Medium/Roboto-Medium.ttf
Normal file
BIN
fonts/Roboto-Medium/Roboto-Medium.ttf
Normal file
Binary file not shown.
BIN
fonts/Roboto-Medium/Roboto-Medium.woff
Normal file
BIN
fonts/Roboto-Medium/Roboto-Medium.woff
Normal file
Binary file not shown.
BIN
fonts/Roboto-Medium/Roboto-Medium.woff2
Normal file
BIN
fonts/Roboto-Medium/Roboto-Medium.woff2
Normal file
Binary file not shown.
BIN
fonts/Roboto-MediumItalic/Roboto-MediumItalic.ttf
Normal file
BIN
fonts/Roboto-MediumItalic/Roboto-MediumItalic.ttf
Normal file
Binary file not shown.
BIN
fonts/Roboto-MediumItalic/Roboto-MediumItalic.woff
Normal file
BIN
fonts/Roboto-MediumItalic/Roboto-MediumItalic.woff
Normal file
Binary file not shown.
BIN
fonts/Roboto-MediumItalic/Roboto-MediumItalic.woff2
Normal file
BIN
fonts/Roboto-MediumItalic/Roboto-MediumItalic.woff2
Normal file
Binary file not shown.
BIN
fonts/Roboto-Regular/Roboto-Regular.ttf
Normal file
BIN
fonts/Roboto-Regular/Roboto-Regular.ttf
Normal file
Binary file not shown.
BIN
fonts/Roboto-Regular/Roboto-Regular.woff
Normal file
BIN
fonts/Roboto-Regular/Roboto-Regular.woff
Normal file
Binary file not shown.
BIN
fonts/Roboto-Regular/Roboto-Regular.woff2
Normal file
BIN
fonts/Roboto-Regular/Roboto-Regular.woff2
Normal file
Binary file not shown.
BIN
fonts/Roboto-Thin/Roboto-Thin.ttf
Normal file
BIN
fonts/Roboto-Thin/Roboto-Thin.ttf
Normal file
Binary file not shown.
BIN
fonts/Roboto-Thin/Roboto-Thin.woff
Normal file
BIN
fonts/Roboto-Thin/Roboto-Thin.woff
Normal file
Binary file not shown.
BIN
fonts/Roboto-Thin/Roboto-Thin.woff2
Normal file
BIN
fonts/Roboto-Thin/Roboto-Thin.woff2
Normal file
Binary file not shown.
BIN
fonts/Roboto-ThinItalic/Roboto-ThinItalic.ttf
Normal file
BIN
fonts/Roboto-ThinItalic/Roboto-ThinItalic.ttf
Normal file
Binary file not shown.
BIN
fonts/Roboto-ThinItalic/Roboto-ThinItalic.woff
Normal file
BIN
fonts/Roboto-ThinItalic/Roboto-ThinItalic.woff
Normal file
Binary file not shown.
BIN
fonts/Roboto-ThinItalic/Roboto-ThinItalic.woff2
Normal file
BIN
fonts/Roboto-ThinItalic/Roboto-ThinItalic.woff2
Normal file
Binary file not shown.
BIN
fonts/RobotoCondensed-Bold/RobotoCondensed-Bold.ttf
Normal file
BIN
fonts/RobotoCondensed-Bold/RobotoCondensed-Bold.ttf
Normal file
Binary file not shown.
BIN
fonts/RobotoCondensed-Bold/RobotoCondensed-Bold.woff
Normal file
BIN
fonts/RobotoCondensed-Bold/RobotoCondensed-Bold.woff
Normal file
Binary file not shown.
BIN
fonts/RobotoCondensed-Bold/RobotoCondensed-Bold.woff2
Normal file
BIN
fonts/RobotoCondensed-Bold/RobotoCondensed-Bold.woff2
Normal file
Binary file not shown.
BIN
fonts/RobotoCondensed-BoldItalic/RobotoCondensed-BoldItalic.ttf
Normal file
BIN
fonts/RobotoCondensed-BoldItalic/RobotoCondensed-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
fonts/RobotoCondensed-BoldItalic/RobotoCondensed-BoldItalic.woff
Normal file
BIN
fonts/RobotoCondensed-BoldItalic/RobotoCondensed-BoldItalic.woff
Normal file
Binary file not shown.
Binary file not shown.
BIN
fonts/RobotoCondensed-Italic/RobotoCondensed-Italic.ttf
Normal file
BIN
fonts/RobotoCondensed-Italic/RobotoCondensed-Italic.ttf
Normal file
Binary file not shown.
BIN
fonts/RobotoCondensed-Italic/RobotoCondensed-Italic.woff
Normal file
BIN
fonts/RobotoCondensed-Italic/RobotoCondensed-Italic.woff
Normal file
Binary file not shown.
BIN
fonts/RobotoCondensed-Italic/RobotoCondensed-Italic.woff2
Normal file
BIN
fonts/RobotoCondensed-Italic/RobotoCondensed-Italic.woff2
Normal file
Binary file not shown.
BIN
fonts/RobotoCondensed-Light/RobotoCondensed-Light.ttf
Normal file
BIN
fonts/RobotoCondensed-Light/RobotoCondensed-Light.ttf
Normal file
Binary file not shown.
BIN
fonts/RobotoCondensed-Light/RobotoCondensed-Light.woff
Normal file
BIN
fonts/RobotoCondensed-Light/RobotoCondensed-Light.woff
Normal file
Binary file not shown.
BIN
fonts/RobotoCondensed-Light/RobotoCondensed-Light.woff2
Normal file
BIN
fonts/RobotoCondensed-Light/RobotoCondensed-Light.woff2
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
fonts/RobotoCondensed-Regular/RobotoCondensed-Regular.ttf
Normal file
BIN
fonts/RobotoCondensed-Regular/RobotoCondensed-Regular.ttf
Normal file
Binary file not shown.
BIN
fonts/RobotoCondensed-Regular/RobotoCondensed-Regular.woff
Normal file
BIN
fonts/RobotoCondensed-Regular/RobotoCondensed-Regular.woff
Normal file
Binary file not shown.
BIN
fonts/RobotoCondensed-Regular/RobotoCondensed-Regular.woff2
Normal file
BIN
fonts/RobotoCondensed-Regular/RobotoCondensed-Regular.woff2
Normal file
Binary file not shown.
95
fonts/roboto.css
Normal file
95
fonts/roboto.css
Normal file
@@ -0,0 +1,95 @@
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
src:
|
||||
local("Roboto Thin"),
|
||||
local("Roboto-Thin"),
|
||||
url("Roboto-Thin/Roboto-Thin.woff2") format("woff2"),
|
||||
url("Roboto-Thin/Roboto-Thin.woff") format("woff"),
|
||||
url("Roboto-Thin/Roboto-Thin.ttf") format("truetype");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Roboto Condensed";
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src:
|
||||
local("Roboto Condensed Light"),
|
||||
local("RobotoCondensed-Light"),
|
||||
url("RobotoCondensed-Light/RobotoCondensed-Light.woff2") format("woff2"),
|
||||
url("RobotoCondensed-Light/RobotoCondensed-Light.woff") format("woff"),
|
||||
url("RobotoCondensed-Light/RobotoCondensed-Light.ttf") format("truetype");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Roboto Condensed";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src:
|
||||
local("Roboto Condensed"),
|
||||
local("RobotoCondensed-Regular"),
|
||||
url("RobotoCondensed-Regular/RobotoCondensed-Regular.woff2") format("woff2"),
|
||||
url("RobotoCondensed-Regular/RobotoCondensed-Regular.woff") format("woff"),
|
||||
url("RobotoCondensed-Regular/RobotoCondensed-Regular.ttf") format("truetype");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Roboto Condensed";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src:
|
||||
local("Roboto Condensed Bold"),
|
||||
local("RobotoCondensed-Bold"),
|
||||
url("RobotoCondensed-Bold/RobotoCondensed-Bold.woff2") format("woff2"),
|
||||
url("RobotoCondensed-Bold/RobotoCondensed-Bold.woff") format("woff"),
|
||||
url("RobotoCondensed-Bold/RobotoCondensed-Bold.ttf") format("truetype");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src:
|
||||
local("Roboto"),
|
||||
local("Roboto-Regular"),
|
||||
url("Roboto-Regular/Roboto-Regular.woff2") format("woff2"),
|
||||
url("Roboto-Regular/Roboto-Regular.woff") format("woff"),
|
||||
url("Roboto-Regular/Roboto-Regular.ttf") format("truetype");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src:
|
||||
local("Roboto Medium"),
|
||||
local("Roboto-Medium"),
|
||||
url("Roboto-Medium/Roboto-Medium.woff2") format("woff2"),
|
||||
url("Roboto-Medium/Roboto-Medium.woff") format("woff"),
|
||||
url("Roboto-Medium/Roboto-Medium.ttf") format("truetype");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src:
|
||||
local("Roboto Bold"),
|
||||
local("Roboto-Bold"),
|
||||
url("Roboto-Bold/Roboto-Bold.woff2") format("woff2"),
|
||||
url("Roboto-Bold/Roboto-Bold.woff") format("woff"),
|
||||
url("Roboto-Bold/Roboto-Bold.ttf") format("truetype");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src:
|
||||
local("Roboto Light"),
|
||||
local("Roboto-Light"),
|
||||
url("Roboto-Light/Roboto-Light.woff2") format("woff2"),
|
||||
url("Roboto-Light/Roboto-Light.woff") format("woff"),
|
||||
url("Roboto-Light/Roboto-Light.ttf") format("truetype");
|
||||
}
|
44
index.html
Normal file
44
index.html
Normal file
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Magic Mirror</title>
|
||||
<meta name="google" content="notranslate" />
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
|
||||
<link rel="stylesheet" type="text/css" href="css/main.css">
|
||||
<link rel="stylesheet" type="text/css" href="fonts/roboto.css">
|
||||
<!-- custom.css is loaded by the loader.js to make sure it's loaded after the module css files. -->
|
||||
</head>
|
||||
<body>
|
||||
<div class="region fullscreen below"><div class="container"></div></div>
|
||||
<div class="region top bar">
|
||||
<div class="container"></div>
|
||||
<div class="region top left"><div class="container"></div></div>
|
||||
<div class="region top center"><div class="container"></div></div>
|
||||
<div class="region top right"><div class="container"></div></div>
|
||||
</div>
|
||||
<div class="region upper third"><div class="container"></div></div>
|
||||
<div class="region middle center"><div class="container"></div></div>
|
||||
<div class="region lower third"><div class="container"><br/></div></div>
|
||||
<div class="region bottom bar">
|
||||
<div class="container"></div>
|
||||
<div class="region bottom left"><div class="container"></div></div>
|
||||
<div class="region bottom center"><div class="container"></div></div>
|
||||
<div class="region bottom right"><div class="container"></div></div>
|
||||
</div>
|
||||
<div class="region fullscreen above"><div class="container"></div></div>
|
||||
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
|
||||
<script type="text/javascript" src="js/defaults.js"></script>
|
||||
<script type="text/javascript" src="config/config.js"></script>
|
||||
<script type="text/javascript" src="vendor/vendor.js"></script>
|
||||
<script type="text/javascript" src="modules/default/defaultmodules.js"></script>
|
||||
<script type="text/javascript" src="js/logger.js"></script>
|
||||
<script type="text/javascript" src="translations/translations.js"></script>
|
||||
<script type="text/javascript" src="js/translator.js"></script>
|
||||
<script type="text/javascript" src="js/class.js"></script>
|
||||
<script type="text/javascript" src="js/module.js"></script>
|
||||
<script type="text/javascript" src="js/loader.js"></script>
|
||||
<script type="text/javascript" src="js/socketclient.js"></script>
|
||||
<script type="text/javascript" src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
42
index.php
42
index.php
@@ -1,42 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Magic Mirror</title>
|
||||
<style type="text/css">
|
||||
<?php include('css/main.css') ?>
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="css/weather-icons.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/font-awesome.css">
|
||||
<script type="text/javascript">
|
||||
var gitHash = '<?php echo trim(`git rev-parse HEAD`) ?>';
|
||||
</script>
|
||||
<meta name="google" value="notranslate" />
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="top right"><div class="windsun small dimmed"></div><div class="temp"></div><div class="forecast small dimmed"></div></div>
|
||||
<div class="top left"><div class="date small dimmed"></div><div class="time" id="time"></div><div class="calendar xxsmall"></div></div>
|
||||
<div class="center-ver center-hor"><!-- <div class="dishwasher light">Vaatwasser is klaar!</div> --></div>
|
||||
<div class="lower-third center-hor"><div class="compliment light"></div></div>
|
||||
<div class="bottom center-hor"><div class="news medium"></div></div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="js/jquery.js"></script>
|
||||
<script src="js/jquery.feedToJSON.js"></script>
|
||||
<script src="js/ical_parser.js"></script>
|
||||
<script src="js/moment-with-locales.min.js"></script>
|
||||
<script src="js/config.js"></script>
|
||||
<script src="js/rrule.js"></script>
|
||||
<script src="js/version/version.js"></script>
|
||||
<script src="js/calendar/calendar.js"></script>
|
||||
<script src="js/compliments/compliments.js"></script>
|
||||
<script src="js/weather/weather.js"></script>
|
||||
<script src="js/time/time.js"></script>
|
||||
<script src="js/news/news.js"></script>
|
||||
<script src="js/main.js?nocache=<?php echo md5(microtime()) ?>"></script>
|
||||
<!-- <script src="js/socket.io.min.js"></script> -->
|
||||
<?php include(dirname(__FILE__).'/controllers/modules.php');?>
|
||||
</body>
|
||||
</html>
|
118
installers/raspberry.sh
Normal file
118
installers/raspberry.sh
Normal file
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This is an installer script for MagicMirror2. It works well enough
|
||||
# that it can detect if you have Node installed, run a binary script
|
||||
# and then download and run MagicMirror2.
|
||||
|
||||
echo -e "\e[0m"
|
||||
echo '$$\ $$\ $$\ $$\ $$\ $$\ $$$$$$\'
|
||||
echo '$$$\ $$$ | \__| $$$\ $$$ |\__| $$ __$$\'
|
||||
echo '$$$$\ $$$$ | $$$$$$\ $$$$$$\ $$\ $$$$$$$\ $$$$\ $$$$ |$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ \__/ $$ |'
|
||||
echo '$$\$$\$$ $$ | \____$$\ $$ __$$\ $$ |$$ _____|$$\$$\$$ $$ |$$ |$$ __$$\ $$ __$$\ $$ __$$\ $$ __$$\ $$$$$$ |'
|
||||
echo '$$ \$$$ $$ | $$$$$$$ |$$ / $$ |$$ |$$ / $$ \$$$ $$ |$$ |$$ | \__|$$ | \__|$$ / $$ |$$ | \__|$$ ____/'
|
||||
echo '$$ |\$ /$$ |$$ __$$ |$$ | $$ |$$ |$$ | $$ |\$ /$$ |$$ |$$ | $$ | $$ | $$ |$$ | $$ |'
|
||||
echo '$$ | \_/ $$ |\$$$$$$$ |\$$$$$$$ |$$ |\$$$$$$$\ $$ | \_/ $$ |$$ |$$ | $$ | \$$$$$$ |$$ | $$$$$$$$\'
|
||||
echo '\__| \__| \_______| \____$$ |\__| \_______|\__| \__|\__|\__| \__| \______/ \__| \________|'
|
||||
echo ' $$\ $$ |'
|
||||
echo ' \$$$$$$ |'
|
||||
echo ' \______/'
|
||||
echo -e "\e[0m"
|
||||
|
||||
# Define the tested version of Node.js.
|
||||
NODE_TESTED="v5.1.0"
|
||||
|
||||
#Determine which Pi is running.
|
||||
ARM=$(uname -m)
|
||||
|
||||
#Check the Raspberry Pi version.
|
||||
if [ "$ARM" != "armv7l" ]; then
|
||||
echo -e "\e[91mSorry, your Raspberry Pi is not supported."
|
||||
echo -e "\e[91mPlease run MagicMirror on a Raspberry Pi 2 or 3."
|
||||
exit;
|
||||
fi
|
||||
|
||||
#define helper methods.
|
||||
function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; }
|
||||
function command_exists () { type "$1" &> /dev/null ;}
|
||||
|
||||
# Installing helper tools
|
||||
echo -e "\e[96mInstalling helper tools ...\e[90m"
|
||||
sudo apt-get install curl wget git build-essential unzip || exit
|
||||
|
||||
# Check if we need to install or upgrade Node.js.
|
||||
echo -e "\e[96mCheck current Node installation ...\e[0m"
|
||||
NODE_INSTALL=false
|
||||
if command_exists node; then
|
||||
echo -e "\e[0mNode currently installed. Checking version number.";
|
||||
NODE_CURRENT=$(node -v)
|
||||
echo -e "\e[0mMinimum Node version: \e[1m$NODE_TESTED\e[0m"
|
||||
echo -e "\e[0mInstalled Node version: \e[1m$NODE_CURRENT\e[0m"
|
||||
if version_gt $NODE_TESTED $NODE_CURRENT; then
|
||||
echo -e "\e[96mNode should be upgraded.\e[0m"
|
||||
NODE_INSTALL=true
|
||||
|
||||
#Check if a node process is currenlty running.
|
||||
#If so abort installation.
|
||||
if pgrep "node" > /dev/null; then
|
||||
echo -e "\e[91mA Node process is currently running. Can't upgrade."
|
||||
echo "Please quit all Node processes and restart the installer."
|
||||
exit;
|
||||
fi
|
||||
|
||||
else
|
||||
echo -e "\e[92mNo Node.js upgrade nessecery.\e[0m"
|
||||
fi
|
||||
|
||||
else
|
||||
echo -e "\e[93mNode.js is not installed.\e[0m";
|
||||
NODE_INSTALL=true
|
||||
fi
|
||||
|
||||
# Install or upgare node if nessecery.
|
||||
if $NODE_INSTALL; then
|
||||
|
||||
echo -e "\e[96mInstalling Node.js ...\e[90m"
|
||||
|
||||
#Fetch the latest version of Node.js from the selected branch
|
||||
#The NODE_STABLE_BRANCH variable will need to be manually adjusted when a new branch is released. (e.g. 7.x)
|
||||
#Only tested (stable) versions are recommended as newer versions could break MagicMirror.
|
||||
|
||||
NODE_STABLE_BRANCH="6.x"
|
||||
curl -sL https://deb.nodesource.com/setup_$NODE_STABLE_BRANCH | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
echo -e "\e[92mNode.js installation Done!\e[0m"
|
||||
fi
|
||||
|
||||
#Install magic mirror
|
||||
cd ~
|
||||
if [ -d "$HOME/MagicMirror" ] ; then
|
||||
echo -e "\e[93mIt seems like MagicMirror is already installed."
|
||||
echo -e "To prevent overwriting, the installer will be aborted."
|
||||
echo -e "Please rename the \e[1m~/MagicMirror\e[0m\e[93m folder and try again.\e[0m"
|
||||
echo ""
|
||||
echo -e "If you want to upgrade your installation run \e[1m\e[97mgit pull\e[0m from the ~/MagicMirror directory."
|
||||
echo ""
|
||||
exit;
|
||||
fi
|
||||
|
||||
echo -e "\e[96mCloning MagicMirror ...\e[90m"
|
||||
if git clone https://github.com/MichMich/MagicMirror.git; then
|
||||
echo -e "\e[92mCloning MagicMirror Done!\e[0m"
|
||||
else
|
||||
echo -e "\e[91mUnable to clone MagicMirror."
|
||||
exit;
|
||||
fi
|
||||
|
||||
cd ~/MagicMirror || exit
|
||||
echo -e "\e[96mInstalling dependencies ...\e[90m"
|
||||
if npm install; then
|
||||
echo -e "\e[92mDependencies installation Done!\e[0m"
|
||||
else
|
||||
echo -e "\e[91mUnable to install dependencies!"
|
||||
exit;
|
||||
fi
|
||||
|
||||
echo " "
|
||||
echo -e "\e[92mWe're ready! Run \e[1m\e[97mDISPLAY=:0 npm start\e[0m\e[92m from the ~/MagicMirror directory to start your MagicMirror.\e[0m"
|
||||
echo " "
|
||||
echo " "
|
142
js/app.js
Normal file
142
js/app.js
Normal file
@@ -0,0 +1,142 @@
|
||||
/* Magic Mirror
|
||||
* The Core App (Server)
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
var fs = require("fs");
|
||||
var Server = require(__dirname + "/server.js");
|
||||
var defaultModules = require(__dirname + "/../modules/default/defaultmodules.js");
|
||||
var path = require("path");
|
||||
|
||||
// The next part is here to prevent a major exception when there
|
||||
// is no internet connection. This could probable be solved better.
|
||||
process.on("uncaughtException", function (err) {
|
||||
console.log("Whoops! There was an uncaught exception...");
|
||||
console.error(err);
|
||||
console.log("MagicMirror will not quit, but it might be a good idea to check why this happened. Maybe no internet connection?");
|
||||
console.log("If you think this really is an issue, please open an issue on GitHub: https://github.com/MichMich/MagicMirror/issues");
|
||||
});
|
||||
|
||||
/* App - The core app.
|
||||
*/
|
||||
var App = function() {
|
||||
var nodeHelpers = [];
|
||||
|
||||
/* loadConfig(callback)
|
||||
* Loads the config file. combines it with the defaults,
|
||||
* and runs the callback with the found config as argument.
|
||||
*
|
||||
* argument callback function - The callback function.
|
||||
*/
|
||||
|
||||
var loadConfig = function(callback) {
|
||||
console.log("Loading config ...");
|
||||
var defaults = require(__dirname + "/defaults.js");
|
||||
var configFilename = path.resolve(__dirname + "/../config/config.js");
|
||||
try {
|
||||
fs.accessSync(configFilename, fs.F_OK);
|
||||
var c = require(configFilename);
|
||||
var config = Object.assign(defaults, c);
|
||||
callback(config);
|
||||
} catch (e) {
|
||||
console.error("WARNING! Could not find config. Please create one.");
|
||||
callback(defaults);
|
||||
}
|
||||
};
|
||||
|
||||
/* loadModule(module)
|
||||
* Loads a specific module.
|
||||
*
|
||||
* argument module string - The name of the module (including subpath).
|
||||
*/
|
||||
var loadModule = function(module) {
|
||||
|
||||
var elements = module.split("/");
|
||||
var moduleName = elements[elements.length - 1];
|
||||
var moduleFolder = __dirname + "/../modules/" + module;
|
||||
|
||||
if (defaultModules.indexOf(moduleName) !== -1) {
|
||||
moduleFolder = __dirname + "/../modules/default/" + module;
|
||||
}
|
||||
|
||||
var helperPath = moduleFolder + "/node_helper.js";
|
||||
|
||||
var loadModule = true;
|
||||
try {
|
||||
fs.accessSync(helperPath, fs.R_OK);
|
||||
} catch (e) {
|
||||
loadModule = false;
|
||||
console.log("No helper found for module: " + moduleName + ".");
|
||||
}
|
||||
|
||||
if (loadModule) {
|
||||
var Module = require(helperPath);
|
||||
var m = new Module();
|
||||
m.setName(moduleName);
|
||||
m.setPath(path.resolve(moduleFolder));
|
||||
nodeHelpers.push(m);
|
||||
}
|
||||
};
|
||||
|
||||
/* loadModules(modules)
|
||||
* Loads all modules.
|
||||
*
|
||||
* argument module string - The name of the module (including subpath).
|
||||
*/
|
||||
var loadModules = function(modules) {
|
||||
console.log("Loading module helpers ...");
|
||||
|
||||
for (var m in modules) {
|
||||
loadModule(modules[m]);
|
||||
}
|
||||
|
||||
console.log("All module helpers loaded.");
|
||||
};
|
||||
|
||||
/* start(callback)
|
||||
* This methods starts the core app.
|
||||
* It loads the config, then it loads all modules.
|
||||
* When it"s done it executs the callback with the config as argument.
|
||||
*
|
||||
* argument callback function - The callback function.
|
||||
*/
|
||||
this.start = function(callback) {
|
||||
|
||||
loadConfig(function(c) {
|
||||
config = c;
|
||||
|
||||
var modules = [];
|
||||
|
||||
for (var m in config.modules) {
|
||||
var module = config.modules[m];
|
||||
if (modules.indexOf(module.module) === -1) {
|
||||
modules.push(module.module);
|
||||
}
|
||||
}
|
||||
|
||||
loadModules(modules);
|
||||
|
||||
var server = new Server(config, function(app, io) {
|
||||
console.log("Server started ...");
|
||||
|
||||
for (var h in nodeHelpers) {
|
||||
var nodeHelper = nodeHelpers[h];
|
||||
nodeHelper.setExpressApp(app);
|
||||
nodeHelper.setSocketIO(io);
|
||||
nodeHelper.start();
|
||||
}
|
||||
|
||||
console.log("Sockets connected & modules started ...");
|
||||
|
||||
if (typeof callback === "function") {
|
||||
callback(config);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = new App();
|
@@ -1,197 +0,0 @@
|
||||
var calendar = {
|
||||
eventList: [],
|
||||
calendarLocation: '.calendar',
|
||||
updateInterval: 1000,
|
||||
updateDataInterval: 60000,
|
||||
fadeInterval: 1000,
|
||||
intervalId: null,
|
||||
dataIntervalId: null,
|
||||
maximumEntries: config.calendar.maximumEntries || 10,
|
||||
calendarUrl: (typeof config.calendar.urls == 'undefined') ? config.calendar.url : config.calendar.urls[0].url,
|
||||
calendarPos: 0,
|
||||
defaultSymbol: config.calendar.defaultSymbol || 'none',
|
||||
calendarSymbol: (typeof config.calendar.urls == 'undefined') ? config.calendar.defaultSymbol || 'none' : config.calendar.urls[0].symbol,
|
||||
displaySymbol: (typeof config.calendar.displaySymbol == 'undefined') ? false : config.calendar.displaySymbol,
|
||||
shortRunningText: 'still',
|
||||
longRunningText: 'until',
|
||||
}
|
||||
|
||||
calendar.processEvents = function (url, events) {
|
||||
tmpEventList = [];
|
||||
var eventListLength = this.eventList.length;
|
||||
for (var i = 0; i < eventListLength; i++) {
|
||||
if (this.eventList[i]['url'] != url) {
|
||||
tmpEventList.push(this.eventList[i]);
|
||||
}
|
||||
}
|
||||
this.eventList = tmpEventList;
|
||||
|
||||
for (var i in events) {
|
||||
|
||||
var e = events[i];
|
||||
for (var key in e) {
|
||||
var value = e[key];
|
||||
var seperator = key.search(';');
|
||||
if (seperator >= 0) {
|
||||
var mainKey = key.substring(0,seperator);
|
||||
var subKey = key.substring(seperator+1);
|
||||
|
||||
var dt;
|
||||
if (subKey == 'VALUE=DATE') {
|
||||
//date
|
||||
dt = new Date(value.substring(0,4), value.substring(4,6) - 1, value.substring(6,8));
|
||||
} else {
|
||||
//time
|
||||
dt = new Date(value.substring(0,4), value.substring(4,6) - 1, value.substring(6,8), value.substring(9,11), value.substring(11,13), value.substring(13,15));
|
||||
}
|
||||
|
||||
if (mainKey == 'DTSTART') e.startDate = dt;
|
||||
if (mainKey == 'DTEND') e.endDate = dt;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.startDate == undefined){
|
||||
//some old events in Gmail Calendar is "start_date"
|
||||
//FIXME: problems with Gmail's TimeZone
|
||||
var days = moment(e.DTSTART).diff(moment(), 'days');
|
||||
var seconds = moment(e.DTSTART).diff(moment(), 'seconds');
|
||||
var startDate = moment(e.DTSTART);
|
||||
var endDays = moment(e.DTEND).diff(moment(), 'days');
|
||||
var endSeconds = moment(e.DTEND).diff(moment(), 'seconds');
|
||||
var endDate = moment(e.DTEND);
|
||||
} else {
|
||||
var days = moment(e.startDate).diff(moment(), 'days');
|
||||
var seconds = moment(e.startDate).diff(moment(), 'seconds');
|
||||
var startDate = moment(e.startDate);
|
||||
var endDays = moment(e.endDate).diff(moment(), 'days');
|
||||
var endSeconds = moment(e.endDate).diff(moment(), 'seconds');
|
||||
var endDate = moment(e.endDate);
|
||||
}
|
||||
|
||||
//only add fututre events, days doesn't work, we need to check seconds
|
||||
if (seconds >= 0) {
|
||||
if (seconds <= 60*60*5 || seconds >= 60*60*24*2) {
|
||||
var time_string = moment(startDate).fromNow();
|
||||
}else {
|
||||
var time_string = moment(startDate).calendar()
|
||||
}
|
||||
if (!e.RRULE) {
|
||||
this.eventList.push({'description':e.SUMMARY,'seconds':seconds,'days':time_string,'url': url, symbol: this.calendarSymbol});
|
||||
}
|
||||
e.seconds = seconds;
|
||||
} else if (endSeconds > 0) {
|
||||
// TODO: Replace with better lang handling
|
||||
if (endSeconds <= 60*60*5 || endSeconds >= 60*60*24*2) {
|
||||
var time_string = this.shortRunningText + ' ' + moment(endDate).fromNow(true);
|
||||
}else {
|
||||
var time_string = this.longRunningText + ' ' + moment(endDate).calendar()
|
||||
}
|
||||
if (!e.RRULE) {
|
||||
this.eventList.push({'description':e.SUMMARY,'seconds':seconds,'days':time_string,'url': url, symbol: this.calendarSymbol});
|
||||
}
|
||||
e.seconds = endSeconds;
|
||||
}
|
||||
// Special handling for rrule events
|
||||
if (e.RRULE) {
|
||||
var options = new RRule.parseString(e.RRULE);
|
||||
options.dtstart = e.startDate;
|
||||
var rule = new RRule(options);
|
||||
|
||||
var oneYear = new Date();
|
||||
oneYear.setFullYear(oneYear.getFullYear() + 1);
|
||||
|
||||
var dates = rule.between(new Date(), oneYear, true, function (date, i){return i < 10});
|
||||
for (date in dates) {
|
||||
var dt = new Date(dates[date]);
|
||||
var days = moment(dt).diff(moment(), 'days');
|
||||
var seconds = moment(dt).diff(moment(), 'seconds');
|
||||
var startDate = moment(dt);
|
||||
if (seconds >= 0) {
|
||||
if (seconds <= 60*60*5 || seconds >= 60*60*24*2) {
|
||||
var time_string = moment(dt).fromNow();
|
||||
} else {
|
||||
var time_string = moment(dt).calendar()
|
||||
}
|
||||
this.eventList.push({'description':e.SUMMARY,'seconds':seconds,'days':time_string,'url': url, symbol: this.calendarSymbol});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.eventList = this.eventList.sort(function(a,b){return a.seconds-b.seconds});
|
||||
|
||||
// Limit the number of entries.
|
||||
this.eventList = this.eventList.slice(0, calendar.maximumEntries);
|
||||
}
|
||||
|
||||
calendar.updateData = function (callback) {
|
||||
new ical_parser("controllers/calendar.php" + "?url="+encodeURIComponent(this.calendarUrl), function(cal) {
|
||||
this.processEvents(this.calendarUrl, cal.getEvents());
|
||||
|
||||
this.calendarPos++;
|
||||
if ((typeof config.calendar.urls == 'undefined') || (this.calendarPos >= config.calendar.urls.length)) {
|
||||
this.calendarPos = 0;
|
||||
// Last Calendar in List is updated, run Callback (i.e. updateScreen)
|
||||
if (callback !== undefined && Object.prototype.toString.call(callback) === '[object Function]') {
|
||||
callback(this.eventList);
|
||||
}
|
||||
} else {
|
||||
// Loading all Calendars in parallel does not work, load them one by one.
|
||||
setTimeout(function () {
|
||||
this.updateData(this.updateCalendar.bind(this));
|
||||
}.bind(this), 10);
|
||||
}
|
||||
if (typeof config.calendar.urls != 'undefined') {
|
||||
this.calendarUrl = config.calendar.urls[this.calendarPos].url;
|
||||
this.calendarSymbol = config.calendar.urls[this.calendarPos].symbol || this.defaultSymbol;
|
||||
}
|
||||
|
||||
}.bind(this));
|
||||
|
||||
}
|
||||
|
||||
calendar.updateCalendar = function (eventList) {
|
||||
var _is_new = true;
|
||||
if ($('.calendar-table').length) {
|
||||
_is_new = false;
|
||||
}
|
||||
table = $('<table/>').addClass('xsmall').addClass('calendar-table');
|
||||
opacity = 1;
|
||||
|
||||
for (var i in eventList) {
|
||||
var e = eventList[i];
|
||||
var row = $('<tr/>').attr('id', 'event'+i).css('opacity',opacity).addClass('event');
|
||||
if (this.displaySymbol) {
|
||||
row.append($('<td/>').addClass('fa').addClass('fa-'+e.symbol).addClass('calendar-icon'));
|
||||
}
|
||||
row.append($('<td/>').html(e.description).addClass('description'));
|
||||
row.append($('<td/>').html(e.days).addClass('days dimmed'));
|
||||
if (! _is_new && $('#event'+i).length) {
|
||||
$('#event'+i).updateWithText(row.children(), this.fadeInterval);
|
||||
} else {
|
||||
// Something wrong - replace whole table
|
||||
_is_new = true;
|
||||
}
|
||||
table.append(row);
|
||||
|
||||
opacity -= 1 / eventList.length;
|
||||
}
|
||||
if (_is_new) {
|
||||
$(this.calendarLocation).updateWithText(table, this.fadeInterval);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
calendar.init = function () {
|
||||
|
||||
this.updateData(this.updateCalendar.bind(this));
|
||||
|
||||
// this.intervalId = setInterval(function () {
|
||||
// this.updateCalendar(this.eventList)
|
||||
// }.bind(this), this.updateInterval);
|
||||
|
||||
this.dataIntervalId = setInterval(function () {
|
||||
this.updateData(this.updateCalendar.bind(this));
|
||||
}.bind(this), this.updateDataInterval);
|
||||
|
||||
}
|
70
js/class.js
Normal file
70
js/class.js
Normal file
@@ -0,0 +1,70 @@
|
||||
/* Simple JavaScript Inheritance
|
||||
* By John Resig http://ejohn.org/
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
// Inspired by base2 and Prototype
|
||||
(function() {
|
||||
var initializing = false;
|
||||
var fnTest = /xyz/.test(function() {xyz;}) ? /\b_super\b/ : /.*/;
|
||||
|
||||
// The base Class implementation (does nothing)
|
||||
this.Class = function() {};
|
||||
|
||||
// Create a new Class that inherits from this class
|
||||
Class.extend = function(prop) {
|
||||
var _super = this.prototype;
|
||||
|
||||
// Instantiate a base class (but only create the instance,
|
||||
// don't run the init constructor)
|
||||
initializing = true;
|
||||
var prototype = new this();
|
||||
initializing = false;
|
||||
|
||||
// Copy the properties over onto the new prototype
|
||||
for (var name in prop) {
|
||||
// Check if we're overwriting an existing function
|
||||
prototype[name] = typeof prop[name] == "function" &&
|
||||
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
|
||||
(function(name, fn) {
|
||||
return function() {
|
||||
var tmp = this._super;
|
||||
|
||||
// Add a new ._super() method that is the same method
|
||||
// but on the super-class
|
||||
this._super = _super[name];
|
||||
|
||||
// The method only need to be bound temporarily, so we
|
||||
// remove it when we're done executing
|
||||
var ret = fn.apply(this, arguments);
|
||||
this._super = tmp;
|
||||
|
||||
return ret;
|
||||
};
|
||||
})(name, prop[name]) :
|
||||
prop[name];
|
||||
}
|
||||
|
||||
// The dummy class constructor
|
||||
function Class() {
|
||||
// All construction is actually done in the init method
|
||||
if (!initializing && this.init) {
|
||||
this.init.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate our constructed prototype object
|
||||
Class.prototype = prototype;
|
||||
|
||||
// Enforce the constructor to be what we expect
|
||||
Class.prototype.constructor = Class;
|
||||
|
||||
// And make this class extendable
|
||||
Class.extend = arguments.callee;
|
||||
|
||||
return Class;
|
||||
};
|
||||
})();
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = Class;}
|
@@ -1,70 +0,0 @@
|
||||
var compliments = {
|
||||
complimentLocation: '.compliment',
|
||||
currentCompliment: '',
|
||||
complimentList: {
|
||||
'morning': config.compliments.morning,
|
||||
'afternoon': config.compliments.afternoon,
|
||||
'evening': config.compliments.evening
|
||||
},
|
||||
updateInterval: config.compliments.interval || 30000,
|
||||
fadeInterval: config.compliments.fadeInterval || 4000,
|
||||
intervalId: null
|
||||
};
|
||||
|
||||
/**
|
||||
* Changes the compliment visible on the screen
|
||||
*/
|
||||
compliments.updateCompliment = function () {
|
||||
|
||||
|
||||
|
||||
var _list = [];
|
||||
|
||||
var hour = moment().hour();
|
||||
|
||||
// In the following if statement we use .slice() on the
|
||||
// compliments array to make a copy by value.
|
||||
// This way the original array of compliments stays intact.
|
||||
|
||||
if (hour >= 3 && hour < 12) {
|
||||
// Morning compliments
|
||||
_list = compliments.complimentList['morning'].slice();
|
||||
} else if (hour >= 12 && hour < 17) {
|
||||
// Afternoon compliments
|
||||
_list = compliments.complimentList['afternoon'].slice();
|
||||
} else if (hour >= 17 || hour < 3) {
|
||||
// Evening compliments
|
||||
_list = compliments.complimentList['evening'].slice();
|
||||
} else {
|
||||
// Edge case in case something weird happens
|
||||
// This will select a compliment from all times of day
|
||||
Object.keys(compliments.complimentList).forEach(function (_curr) {
|
||||
_list = _list.concat(compliments.complimentList[_curr]).slice();
|
||||
});
|
||||
}
|
||||
|
||||
// Search for the location of the current compliment in the list
|
||||
var _spliceIndex = _list.indexOf(compliments.currentCompliment);
|
||||
|
||||
// If it exists, remove it so we don't see it again
|
||||
if (_spliceIndex !== -1) {
|
||||
_list.splice(_spliceIndex, 1);
|
||||
}
|
||||
|
||||
// Randomly select a location
|
||||
var _randomIndex = Math.floor(Math.random() * _list.length);
|
||||
compliments.currentCompliment = _list[_randomIndex];
|
||||
|
||||
$('.compliment').updateWithText(compliments.currentCompliment, compliments.fadeInterval);
|
||||
|
||||
}
|
||||
|
||||
compliments.init = function () {
|
||||
|
||||
this.updateCompliment();
|
||||
|
||||
this.intervalId = setInterval(function () {
|
||||
this.updateCompliment();
|
||||
}.bind(this), this.updateInterval)
|
||||
|
||||
}
|
68
js/config.js
68
js/config.js
@@ -1,68 +0,0 @@
|
||||
var config = {
|
||||
lang: 'nl',
|
||||
time: {
|
||||
timeFormat: 12,
|
||||
displaySeconds: true,
|
||||
digitFade: false,
|
||||
},
|
||||
weather: {
|
||||
//change weather params here:
|
||||
//units: metric or imperial
|
||||
params: {
|
||||
q: 'Baarn,Netherlands',
|
||||
units: 'metric',
|
||||
// if you want a different lang for the weather that what is set above, change it here
|
||||
lang: 'nl',
|
||||
APPID: 'YOUR_FREE_OPENWEATHER_API_KEY'
|
||||
}
|
||||
},
|
||||
compliments: {
|
||||
interval: 30000,
|
||||
fadeInterval: 4000,
|
||||
morning: [
|
||||
'Good morning, handsome!',
|
||||
'Enjoy your day!',
|
||||
'How was your sleep?'
|
||||
],
|
||||
afternoon: [
|
||||
'Hello, beauty!',
|
||||
'You look sexy!',
|
||||
'Looking good today!'
|
||||
],
|
||||
evening: [
|
||||
'Wow, you look hot!',
|
||||
'You look nice!',
|
||||
'Hi, sexy!'
|
||||
]
|
||||
},
|
||||
calendar: {
|
||||
maximumEntries: 10, // Total Maximum Entries
|
||||
displaySymbol: true,
|
||||
defaultSymbol: 'calendar', // Fontawsome Symbol see http://fontawesome.io/cheatsheet/
|
||||
urls: [
|
||||
{
|
||||
symbol: 'calendar-plus-o',
|
||||
url: 'https://p01-calendarws.icloud.com/ca/subscribe/1/n6x7Farxpt7m9S8bHg1TGArSj7J6kanm_2KEoJPL5YIAk3y70FpRo4GyWwO-6QfHSY5mXtHcRGVxYZUf7U3HPDOTG5x0qYnno1Zr_VuKH2M'
|
||||
},
|
||||
{
|
||||
symbol: 'soccer-ball-o',
|
||||
url: 'https://www.google.com/calendar/ical/akvbisn5iha43idv0ktdalnor4%40group.calendar.google.com/public/basic.ics',
|
||||
},
|
||||
// {
|
||||
// symbol: 'mars',
|
||||
// url: "https://server/url/to/his.ics",
|
||||
// },
|
||||
// {
|
||||
// symbol: 'venus',
|
||||
// url: "https://server/url/to/hers.ics",
|
||||
// },
|
||||
// {
|
||||
// symbol: 'venus-mars',
|
||||
// url: "https://server/url/to/theirs.ics",
|
||||
// },
|
||||
]
|
||||
},
|
||||
news: {
|
||||
feed: 'http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml'
|
||||
}
|
||||
}
|
66
js/defaults.js
Normal file
66
js/defaults.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/* exported defaults */
|
||||
|
||||
/* Magic Mirror
|
||||
* Config Defauls
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
var defaults = {
|
||||
port: 8080,
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "helloworld",
|
||||
position: "upper_third",
|
||||
classes: "large thin",
|
||||
config: {
|
||||
text: "Magic Mirror<sup>2</sup>"
|
||||
}
|
||||
},
|
||||
{
|
||||
module: "helloworld",
|
||||
position: "middle_center",
|
||||
config: {
|
||||
text: "Please create a config file."
|
||||
}
|
||||
},
|
||||
{
|
||||
module: "helloworld",
|
||||
position: "middle_center",
|
||||
classes: "small dimmed",
|
||||
config: {
|
||||
text: "See README for more information."
|
||||
}
|
||||
},
|
||||
{
|
||||
module: "helloworld",
|
||||
position: "middle_center",
|
||||
classes: "xsmall",
|
||||
config: {
|
||||
text: "If you get this message while your config file is already<br>created, your config file probably contains an error.<br>Use a JavaScript linter to validate your file."
|
||||
}
|
||||
},
|
||||
{
|
||||
module: "helloworld",
|
||||
position: "bottom_bar",
|
||||
classes: "xsmall dimmed",
|
||||
config: {
|
||||
text: "www.michaelteeuw.nl"
|
||||
}
|
||||
},
|
||||
],
|
||||
|
||||
paths: {
|
||||
modules: "modules",
|
||||
vendor: "vendor"
|
||||
},
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = defaults;}
|
66
js/electron.js
Normal file
66
js/electron.js
Normal file
@@ -0,0 +1,66 @@
|
||||
"use strict";
|
||||
|
||||
const Server = require(__dirname + "/server.js");
|
||||
const electron = require("electron");
|
||||
const core = require(__dirname + "/app.js");
|
||||
|
||||
// Config
|
||||
var config = {};
|
||||
// Module to control application life.
|
||||
const app = electron.app;
|
||||
// Module to create native browser window.
|
||||
const BrowserWindow = electron.BrowserWindow;
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow;
|
||||
|
||||
function createWindow() {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({width: 800, height: 600, fullscreen: true, autoHideMenuBar: true, darkTheme: true, webPreferences: {nodeIntegration: false}});
|
||||
|
||||
// and load the index.html of the app.
|
||||
//mainWindow.loadURL('file://' + __dirname + '../../index.html');
|
||||
mainWindow.loadURL("http://localhost:" + config.port);
|
||||
|
||||
// Open the DevTools.
|
||||
//mainWindow.webContents.openDevTools();
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on("closed", function() {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null;
|
||||
});
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
app.on("ready", function() {
|
||||
console.log("Launching application.");
|
||||
createWindow();
|
||||
});
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on("window-all-closed", function() {
|
||||
// On OS X it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== "darwin") {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on("activate", function() {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// Start the core application.
|
||||
// This starts all node helpers and starts the webserver.
|
||||
core.start(function(c) {
|
||||
config = c;
|
||||
});
|
@@ -1,228 +0,0 @@
|
||||
/**
|
||||
* Javascript ical Parser
|
||||
* Proof of concept method of reading icalendar (.ics) files with javascript.
|
||||
*
|
||||
* @author: Carl Saggs
|
||||
* @source: https://github.com/thybag/
|
||||
* @version: 0.2
|
||||
*/
|
||||
function ical_parser(feed_url, callback){
|
||||
//store of unproccesed data.
|
||||
this.raw_data = null;
|
||||
//Store of proccessed data.
|
||||
this.events = [];
|
||||
|
||||
/**
|
||||
* loadFile
|
||||
* Using AJAX to load the requested .ics file, passing it to the callback when completed.
|
||||
* @param url URL of .ics file
|
||||
* @param callback Function to call on completion.
|
||||
*/
|
||||
this.loadFile = function(url, callback){
|
||||
//Create request object
|
||||
try {xmlhttp = window.XMLHttpRequest?new XMLHttpRequest(): new ActiveXObject("Microsoft.XMLHTTP");} catch (e) { }
|
||||
//Grab file
|
||||
xmlhttp.onreadystatechange = function(){
|
||||
if ((xmlhttp.readyState == 4) && (xmlhttp.status == 200)) {
|
||||
//On success, run callback.
|
||||
callback(xmlhttp.responseText);
|
||||
}
|
||||
}
|
||||
xmlhttp.open("GET", url, true);
|
||||
xmlhttp.send(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* makeDate
|
||||
* Convert the dateformat used by ICalendar in to one more suitable for javascript.
|
||||
* @param String ical_date
|
||||
* @return dt object, includes javascript Date + day name, hour/minutes/day/month/year etc.
|
||||
*/
|
||||
this.makeDate = function(ical_date){
|
||||
//break date apart
|
||||
var dtutc = {
|
||||
year: ical_date.substr(0,4),
|
||||
month: ical_date.substr(4,2),
|
||||
day: ical_date.substr(6,2),
|
||||
hour: ical_date.substr(9,2),
|
||||
minute: ical_date.substr(11,2)
|
||||
}
|
||||
//Create JS date (months start at 0 in JS - don't ask)
|
||||
var utcdatems = Date.UTC(dtutc.year, (dtutc.month-1), dtutc.day, dtutc.hour, dtutc.minute);
|
||||
var dt = {};
|
||||
dt.date = new Date(utcdatems);
|
||||
|
||||
dt.year = dt.date.getFullYear();
|
||||
dt.month = ('0' + (dt.date.getMonth()+1)).slice(-2);
|
||||
dt.day = ('0' + dt.date.getDate()).slice(-2);
|
||||
dt.hour = ('0' + dt.date.getHours()).slice(-2);
|
||||
dt.minute = ('0' + dt.date.getMinutes()).slice(-2);
|
||||
|
||||
//Get the full name of the given day
|
||||
dt.dayname =["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"][dt.date.getDay()];
|
||||
dt.monthname = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ][dt.date.getMonth()] ;
|
||||
|
||||
return dt;
|
||||
}
|
||||
|
||||
/**
|
||||
* parseICAL
|
||||
* Convert the ICAL format in to a number of javascript objects (Each representing a date)
|
||||
*
|
||||
* @param data Raw ICAL data
|
||||
*/
|
||||
this.parseICAL = function(data){
|
||||
//Ensure cal is empty
|
||||
this.events = [];
|
||||
|
||||
//Clean string and split the file so we can handle it (line by line)
|
||||
cal_array = data.replace(new RegExp( "\\r", "g" ), "").replace(/\n /g,"").split("\n");
|
||||
|
||||
//Keep track of when we are activly parsing an event
|
||||
var in_event = false;
|
||||
//Use as a holder for the current event being proccessed.
|
||||
var cur_event = null;
|
||||
for(var i=0;i<cal_array.length;i++){
|
||||
ln = cal_array[i];
|
||||
//If we encounted a new Event, create a blank event object + set in event options.
|
||||
if(!in_event && ln == 'BEGIN:VEVENT'){
|
||||
in_event = true;
|
||||
cur_event = {};
|
||||
}
|
||||
//If we encounter end event, complete the object and add it to our events array then clear it for reuse.
|
||||
if(in_event && ln == 'END:VEVENT'){
|
||||
in_event = false;
|
||||
this.events.push(cur_event);
|
||||
cur_event = null;
|
||||
}
|
||||
//If we are in an event
|
||||
else if(in_event){
|
||||
//var lntrim = ln.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
|
||||
//var lnsplit = lntrim.split(':');
|
||||
//type = lnsplit[0];
|
||||
//val = lnsplit[1];
|
||||
|
||||
//Split the item based on the first ":"
|
||||
idx = ln.indexOf(':');
|
||||
//Apply trimming to values to reduce risks of badly formatted ical files.
|
||||
type = ln.substr(0,idx).replace(/^\s\s*/, '').replace(/\s\s*$/, '');//Trim
|
||||
val = ln.substr(idx+1).replace(/^\s\s*/, '').replace(/\s\s*$/, '');
|
||||
|
||||
if(typeof cur_event[type] !== 'undefined'){
|
||||
continue;
|
||||
}
|
||||
|
||||
//If the type is a start date, proccess it and store details
|
||||
if(type =='DTSTART'){
|
||||
dt = this.makeDate(val);
|
||||
val = dt.date;
|
||||
//These are helpful for display
|
||||
cur_event.start_time = dt.hour+':'+dt.minute;
|
||||
cur_event.start_date = dt.day+'/'+dt.month+'/'+dt.year;
|
||||
cur_event.day = dt.dayname;
|
||||
cur_event.start_date_long = dt.day+'. '+dt.monthname+' '+dt.year ;
|
||||
}
|
||||
//If the type is an end date, do the same as above
|
||||
else if(type =='DTEND'){
|
||||
dt = this.makeDate(val);
|
||||
val = dt.date;
|
||||
//These are helpful for display
|
||||
cur_event.end_time = dt.hour+':'+dt.minute;
|
||||
cur_event.end_date = dt.day+'/'+dt.month+'/'+dt.year;
|
||||
cur_event.day = dt.dayname;
|
||||
}
|
||||
//Convert timestamp
|
||||
else if(type =='DTSTAMP'){
|
||||
val = this.makeDate(val).date;
|
||||
}
|
||||
else {
|
||||
val = val
|
||||
.replace(/\\r\\n/g,'<br />')
|
||||
.replace(/\\n/g,'<br />')
|
||||
.replace(/\\,/g,',');
|
||||
}
|
||||
|
||||
//Add the value to our event object.
|
||||
cur_event[type] = val;
|
||||
}
|
||||
}
|
||||
//Run this to finish proccessing our Events.
|
||||
this.complete();
|
||||
}
|
||||
/**
|
||||
* complete
|
||||
* Sort all events in to a sensible order and run the original callback
|
||||
*/
|
||||
this.complete = function(){
|
||||
//Sort the data so its in date order.
|
||||
this.events.sort(function(a,b){
|
||||
return a.DTSTART-b.DTSTART;
|
||||
});
|
||||
//Run callback method, if was defined. (return self)
|
||||
if(typeof callback == 'function') callback(this);
|
||||
}
|
||||
/**
|
||||
* getEvents
|
||||
* return all events found in the ical file.
|
||||
*
|
||||
* @return list of events objects
|
||||
*/
|
||||
this.getEvents = function(){
|
||||
return this.events;
|
||||
}
|
||||
|
||||
/**
|
||||
* getFutureEvents
|
||||
* return all events sheduled to take place after the current date.
|
||||
*
|
||||
* @return list of events objects
|
||||
*/
|
||||
this.getFutureEvents = function(){
|
||||
var future_events = [], current_date = new Date();
|
||||
|
||||
this.events.forEach(function(itm){
|
||||
//If the event ends after the current time, add it to the array to return.
|
||||
if(itm.DTEND > current_date) future_events.push(itm);
|
||||
});
|
||||
return future_events;
|
||||
}
|
||||
|
||||
/**
|
||||
* getPastEvents
|
||||
* return all events sheduled to take place before the current date.
|
||||
*
|
||||
* @return list of events objects
|
||||
*/
|
||||
this.getPastEvents = function(){
|
||||
var past_events = [], current_date = new Date();
|
||||
|
||||
this.events.forEach(function(itm){
|
||||
//If the event ended before the current time, add it to the array to return.
|
||||
if(itm.DTEND <= current_date) past_events.push(itm);
|
||||
});
|
||||
return past_events.reverse();
|
||||
}
|
||||
|
||||
/**
|
||||
* load
|
||||
* load a new ICAL file.
|
||||
*
|
||||
* @param ical file url
|
||||
*/
|
||||
this.load = function(ical_file){
|
||||
var tmp_this = this;
|
||||
this.raw_data = null;
|
||||
this.loadFile(ical_file, function(data){
|
||||
//if the file loads, store the data and invoke the parser
|
||||
tmp_this.raw_data = data;
|
||||
tmp_this.parseICAL(data);
|
||||
});
|
||||
}
|
||||
|
||||
//Store this so we can use it in the callback from the load function.
|
||||
var tmp_this = this;
|
||||
//Store the feed url
|
||||
this.feed_url = feed_url;
|
||||
//Load the file
|
||||
this.load(this.feed_url);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user