├── cordova
├── .npmignore
├── www
│ ├── libs
│ │ ├── jquery
│ │ │ └── VERSION
│ │ ├── evothings
│ │ │ ├── VERSION
│ │ │ ├── ui
│ │ │ │ ├── ui.js
│ │ │ │ └── fastclick.js
│ │ │ ├── util
│ │ │ │ └── util.js
│ │ │ └── evothings.js
│ │ ├── nouislider
│ │ │ └── nouislider.css
│ │ └── spectrum
│ │ │ └── spectrum.css
│ ├── evothings.json
│ ├── img
│ │ ├── loader.gif
│ │ ├── paint
│ │ │ ├── fire.jpg
│ │ │ ├── mario.png
│ │ │ ├── pacman.png
│ │ │ ├── water.jpg
│ │ │ ├── Nyan-Cat.png
│ │ │ ├── monalisa.jpg
│ │ │ └── space_invaders.png
│ │ └── loader_small.gif
│ ├── fonts
│ │ ├── Rajdhani-Regular.woff2
│ │ ├── rajdhani.css
│ │ ├── Rajdhani-Bold.woff2
│ │ ├── Rajdhani-Medium.woff2
│ │ └── Rajdhani-SemiBold.woff2
│ ├── ui
│ │ ├── fonts
│ │ │ ├── ProximaNova-Reg-webfont.eot
│ │ │ ├── ProximaNova-Reg-webfont.ttf
│ │ │ ├── ProximaNova-Black-webfont.eot
│ │ │ ├── ProximaNova-Black-webfont.ttf
│ │ │ ├── ProximaNova-Bold-webfont.eot
│ │ │ ├── ProximaNova-Bold-webfont.ttf
│ │ │ ├── ProximaNova-Bold-webfont.woff
│ │ │ ├── ProximaNova-Reg-webfont.woff
│ │ │ ├── ProximaNova-Black-webfont.woff
│ │ │ └── ProximaNova.css
│ │ ├── images
│ │ │ ├── arrow-left.svg
│ │ │ ├── arrow-right.svg
│ │ │ ├── menu.svg
│ │ │ └── logo.svg
│ │ └── css
│ │ │ └── evothings-app.css
│ ├── css
│ │ └── spinner.css
│ ├── index.html
│ └── app.js
├── res
│ ├── logo
│ │ ├── icon.png
│ │ ├── splash.png
│ │ ├── appstore.png
│ │ ├── original.jpg
│ │ ├── googleplay.png
│ │ └── icon-feature-graphic-playstore.jpeg
│ ├── icons
│ │ ├── ios
│ │ │ ├── icon.png
│ │ │ ├── icon-2x.png
│ │ │ ├── icon-40.png
│ │ │ ├── icon-50.png
│ │ │ ├── icon-60.png
│ │ │ ├── icon-72.png
│ │ │ ├── icon-76.png
│ │ │ ├── icon-40-2x.png
│ │ │ ├── icon-50-2x.png
│ │ │ ├── icon-60-2x.png
│ │ │ ├── icon-60-3x.png
│ │ │ ├── icon-72-2x.png
│ │ │ ├── icon-76-2x.png
│ │ │ ├── icon-small.png
│ │ │ └── icon-small-2x.png
│ │ └── android
│ │ │ ├── icon-36-ldpi.png
│ │ │ ├── icon-48-mdpi.png
│ │ │ ├── icon-72-hdpi.png
│ │ │ ├── icon-96-xhdpi.png
│ │ │ ├── icon-144-xxhdpi.png
│ │ │ └── icon-192-xxxhdpi.png
│ ├── icon
│ │ ├── ios
│ │ │ ├── icon-57.png
│ │ │ ├── icon-72.png
│ │ │ ├── icon-57-2x.png
│ │ │ └── icon-72-2x.png
│ │ ├── bada
│ │ │ └── icon-128.png
│ │ ├── tizen
│ │ │ └── icon-128.png
│ │ ├── webos
│ │ │ └── icon-64.png
│ │ ├── blackberry
│ │ │ └── icon-80.png
│ │ ├── android
│ │ │ ├── icon-36-ldpi.png
│ │ │ ├── icon-48-mdpi.png
│ │ │ ├── icon-72-hdpi.png
│ │ │ └── icon-96-xhdpi.png
│ │ ├── bada-wac
│ │ │ ├── icon-48-type5.png
│ │ │ ├── icon-50-type3.png
│ │ │ └── icon-80-type4.png
│ │ ├── blackberry10
│ │ │ └── icon-80.png
│ │ └── windows-phone
│ │ │ ├── icon-48.png
│ │ │ ├── icon-173-tile.png
│ │ │ └── icon-62-tile.png
│ ├── screen
│ │ ├── webos
│ │ │ └── screen-64.png
│ │ ├── bada
│ │ │ └── screen-portrait.png
│ │ ├── bada-wac
│ │ │ ├── screen-type3.png
│ │ │ ├── screen-type4.png
│ │ │ └── screen-type5.png
│ │ ├── blackberry
│ │ │ └── screen-225.png
│ │ ├── ios
│ │ │ ├── screen-ipad-landscape.png
│ │ │ ├── screen-ipad-portrait.png
│ │ │ ├── screen-ipad-portrait-2x.png
│ │ │ ├── screen-iphone-landscape.png
│ │ │ ├── screen-iphone-portrait.png
│ │ │ ├── screen-ipad-landscape-2x.png
│ │ │ ├── screen-iphone-landscape-2x.png
│ │ │ ├── screen-iphone-portrait-2x.png
│ │ │ └── screen-iphone-portrait-568h-2x.png
│ │ ├── blackberry10
│ │ │ ├── splash-720x720.png
│ │ │ ├── splash-1280x768.png
│ │ │ └── splash-768x1280.png
│ │ ├── android
│ │ │ ├── screen-hdpi-landscape.png
│ │ │ ├── screen-hdpi-portrait.png
│ │ │ ├── screen-ldpi-landscape.png
│ │ │ ├── screen-ldpi-portrait.png
│ │ │ ├── screen-mdpi-landscape.png
│ │ │ ├── screen-mdpi-portrait.png
│ │ │ ├── screen-xhdpi-landscape.png
│ │ │ └── screen-xhdpi-portrait.png
│ │ ├── windows-phone
│ │ │ └── screen-portrait.jpg
│ │ └── tizen
│ │ │ └── README.md
│ ├── screens
│ │ ├── ios
│ │ │ ├── screen-ipad-portrait.png
│ │ │ ├── screen-iphone-portrait.png
│ │ │ ├── screen-ipad-portrait-2x.png
│ │ │ ├── screen-iphone-portrait-2x.png
│ │ │ ├── screen-iphone-portrait-667h.png
│ │ │ ├── screen-iphone-portrait-736h.png
│ │ │ └── screen-iphone-portrait-568h-2x.png
│ │ └── android
│ │ │ ├── screen-hdpi-portrait.png
│ │ │ ├── screen-ldpi-portrait.png
│ │ │ ├── screen-mdpi-portrait.png
│ │ │ ├── screen-xhdpi-portrait.png
│ │ │ ├── screen-xxhdpi-portrait.png
│ │ │ └── screen-xxxhdpi-portrait.png
│ └── README.md
├── RELEASE_ALL.sh
├── package.json
└── config.xml
├── .gitignore
├── Programmable LED Pattern Language Architecture (ESP32 + Mobile App).pdf
├── index.html
├── gfxfont.h
├── platformio.ini
├── BLEDeviceID.h
├── BLESerial.h
├── README.md
├── BLESerial.cpp
├── BLEDeviceID.cpp
├── Picopixel.h
├── Adafruit_GFX.h
└── glcdfont.c
/cordova/.npmignore:
--------------------------------------------------------------------------------
1 | # OS X
2 | .DS_Store
3 |
--------------------------------------------------------------------------------
/cordova/www/libs/jquery/VERSION:
--------------------------------------------------------------------------------
1 | jquery-2.1.1.min.js
2 |
--------------------------------------------------------------------------------
/cordova/www/libs/evothings/VERSION:
--------------------------------------------------------------------------------
1 | Evothings Libraries version 2.1.0
2 |
--------------------------------------------------------------------------------
/cordova/www/evothings.json:
--------------------------------------------------------------------------------
1 | {
2 | "uuid": "31f6be13-7f18-49ad-9f33-9d885791fa72"
3 | }
--------------------------------------------------------------------------------
/cordova/res/logo/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/logo/icon.png
--------------------------------------------------------------------------------
/cordova/res/logo/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/logo/splash.png
--------------------------------------------------------------------------------
/cordova/www/img/loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/img/loader.gif
--------------------------------------------------------------------------------
/cordova/res/icons/ios/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/ios/icon.png
--------------------------------------------------------------------------------
/cordova/res/logo/appstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/logo/appstore.png
--------------------------------------------------------------------------------
/cordova/res/logo/original.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/logo/original.jpg
--------------------------------------------------------------------------------
/cordova/www/img/paint/fire.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/img/paint/fire.jpg
--------------------------------------------------------------------------------
/cordova/res/icon/ios/icon-57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/ios/icon-57.png
--------------------------------------------------------------------------------
/cordova/res/icon/ios/icon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/ios/icon-72.png
--------------------------------------------------------------------------------
/cordova/res/icons/ios/icon-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/ios/icon-2x.png
--------------------------------------------------------------------------------
/cordova/res/icons/ios/icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/ios/icon-40.png
--------------------------------------------------------------------------------
/cordova/res/icons/ios/icon-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/ios/icon-50.png
--------------------------------------------------------------------------------
/cordova/res/icons/ios/icon-60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/ios/icon-60.png
--------------------------------------------------------------------------------
/cordova/res/icons/ios/icon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/ios/icon-72.png
--------------------------------------------------------------------------------
/cordova/res/icons/ios/icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/ios/icon-76.png
--------------------------------------------------------------------------------
/cordova/res/logo/googleplay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/logo/googleplay.png
--------------------------------------------------------------------------------
/cordova/www/img/loader_small.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/img/loader_small.gif
--------------------------------------------------------------------------------
/cordova/www/img/paint/mario.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/img/paint/mario.png
--------------------------------------------------------------------------------
/cordova/www/img/paint/pacman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/img/paint/pacman.png
--------------------------------------------------------------------------------
/cordova/www/img/paint/water.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/img/paint/water.jpg
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.sw?
3 | node_modules
4 | cordova/platforms/
5 | cordova/plugins/
6 | cordova/hooks/
7 |
8 |
--------------------------------------------------------------------------------
/cordova/res/icon/bada/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/bada/icon-128.png
--------------------------------------------------------------------------------
/cordova/res/icon/ios/icon-57-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/ios/icon-57-2x.png
--------------------------------------------------------------------------------
/cordova/res/icon/ios/icon-72-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/ios/icon-72-2x.png
--------------------------------------------------------------------------------
/cordova/res/icon/tizen/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/tizen/icon-128.png
--------------------------------------------------------------------------------
/cordova/res/icon/webos/icon-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/webos/icon-64.png
--------------------------------------------------------------------------------
/cordova/www/img/paint/Nyan-Cat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/img/paint/Nyan-Cat.png
--------------------------------------------------------------------------------
/cordova/www/img/paint/monalisa.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/img/paint/monalisa.jpg
--------------------------------------------------------------------------------
/cordova/res/icons/ios/icon-40-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/ios/icon-40-2x.png
--------------------------------------------------------------------------------
/cordova/res/icons/ios/icon-50-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/ios/icon-50-2x.png
--------------------------------------------------------------------------------
/cordova/res/icons/ios/icon-60-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/ios/icon-60-2x.png
--------------------------------------------------------------------------------
/cordova/res/icons/ios/icon-60-3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/ios/icon-60-3x.png
--------------------------------------------------------------------------------
/cordova/res/icons/ios/icon-72-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/ios/icon-72-2x.png
--------------------------------------------------------------------------------
/cordova/res/icons/ios/icon-76-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/ios/icon-76-2x.png
--------------------------------------------------------------------------------
/cordova/res/icons/ios/icon-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/ios/icon-small.png
--------------------------------------------------------------------------------
/cordova/res/screen/webos/screen-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/webos/screen-64.png
--------------------------------------------------------------------------------
/cordova/res/icon/blackberry/icon-80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/blackberry/icon-80.png
--------------------------------------------------------------------------------
/cordova/res/icons/ios/icon-small-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/ios/icon-small-2x.png
--------------------------------------------------------------------------------
/cordova/www/fonts/Rajdhani-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/fonts/Rajdhani-Regular.woff2
--------------------------------------------------------------------------------
/cordova/www/img/paint/space_invaders.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/img/paint/space_invaders.png
--------------------------------------------------------------------------------
/cordova/res/icon/android/icon-36-ldpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/android/icon-36-ldpi.png
--------------------------------------------------------------------------------
/cordova/res/icon/android/icon-48-mdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/android/icon-48-mdpi.png
--------------------------------------------------------------------------------
/cordova/res/icon/android/icon-72-hdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/android/icon-72-hdpi.png
--------------------------------------------------------------------------------
/cordova/res/icon/android/icon-96-xhdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/android/icon-96-xhdpi.png
--------------------------------------------------------------------------------
/cordova/res/icon/bada-wac/icon-48-type5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/bada-wac/icon-48-type5.png
--------------------------------------------------------------------------------
/cordova/res/icon/bada-wac/icon-50-type3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/bada-wac/icon-50-type3.png
--------------------------------------------------------------------------------
/cordova/res/icon/bada-wac/icon-80-type4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/bada-wac/icon-80-type4.png
--------------------------------------------------------------------------------
/cordova/res/icon/blackberry10/icon-80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/blackberry10/icon-80.png
--------------------------------------------------------------------------------
/cordova/res/icon/windows-phone/icon-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/windows-phone/icon-48.png
--------------------------------------------------------------------------------
/cordova/res/icons/android/icon-36-ldpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/android/icon-36-ldpi.png
--------------------------------------------------------------------------------
/cordova/res/icons/android/icon-48-mdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/android/icon-48-mdpi.png
--------------------------------------------------------------------------------
/cordova/res/icons/android/icon-72-hdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/android/icon-72-hdpi.png
--------------------------------------------------------------------------------
/cordova/res/icons/android/icon-96-xhdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/android/icon-96-xhdpi.png
--------------------------------------------------------------------------------
/cordova/res/screen/bada/screen-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/bada/screen-portrait.png
--------------------------------------------------------------------------------
/cordova/res/icons/android/icon-144-xxhdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/android/icon-144-xxhdpi.png
--------------------------------------------------------------------------------
/cordova/res/screen/bada-wac/screen-type3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/bada-wac/screen-type3.png
--------------------------------------------------------------------------------
/cordova/res/screen/bada-wac/screen-type4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/bada-wac/screen-type4.png
--------------------------------------------------------------------------------
/cordova/res/screen/bada-wac/screen-type5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/bada-wac/screen-type5.png
--------------------------------------------------------------------------------
/cordova/res/screen/blackberry/screen-225.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/blackberry/screen-225.png
--------------------------------------------------------------------------------
/cordova/res/icon/windows-phone/icon-173-tile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/windows-phone/icon-173-tile.png
--------------------------------------------------------------------------------
/cordova/res/icon/windows-phone/icon-62-tile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icon/windows-phone/icon-62-tile.png
--------------------------------------------------------------------------------
/cordova/res/icons/android/icon-192-xxxhdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/icons/android/icon-192-xxxhdpi.png
--------------------------------------------------------------------------------
/cordova/res/screen/ios/screen-ipad-landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/ios/screen-ipad-landscape.png
--------------------------------------------------------------------------------
/cordova/res/screen/ios/screen-ipad-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/ios/screen-ipad-portrait.png
--------------------------------------------------------------------------------
/cordova/res/screens/ios/screen-ipad-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screens/ios/screen-ipad-portrait.png
--------------------------------------------------------------------------------
/cordova/www/ui/fonts/ProximaNova-Reg-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/ui/fonts/ProximaNova-Reg-webfont.eot
--------------------------------------------------------------------------------
/cordova/www/ui/fonts/ProximaNova-Reg-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/ui/fonts/ProximaNova-Reg-webfont.ttf
--------------------------------------------------------------------------------
/cordova/res/screen/blackberry10/splash-720x720.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/blackberry10/splash-720x720.png
--------------------------------------------------------------------------------
/cordova/res/screen/ios/screen-ipad-portrait-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/ios/screen-ipad-portrait-2x.png
--------------------------------------------------------------------------------
/cordova/res/screen/ios/screen-iphone-landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/ios/screen-iphone-landscape.png
--------------------------------------------------------------------------------
/cordova/res/screen/ios/screen-iphone-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/ios/screen-iphone-portrait.png
--------------------------------------------------------------------------------
/cordova/res/screens/ios/screen-iphone-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screens/ios/screen-iphone-portrait.png
--------------------------------------------------------------------------------
/cordova/www/ui/fonts/ProximaNova-Black-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/ui/fonts/ProximaNova-Black-webfont.eot
--------------------------------------------------------------------------------
/cordova/www/ui/fonts/ProximaNova-Black-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/ui/fonts/ProximaNova-Black-webfont.ttf
--------------------------------------------------------------------------------
/cordova/www/ui/fonts/ProximaNova-Bold-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/ui/fonts/ProximaNova-Bold-webfont.eot
--------------------------------------------------------------------------------
/cordova/www/ui/fonts/ProximaNova-Bold-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/ui/fonts/ProximaNova-Bold-webfont.ttf
--------------------------------------------------------------------------------
/cordova/www/ui/fonts/ProximaNova-Bold-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/ui/fonts/ProximaNova-Bold-webfont.woff
--------------------------------------------------------------------------------
/cordova/www/ui/fonts/ProximaNova-Reg-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/ui/fonts/ProximaNova-Reg-webfont.woff
--------------------------------------------------------------------------------
/cordova/res/logo/icon-feature-graphic-playstore.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/logo/icon-feature-graphic-playstore.jpeg
--------------------------------------------------------------------------------
/cordova/res/screen/android/screen-hdpi-landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/android/screen-hdpi-landscape.png
--------------------------------------------------------------------------------
/cordova/res/screen/android/screen-hdpi-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/android/screen-hdpi-portrait.png
--------------------------------------------------------------------------------
/cordova/res/screen/android/screen-ldpi-landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/android/screen-ldpi-landscape.png
--------------------------------------------------------------------------------
/cordova/res/screen/android/screen-ldpi-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/android/screen-ldpi-portrait.png
--------------------------------------------------------------------------------
/cordova/res/screen/android/screen-mdpi-landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/android/screen-mdpi-landscape.png
--------------------------------------------------------------------------------
/cordova/res/screen/android/screen-mdpi-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/android/screen-mdpi-portrait.png
--------------------------------------------------------------------------------
/cordova/res/screen/android/screen-xhdpi-landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/android/screen-xhdpi-landscape.png
--------------------------------------------------------------------------------
/cordova/res/screen/android/screen-xhdpi-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/android/screen-xhdpi-portrait.png
--------------------------------------------------------------------------------
/cordova/res/screen/blackberry10/splash-1280x768.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/blackberry10/splash-1280x768.png
--------------------------------------------------------------------------------
/cordova/res/screen/blackberry10/splash-768x1280.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/blackberry10/splash-768x1280.png
--------------------------------------------------------------------------------
/cordova/res/screen/ios/screen-ipad-landscape-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/ios/screen-ipad-landscape-2x.png
--------------------------------------------------------------------------------
/cordova/res/screen/ios/screen-iphone-landscape-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/ios/screen-iphone-landscape-2x.png
--------------------------------------------------------------------------------
/cordova/res/screen/ios/screen-iphone-portrait-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/ios/screen-iphone-portrait-2x.png
--------------------------------------------------------------------------------
/cordova/res/screen/windows-phone/screen-portrait.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/windows-phone/screen-portrait.jpg
--------------------------------------------------------------------------------
/cordova/res/screens/android/screen-hdpi-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screens/android/screen-hdpi-portrait.png
--------------------------------------------------------------------------------
/cordova/res/screens/android/screen-ldpi-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screens/android/screen-ldpi-portrait.png
--------------------------------------------------------------------------------
/cordova/res/screens/android/screen-mdpi-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screens/android/screen-mdpi-portrait.png
--------------------------------------------------------------------------------
/cordova/res/screens/android/screen-xhdpi-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screens/android/screen-xhdpi-portrait.png
--------------------------------------------------------------------------------
/cordova/res/screens/ios/screen-ipad-portrait-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screens/ios/screen-ipad-portrait-2x.png
--------------------------------------------------------------------------------
/cordova/res/screens/ios/screen-iphone-portrait-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screens/ios/screen-iphone-portrait-2x.png
--------------------------------------------------------------------------------
/cordova/www/ui/fonts/ProximaNova-Black-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/www/ui/fonts/ProximaNova-Black-webfont.woff
--------------------------------------------------------------------------------
/cordova/res/screens/android/screen-xxhdpi-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screens/android/screen-xxhdpi-portrait.png
--------------------------------------------------------------------------------
/cordova/res/screens/android/screen-xxxhdpi-portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screens/android/screen-xxxhdpi-portrait.png
--------------------------------------------------------------------------------
/cordova/res/screens/ios/screen-iphone-portrait-667h.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screens/ios/screen-iphone-portrait-667h.png
--------------------------------------------------------------------------------
/cordova/res/screens/ios/screen-iphone-portrait-736h.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screens/ios/screen-iphone-portrait-736h.png
--------------------------------------------------------------------------------
/cordova/res/screen/ios/screen-iphone-portrait-568h-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screen/ios/screen-iphone-portrait-568h-2x.png
--------------------------------------------------------------------------------
/cordova/res/screens/ios/screen-iphone-portrait-568h-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/cordova/res/screens/ios/screen-iphone-portrait-568h-2x.png
--------------------------------------------------------------------------------
/Programmable LED Pattern Language Architecture (ESP32 + Mobile App).pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hobzcalvin/blume/HEAD/Programmable LED Pattern Language Architecture (ESP32 + Mobile App).pdf
--------------------------------------------------------------------------------
/cordova/RELEASE_ALL.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | code-push release-cordova blume-android android -m -t 1.0.x -d Production
4 | code-push release-cordova blume-ios ios -m -t 1.0.x -d Production
5 | echo Android:
6 | code-push deployment ls blume-android
7 | echo iOS:
8 | code-push deployment ls blume-ios
9 |
--------------------------------------------------------------------------------
/cordova/www/css/spinner.css:
--------------------------------------------------------------------------------
1 | .spinner {
2 | width: 40px;
3 | height: 40px;
4 | margin: 20px auto;
5 | border: 3px solid var(--border-color);
6 | border-top: 3px solid var(--accent-color);
7 | border-radius: 50%;
8 | animation: spin 1s linear infinite;
9 | }
10 |
11 | @keyframes spin {
12 | 0% { transform: rotate(0deg); }
13 | 100% { transform: rotate(360deg); }
14 | }
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 | Blume
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/cordova/www/fonts/rajdhani.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Rajdhani';
3 | font-style: normal;
4 | font-weight: 400;
5 | src: url('Rajdhani-Regular.woff2') format('woff2');
6 | }
7 |
8 | @font-face {
9 | font-family: 'Rajdhani';
10 | font-style: normal;
11 | font-weight: 500;
12 | src: url('Rajdhani-Medium.woff2') format('woff2');
13 | }
14 |
15 | @font-face {
16 | font-family: 'Rajdhani';
17 | font-style: normal;
18 | font-weight: 600;
19 | src: url('Rajdhani-SemiBold.woff2') format('woff2');
20 | }
21 |
22 | @font-face {
23 | font-family: 'Rajdhani';
24 | font-style: normal;
25 | font-weight: 700;
26 | src: url('Rajdhani-Bold.woff2') format('woff2');
27 | }
--------------------------------------------------------------------------------
/cordova/www/ui/images/arrow-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
10 |
11 |
--------------------------------------------------------------------------------
/cordova/www/ui/images/arrow-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/cordova/res/screen/tizen/README.md:
--------------------------------------------------------------------------------
1 |
21 |
22 | # Tizen Splash Screen
23 |
24 | Splash screens are unsupported on the Tizen platform.
25 |
--------------------------------------------------------------------------------
/gfxfont.h:
--------------------------------------------------------------------------------
1 | // Font structures for newer Adafruit_GFX (1.1 and later).
2 | // Example fonts are included in 'Fonts' directory.
3 | // To use a font in your Arduino sketch, #include the corresponding .h
4 | // file and pass address of GFXfont struct to setFont(). Pass NULL to
5 | // revert to 'classic' fixed-space bitmap font.
6 |
7 | #ifndef _GFXFONT_H_
8 | #define _GFXFONT_H_
9 |
10 | typedef struct { // Data stored PER GLYPH
11 | uint16_t bitmapOffset; // Pointer into GFXfont->bitmap
12 | uint8_t width, height; // Bitmap dimensions in pixels
13 | uint8_t xAdvance; // Distance to advance cursor (x axis)
14 | int8_t xOffset, yOffset; // Dist from cursor pos to UL corner
15 | } GFXglyph;
16 |
17 | typedef struct { // Data stored for FONT AS A WHOLE:
18 | uint8_t *bitmap; // Glyph bitmaps, concatenated
19 | GFXglyph *glyph; // Glyph array
20 | uint8_t first, last; // ASCII extents
21 | uint8_t yAdvance; // Newline distance (y axis)
22 | } GFXfont;
23 |
24 | #endif // _GFXFONT_H_
25 |
--------------------------------------------------------------------------------
/cordova/www/libs/evothings/ui/ui.js:
--------------------------------------------------------------------------------
1 | /*
2 | Evothings UI functionality.
3 |
4 | FastClick is used to make UI responsive.
5 | */
6 |
7 | ;(function()
8 | {
9 | // Special layout hack for iOS 7.
10 | function applyiOS7LayoutHack()
11 | {
12 | // Set an absolute base font size in iOS 7 due to that viewport-relative
13 | // font sizes doesn't work properly caused by the WebKit bug described at
14 | // https://bugs.webkit.org/show_bug.cgi?id=131863.
15 | if (evothings.os.isIOS7())
16 | {
17 | document.body.style.fontSize = '20pt';
18 | }
19 | }
20 |
21 | function applyUIUpdatesWhenPageHasLoaded()
22 | {
23 | var applyUIUpdates = function() {
24 | applyiOS7LayoutHack();
25 | FastClick.attach(document.body);
26 | }
27 |
28 | /* If the DOMContentLoaded event was already fired, apply the UI updates
29 | * now, otherwise wait for the event.
30 | */
31 | if (evothings.gotDOMContentLoaded)
32 | {
33 | applyUIUpdates()
34 | }
35 | else
36 | {
37 | window.addEventListener('DOMContentLoaded', applyUIUpdates)
38 | }
39 | }
40 |
41 | // Load FastClick, when loaded apply UI modifications.
42 | evothings.loadScript(
43 | 'libs/evothings/ui/fastclick.js',
44 | applyUIUpdatesWhenPageHasLoaded);
45 | })();
46 |
--------------------------------------------------------------------------------
/platformio.ini:
--------------------------------------------------------------------------------
1 | [env:esp32dev]
2 | platform = espressif32
3 | board = esp32dev
4 | framework = arduino
5 | monitor_speed = 115200
6 | upload_speed = 115200
7 |
8 | ; ESP32 Arduino Core - Using 2.0.14 for smaller binary size
9 | ; Note: Current version is 2.3.2, but this version was chosen for size optimization
10 | platform_packages =
11 | framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.14
12 |
13 | ; Required libraries - versions chosen for stability and compatibility
14 | lib_deps =
15 | fastled/FastLED @ ^3.6.0
16 | adafruit/Adafruit GFX Library @ ^1.11.5
17 | adafruit/Adafruit BusIO @ ^1.14.1
18 |
19 | ; Build flags
20 | build_flags =
21 | -D ESP32
22 | -D DEBUG
23 | -D MAX_MILLIAMPS=5000
24 | -D BLUETOOTH_NAME="Blume"
25 | -D WIDTH=2
26 | -D HEIGHT=66
27 | -D NUM_LEDS=131
28 | -D SKIP_FRONT=0
29 | -D MAX_FPS=120
30 | -D DISABLE_DITHER
31 | -D COLOR_ORDER=GRB
32 | -D CHIPSET=WS2812
33 | -D DATA_PIN_0=32
34 | -D DATA_PIN_1=33
35 | -D CLOCK_PIN_0=25
36 | -D CLOCK_PIN_1=26
37 | -D ROTATE
38 | -D STAGGERED
39 | -D BASE_WID=2
40 | -D PHYSICAL_WIDTH=2
41 | -D TEXTMODE
42 | -D FONT=Adafruit5x7
43 | -D TEXT_OFFSET=0
--------------------------------------------------------------------------------
/cordova/res/README.md:
--------------------------------------------------------------------------------
1 |
21 |
22 | Note that these image resources are not copied into a project when a project
23 | is created with the CLI. Although there are default image resources in a
24 | newly-created project, those come from the platform-specific project template,
25 | which can generally be found in the platform's `template` directory. Until
26 | icon and splashscreen support is added to the CLI, these image resources
27 | aren't used directly.
28 |
29 | See https://issues.apache.org/jira/browse/CB-5145
30 |
--------------------------------------------------------------------------------
/cordova/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "org.selfobserved.blume",
3 | "displayName": "Blume",
4 | "version": "1.0.0",
5 | "description": "A sample Apache Cordova application that responds to the deviceready event.",
6 | "main": "index.js",
7 | "scripts": {
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "author": "Apache Cordova Team",
11 | "license": "Apache-2.0",
12 | "dependencies": {
13 | "code-push": "^2.0.1-beta",
14 | "cordova-android": "^6.2.3",
15 | "cordova-plugin-ble": "git+https://github.com/hobzcalvin/cordova-ble.git",
16 | "cordova-plugin-code-push": "^1.9.5-beta",
17 | "cordova-plugin-compat": "^1.1.0",
18 | "cordova-plugin-device": "^1.1.6",
19 | "cordova-plugin-dialogs": "^1.3.3",
20 | "cordova-plugin-file": "^4.3.3",
21 | "cordova-plugin-file-transfer": "^1.6.3",
22 | "cordova-plugin-whitelist": "^1.3.2",
23 | "cordova-plugin-zip": "^3.1.0"
24 | },
25 | "cordova": {
26 | "plugins": {
27 | "cordova-plugin-code-push": {},
28 | "cordova-plugin-whitelist": {},
29 | "cordova-plugin-ble": {
30 | "BLUETOOTH_USAGE_DESCRIPTION": "This app would like to use Bluetooth to connect to nearby devices."
31 | }
32 | },
33 | "platforms": [
34 | "android",
35 | "ios"
36 | ]
37 | },
38 | "devDependencies": {
39 | "cordova-ios": "^7.1.1"
40 | }
41 | }
--------------------------------------------------------------------------------
/cordova/www/ui/images/menu.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
10 |
11 |
13 |
15 |
17 |
19 |
20 |
--------------------------------------------------------------------------------
/cordova/www/fonts/Rajdhani-Bold.woff2:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Error 404 (Not Found)!!1
6 |
9 |
10 | 404. That’s an error.
11 |
The requested URL /s/rajdhani/v15/LDI2apCSOBg7S-QT7q4tOeegPdo.woff2 was not found on this server. That’s all we know.
12 |
--------------------------------------------------------------------------------
/cordova/www/fonts/Rajdhani-Medium.woff2:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Error 404 (Not Found)!!1
6 |
9 |
10 | 404. That’s an error.
11 |
The requested URL /s/rajdhani/v15/LDI2apCSOBg7S-QT7q4AOuegPdo.woff2 was not found on this server. That’s all we know.
12 |
--------------------------------------------------------------------------------
/cordova/www/fonts/Rajdhani-SemiBold.woff2:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Error 404 (Not Found)!!1
6 |
9 |
10 | 404. That’s an error.
11 |
The requested URL /s/rajdhani/v15/LDI2apCSOBg7S-QT7q4YOOegPdo.woff2 was not found on this server. That’s all we know.
12 |
--------------------------------------------------------------------------------
/cordova/www/ui/fonts/ProximaNova.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Web Fonts from fontspring.com
3 | *
4 | * All OpenType features and all extended glyphs have been removed.
5 | * Fully installable fonts can be purchased at http://www.fontspring.com
6 | *
7 | * The fonts included in this stylesheet are subject to the End User License you purchased
8 | * from Fontspring. The fonts are protected under domestic and international trademark and
9 | * copyright law. You are prohibited from modifying, reverse engineering, duplicating, or
10 | * distributing this font software.
11 | *
12 | * (c) 2010-2014 Fontspring
13 | *
14 | *
15 | *
16 | *
17 | * The fonts included are copyrighted by the vendor listed below.
18 | *
19 | * Vendor: Mark Simonson Studio
20 | * License URL: http://www.fontspring.com/fflicense/mark-simonson-studio
21 | *
22 | *
23 | */
24 |
25 | @font-face {
26 | font-family: 'Proxima Nova Regular';
27 | src: url('ProximaNova-Reg-webfont.eot');
28 | src: url('ProximaNova-Reg-webfont.eot?#iefix') format('embedded-opentype'),
29 | url('ProximaNova-Reg-webfont.woff') format('woff'),
30 | url('ProximaNova-Reg-webfont.ttf') format('truetype'),
31 | url('ProximaNova-Reg-webfont.svg#proxima_nova_rgregular') format('svg');
32 | font-weight: normal;
33 | font-style: normal;
34 | }
35 |
36 | @font-face {
37 | font-family: 'Proxima Nova Bold';
38 | src: url('ProximaNova-Bold-webfont.eot');
39 | src: url('ProximaNova-Bold-webfont.eot?#iefix') format('embedded-opentype'),
40 | url('ProximaNova-Bold-webfont.woff') format('woff'),
41 | url('ProximaNova-Bold-webfont.ttf') format('truetype'),
42 | url('ProximaNova-Bold-webfont.svg#proxima_nova_rgbold') format('svg');
43 | font-weight: bold;
44 | font-style: normal;
45 | }
46 |
47 | @font-face {
48 | font-family: 'Proxima Nova Black';
49 | src: url('ProximaNova-Black-webfont.eot');
50 | src: url('ProximaNova-Black-webfont.eot?#iefix') format('embedded-opentype'),
51 | url('ProximaNova-Black-webfont.woff') format('woff'),
52 | url('ProximaNova-Black-webfont.ttf') format('truetype'),
53 | url('ProximaNova-Black-webfont.svg#proxima_nova_blblack') format('svg');
54 | font-weight: bolder;
55 | font-style: normal;
56 | }
57 |
--------------------------------------------------------------------------------
/BLEDeviceID.h:
--------------------------------------------------------------------------------
1 | /*
2 | BLEDeviceID.h - Bluetooth Low Energy Device ID Profile implementation.
3 | Copyright (c) 2017 by Grant Patterson. All rights reserved.
4 |
5 | This library is free software; you can redistribute it and/or
6 | modify it under the terms of the GNU Lesser General Public
7 | License as published by the Free Software Foundation; either
8 | version 2.1 of the License, or (at your option) any later version.
9 |
10 | This library is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public
16 | License along with this library; if not, write to the Free Software
17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 |
20 | #ifndef MAIN_BLEDeviceID_H_
21 | #define MAIN_BLEDeviceID_H_
22 | #include "sdkconfig.h"
23 | #if defined(CONFIG_BT_ENABLED)
24 | #include
25 | #include
26 |
27 | #include "BLEService.h"
28 |
29 | class BLEDeviceID {
30 | public:
31 | // Specify server and all values needed to implement the Device ID spec:
32 | // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.device_information.xml
33 | BLEDeviceID(
34 | BLEServer* server,
35 | std::string systemId, // 0x2A23, 64 bits, use strtoull format "0x..."
36 | std::string modelNumber, // 0x2A24
37 | std::string serialNumber, // 0x2A25
38 | std::string firmwareRevision, // 0x2A26
39 | std::string hardwareRevision, // 0x2A27
40 | std::string softwareRevision, // 0x2A28
41 | std::string manufacturerName, // 0x2A29
42 | std::string ieee11073_20601, // 0x2A2A, standard is behind paywall.
43 | // 0x2A50 PnP ID values:
44 | uint8_t vendorIdSource, // 1=Bluetooth SIG, 2=USB Impl. Forum
45 | uint16_t vendorId,
46 | uint16_t productId,
47 | uint16_t productVersion);
48 | void start();
49 |
50 | protected:
51 | BLEService* service;
52 | };
53 |
54 | #endif // CONFIG_BT_ENABLED
55 | #endif /* MAIN_BLEDeviceID_H_ */
56 |
--------------------------------------------------------------------------------
/BLESerial.h:
--------------------------------------------------------------------------------
1 | /*
2 | BLESerial.h - Bluetooth Low Energy Serial library.
3 | Copyright (c) 2017 by Grant Patterson. All rights reserved.
4 |
5 | This library is free software; you can redistribute it and/or
6 | modify it under the terms of the GNU Lesser General Public
7 | License as published by the Free Software Foundation; either
8 | version 2.1 of the License, or (at your option) any later version.
9 |
10 | This library is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public
16 | License along with this library; if not, write to the Free Software
17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 |
20 | #ifndef BLESerial_h
21 | #define BLESerial_h
22 |
23 | #include
24 | #include
25 |
26 | #include "BLECharacteristic.h"
27 | #include "BLEService.h"
28 | #include "Stream.h"
29 |
30 | class BLESerial: public Stream, public BLECharacteristicCallbacks {
31 | public:
32 | BLESerial();
33 |
34 | void begin(BLEServer* server, BLEUUID service_uuid, BLEUUID characteristic_uuid);
35 | void end();
36 | int available(void);
37 | int peek(void);
38 | int read(void);
39 | void flush(void);
40 | size_t write(uint8_t);
41 | size_t write(const uint8_t *buffer, size_t size);
42 |
43 | inline size_t write(const char * s) {
44 | return write((uint8_t*) s, strlen(s));
45 | }
46 | inline size_t write(unsigned long n) {
47 | return write((uint8_t) n);
48 | }
49 | inline size_t write(long n) {
50 | return write((uint8_t) n);
51 | }
52 | inline size_t write(unsigned int n) {
53 | return write((uint8_t) n);
54 | }
55 | inline size_t write(int n) {
56 | return write((uint8_t) n);
57 | }
58 | uint32_t baudRate();
59 | operator bool() const;
60 |
61 | void setDebugOutput(bool);
62 |
63 | void onWrite(BLECharacteristic* characteristic);
64 |
65 | protected:
66 | BLEService* service;
67 | BLECharacteristic* characteristic;
68 | std::string buffer;
69 | };
70 |
71 | #endif
72 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Blume LED Controller
2 |
3 | A flexible LED controller for ESP32 that supports Bluetooth Low Energy (BLE) communication and various LED patterns.
4 |
5 | ## Features
6 |
7 | - BLE control interface
8 | - Support for WS2812/APA102 LED strips
9 | - Multiple display modes and patterns
10 | - Text display mode
11 | - Demo mode with saved presets
12 | - Configurable dimensions and LED layout
13 |
14 | ## Hardware Requirements
15 |
16 | - ESP32 development board
17 | - WS2812 or APA102 LED strips
18 | - Power supply (5V, sufficient for your LED count)
19 |
20 | ## Software Setup
21 |
22 | ### Option 1: Using PlatformIO (Recommended)
23 |
24 | 1. Install [PlatformIO](https://platformio.org/install)
25 | 2. Clone this repository
26 | 3. Open the project in PlatformIO
27 | 4. Build and upload to your ESP32
28 |
29 | ### Option 2: Using Arduino IDE
30 |
31 | 1. Install [Arduino IDE](https://www.arduino.cc/en/software)
32 | 2. Install ESP32 board support:
33 | - Open Arduino IDE
34 | - Go to File > Preferences
35 | - Add `https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json` to Additional Boards Manager URLs
36 | - Go to Tools > Board > Boards Manager
37 | - Search for "esp32" and install "ESP32 by Espressif Systems"
38 | 3. Install required libraries:
39 | - FastLED (3.6.0 or later)
40 | - Adafruit GFX Library (1.11.5 or later)
41 | - Adafruit BusIO (1.14.1 or later)
42 | 4. Open `blume.ino` in Arduino IDE
43 | 5. Select your ESP32 board from Tools > Board
44 | 6. Build and upload
45 |
46 | ## Configuration
47 |
48 | The project can be configured through build flags in `platformio.ini` or by modifying the defines in the code:
49 |
50 | - `WIDTH` and `HEIGHT`: Physical dimensions of your LED matrix
51 | - `NUM_LEDS`: Total number of LEDs
52 | - `CHIPSET`: LED type (WS2812 or APA102)
53 | - `DATA_PIN_0`, `DATA_PIN_1`, `CLOCK_PIN_0`, `CLOCK_PIN_1`: Pin assignments
54 | - `MAX_MILLIAMPS`: Maximum current draw
55 | - `BLUETOOTH_NAME`: Name of the BLE device
56 |
57 | ## Usage
58 |
59 | 1. Power on the ESP32
60 | 2. Connect to the BLE device named "Blume" (or your configured name)
61 | 3. Send commands through the BLE interface:
62 | - `!D`: Request dimensions
63 | - `!d`: Demo mode commands
64 | - Other commands as documented in the code
65 |
66 | ## License
67 |
68 | [Add your license information here]
69 |
--------------------------------------------------------------------------------
/cordova/www/ui/images/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
14 |
18 |
22 |
23 |
25 |
26 |
27 |
28 |
29 |
31 |
32 |
33 |
34 |
35 |
38 |
39 |
--------------------------------------------------------------------------------
/BLESerial.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | BLESerial.cpp - Bluetooth Low Energy Serial library.
3 | Copyright (c) 2017 by Grant Patterson. All rights reserved.
4 |
5 | This library is free software; you can redistribute it and/or
6 | modify it under the terms of the GNU Lesser General Public
7 | License as published by the Free Software Foundation; either
8 | version 2.1 of the License, or (at your option) any later version.
9 |
10 | This library is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public
16 | License along with this library; if not, write to the Free Software
17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 |
20 | #ifdef ESP32
21 |
22 | #include "BLESerial.h"
23 |
24 | BLESerial::BLESerial() {
25 | }
26 |
27 | // From BLECharacteristicCallbacks
28 | void BLESerial::onWrite(BLECharacteristic* characteristic) {
29 | buffer += characteristic->getValue();
30 | }
31 |
32 | void BLESerial::begin(BLEServer* server, BLEUUID service_uuid, BLEUUID characteristic_uuid)
33 | {
34 | service = server->createService(service_uuid);
35 | buffer = "";
36 | characteristic = service->createCharacteristic(characteristic_uuid,
37 | BLECharacteristic::PROPERTY_READ |
38 | BLECharacteristic::PROPERTY_WRITE |
39 | BLECharacteristic::PROPERTY_NOTIFY);
40 | BLEDescriptor* desc = new BLEDescriptor(BLEUUID((uint16_t)0x2901));
41 | desc->setValue("Serial");
42 | characteristic->addDescriptor(desc);
43 | characteristic->setCallbacks(this);
44 | service->start();
45 | }
46 |
47 | void BLESerial::end() {
48 | }
49 |
50 | void BLESerial::setDebugOutput(bool en) {
51 | }
52 |
53 | int BLESerial::available(void) {
54 | return buffer.size();
55 | }
56 |
57 | int BLESerial::peek(void) {
58 | if (available()) {
59 | return (int)buffer[0];
60 | }
61 | return -1;
62 | }
63 |
64 | int BLESerial::read(void) {
65 | if (available()) {
66 | int result = (int)buffer[0];
67 | buffer.erase(0, 1);
68 | return result;
69 | }
70 | return -1;
71 | }
72 |
73 | void BLESerial::flush() {
74 | // Pretty sure this is supposed to go both ways in Arduino.
75 | buffer.clear();
76 | }
77 |
78 | size_t BLESerial::write(uint8_t c) {
79 | characteristic->setValue(&c, 1);
80 | characteristic->notify();
81 | return 1;
82 | }
83 |
84 | size_t BLESerial::write(const uint8_t *buffer, size_t size) {
85 | characteristic->setValue((uint8_t*)buffer, size);
86 | characteristic->notify();
87 | return size;
88 | }
89 |
90 | uint32_t BLESerial::baudRate() {
91 | return 0;
92 | }
93 |
94 | BLESerial::operator bool() const {
95 | return true;
96 | }
97 |
98 | #endif // ESP32
99 |
--------------------------------------------------------------------------------
/cordova/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Blume
4 |
5 | An app to control LEDs based on the Blume Bluetooth platform.
6 |
7 |
8 | Grant Patterson
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/BLEDeviceID.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | BLEDeviceID.cpp - Bluetooth Low Energy Device ID Profile implementation.
3 | Copyright (c) 2017 by Grant Patterson. All rights reserved.
4 |
5 | This library is free software; you can redistribute it and/or
6 | modify it under the terms of the GNU Lesser General Public
7 | License as published by the Free Software Foundation; either
8 | version 2.1 of the License, or (at your option) any later version.
9 |
10 | This library is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public
16 | License along with this library; if not, write to the Free Software
17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 | */
19 | #ifdef ESP32
20 | #include "sdkconfig.h"
21 | #if defined(CONFIG_BT_ENABLED)
22 | #include
23 |
24 | #include "BLEDeviceID.h"
25 |
26 | BLEDeviceID::BLEDeviceID(
27 | BLEServer* server,
28 | std::string systemId, // 0x2A23, 64 bits, use strtoull format "0x..."
29 | std::string modelNumber, // 0x2A24
30 | std::string serialNumber, // 0x2A25
31 | std::string firmwareRevision, // 0x2A26
32 | std::string hardwareRevision, // 0x2A27
33 | std::string softwareRevision, // 0x2A28
34 | std::string manufacturerName, // 0x2A29
35 | std::string ieee11073_20601, // 0x2A2A, standard is behind paywall.
36 | // 0x2A50 PnP ID values:
37 | uint8_t vendorIdSource, // 1=Bluetooth SIG, 2=USB Impl. Forum
38 | uint16_t vendorId,
39 | uint16_t productId,
40 | uint16_t productVersion) {
41 | // Need space for 20 handles to hold all the characteristics we have.
42 | service = server->createService(BLEUUID((uint16_t)0x180A), 20);
43 |
44 | // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.system_id.xml
45 | // Use strtoull to convert string to 64-bit value.
46 | // systemId should probably be of format "0x0123456789ABCDEF"
47 | unsigned long long systemIdLong = strtoull(systemId.c_str(), NULL, 0);
48 | // Store 64 bits as byte stream; string is convenient
49 | systemId = std::string((char*)&systemIdLong, sizeof(systemIdLong));
50 | // Need to reverse string to correct for bigendian
51 | systemId = std::string(systemId.rbegin(), systemId.rend());
52 | service->createCharacteristic(
53 | BLEUUID((uint16_t)0x2A23),
54 | BLECharacteristic::PROPERTY_READ)
55 | ->setValue(systemId);
56 |
57 | // Straightforward string values.
58 | service->createCharacteristic(
59 | BLEUUID((uint16_t)0x2A24),
60 | BLECharacteristic::PROPERTY_READ)
61 | ->setValue(modelNumber);
62 | service->createCharacteristic(
63 | BLEUUID((uint16_t)0x2A25),
64 | BLECharacteristic::PROPERTY_READ)
65 | ->setValue(serialNumber);
66 | service->createCharacteristic(
67 | BLEUUID((uint16_t)0x2A26),
68 | BLECharacteristic::PROPERTY_READ)
69 | ->setValue(firmwareRevision);
70 | service->createCharacteristic(
71 | BLEUUID((uint16_t)0x2A27),
72 | BLECharacteristic::PROPERTY_READ)
73 | ->setValue(hardwareRevision);
74 | service->createCharacteristic(
75 | BLEUUID((uint16_t)0x2A28),
76 | BLECharacteristic::PROPERTY_READ)
77 | ->setValue(softwareRevision);
78 | service->createCharacteristic(
79 | BLEUUID((uint16_t)0x2A29),
80 | BLECharacteristic::PROPERTY_READ)
81 | ->setValue(manufacturerName);
82 | // Format is mysterious.
83 | // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.ieee_11073-20601_regulatory_certification_data_list.xml
84 | service->createCharacteristic(
85 | BLEUUID((uint16_t)0x2A2A),
86 | BLECharacteristic::PROPERTY_READ)
87 | ->setValue(ieee11073_20601);
88 |
89 | // Per spec, order these from least-significant octet to most.
90 | // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.pnp_id.xml
91 | uint8_t val2a50[7];
92 | val2a50[6] = vendorIdSource;
93 | val2a50[5] = vendorId;
94 | val2a50[4] = vendorId >> 8;
95 | val2a50[3] = productId;
96 | val2a50[2] = productId >> 8;
97 | val2a50[1] = productVersion;
98 | val2a50[0] = productVersion >> 8;
99 | service->createCharacteristic(
100 | BLEUUID((uint16_t)0x2A50),
101 | BLECharacteristic::PROPERTY_READ)
102 | ->setValue(val2a50, sizeof(val2a50));
103 | }
104 |
105 | void BLEDeviceID::start() {
106 | service->start();
107 | }
108 |
109 | #endif // CONFIG_BT_ENABLED
110 | #endif // ESP32
111 |
--------------------------------------------------------------------------------
/cordova/www/libs/nouislider/nouislider.css:
--------------------------------------------------------------------------------
1 | /*! nouislider - 10.0.0 - 2017-05-28 14:52:48 */
2 | /* Functional styling;
3 | * These styles are required for noUiSlider to function.
4 | * You don't need to change these rules to apply your design.
5 | */
6 | .noUi-target,
7 | .noUi-target * {
8 | -webkit-touch-callout: none;
9 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
10 | -webkit-user-select: none;
11 | -ms-touch-action: none;
12 | touch-action: none;
13 | -ms-user-select: none;
14 | -moz-user-select: none;
15 | user-select: none;
16 | -moz-box-sizing: border-box;
17 | box-sizing: border-box;
18 | }
19 | .noUi-target {
20 | position: relative;
21 | direction: ltr;
22 | }
23 | .noUi-base {
24 | width: 100%;
25 | height: 100%;
26 | position: relative;
27 | z-index: 1;
28 | /* Fix 401 */
29 | }
30 | .noUi-connect {
31 | position: absolute;
32 | right: 0;
33 | top: 0;
34 | left: 0;
35 | bottom: 0;
36 | }
37 | .noUi-origin {
38 | position: absolute;
39 | height: 0;
40 | width: 0;
41 | }
42 | .noUi-handle {
43 | position: relative;
44 | z-index: 1;
45 | }
46 | .noUi-state-tap .noUi-connect,
47 | .noUi-state-tap .noUi-origin {
48 | -webkit-transition: top 0.3s, right 0.3s, bottom 0.3s, left 0.3s;
49 | transition: top 0.3s, right 0.3s, bottom 0.3s, left 0.3s;
50 | }
51 | .noUi-state-drag * {
52 | cursor: inherit !important;
53 | }
54 | /* Painting and performance;
55 | * Browsers can paint handles in their own layer.
56 | */
57 | .noUi-base,
58 | .noUi-handle {
59 | -webkit-transform: translate3d(0, 0, 0);
60 | transform: translate3d(0, 0, 0);
61 | }
62 | /* Slider size and handle placement;
63 | */
64 | .noUi-horizontal {
65 | height: 18px;
66 | }
67 | .noUi-horizontal .noUi-handle {
68 | width: 34px;
69 | height: 28px;
70 | left: -17px;
71 | top: -6px;
72 | }
73 | .noUi-vertical {
74 | width: 18px;
75 | }
76 | .noUi-vertical .noUi-handle {
77 | width: 28px;
78 | height: 34px;
79 | left: -6px;
80 | top: -17px;
81 | }
82 | /* Styling;
83 | */
84 | .noUi-target {
85 | background: #FAFAFA;
86 | border-radius: 4px;
87 | border: 1px solid #D3D3D3;
88 | box-shadow: inset 0 1px 1px #F0F0F0, 0 3px 6px -5px #BBB;
89 | }
90 | .noUi-connect {
91 | background: #3FB8AF;
92 | border-radius: 4px;
93 | box-shadow: inset 0 0 3px rgba(51, 51, 51, 0.45);
94 | -webkit-transition: background 450ms;
95 | transition: background 450ms;
96 | }
97 | /* Handles and cursors;
98 | */
99 | .noUi-draggable {
100 | cursor: ew-resize;
101 | }
102 | .noUi-vertical .noUi-draggable {
103 | cursor: ns-resize;
104 | }
105 | .noUi-handle {
106 | border: 1px solid #D9D9D9;
107 | border-radius: 3px;
108 | background: #FFF;
109 | cursor: default;
110 | box-shadow: inset 0 0 1px #FFF, inset 0 1px 7px #EBEBEB, 0 3px 6px -3px #BBB;
111 | }
112 | .noUi-active {
113 | box-shadow: inset 0 0 1px #FFF, inset 0 1px 7px #DDD, 0 3px 6px -3px #BBB;
114 | }
115 | /* Handle stripes;
116 | */
117 | .noUi-handle:before,
118 | .noUi-handle:after {
119 | content: "";
120 | display: block;
121 | position: absolute;
122 | height: 14px;
123 | width: 1px;
124 | background: #E8E7E6;
125 | left: 14px;
126 | top: 6px;
127 | }
128 | .noUi-handle:after {
129 | left: 17px;
130 | }
131 | .noUi-vertical .noUi-handle:before,
132 | .noUi-vertical .noUi-handle:after {
133 | width: 14px;
134 | height: 1px;
135 | left: 6px;
136 | top: 14px;
137 | }
138 | .noUi-vertical .noUi-handle:after {
139 | top: 17px;
140 | }
141 | /* Disabled state;
142 | */
143 | [disabled] .noUi-connect {
144 | background: #B8B8B8;
145 | }
146 | [disabled].noUi-target,
147 | [disabled].noUi-handle,
148 | [disabled] .noUi-handle {
149 | cursor: not-allowed;
150 | }
151 | /* Base;
152 | *
153 | */
154 | .noUi-pips,
155 | .noUi-pips * {
156 | -moz-box-sizing: border-box;
157 | box-sizing: border-box;
158 | }
159 | .noUi-pips {
160 | position: absolute;
161 | color: #999;
162 | }
163 | /* Values;
164 | *
165 | */
166 | .noUi-value {
167 | position: absolute;
168 | white-space: nowrap;
169 | text-align: center;
170 | }
171 | .noUi-value-sub {
172 | color: #ccc;
173 | font-size: 10px;
174 | }
175 | /* Markings;
176 | *
177 | */
178 | .noUi-marker {
179 | position: absolute;
180 | background: #CCC;
181 | }
182 | .noUi-marker-sub {
183 | background: #AAA;
184 | }
185 | .noUi-marker-large {
186 | background: #AAA;
187 | }
188 | /* Horizontal layout;
189 | *
190 | */
191 | .noUi-pips-horizontal {
192 | padding: 10px 0;
193 | height: 80px;
194 | top: 100%;
195 | left: 0;
196 | width: 100%;
197 | }
198 | .noUi-value-horizontal {
199 | -webkit-transform: translate3d(-50%, 50%, 0);
200 | transform: translate3d(-50%, 50%, 0);
201 | }
202 | .noUi-marker-horizontal.noUi-marker {
203 | margin-left: -1px;
204 | width: 2px;
205 | height: 5px;
206 | }
207 | .noUi-marker-horizontal.noUi-marker-sub {
208 | height: 10px;
209 | }
210 | .noUi-marker-horizontal.noUi-marker-large {
211 | height: 15px;
212 | }
213 | /* Vertical layout;
214 | *
215 | */
216 | .noUi-pips-vertical {
217 | padding: 0 10px;
218 | height: 100%;
219 | top: 0;
220 | left: 100%;
221 | }
222 | .noUi-value-vertical {
223 | -webkit-transform: translate3d(0, 50%, 0);
224 | transform: translate3d(0, 50%, 0);
225 | padding-left: 25px;
226 | }
227 | .noUi-marker-vertical.noUi-marker {
228 | width: 5px;
229 | height: 2px;
230 | margin-top: -1px;
231 | }
232 | .noUi-marker-vertical.noUi-marker-sub {
233 | width: 10px;
234 | }
235 | .noUi-marker-vertical.noUi-marker-large {
236 | width: 15px;
237 | }
238 | .noUi-tooltip {
239 | display: block;
240 | position: absolute;
241 | border: 1px solid #D9D9D9;
242 | border-radius: 3px;
243 | background: #fff;
244 | color: #000;
245 | padding: 5px;
246 | text-align: center;
247 | white-space: nowrap;
248 | }
249 | .noUi-horizontal .noUi-tooltip {
250 | -webkit-transform: translate(-50%, 0);
251 | transform: translate(-50%, 0);
252 | left: 50%;
253 | bottom: 120%;
254 | }
255 | .noUi-vertical .noUi-tooltip {
256 | -webkit-transform: translate(0, -50%);
257 | transform: translate(0, -50%);
258 | top: 50%;
259 | right: 120%;
260 | }
261 |
--------------------------------------------------------------------------------
/Picopixel.h:
--------------------------------------------------------------------------------
1 | // Picopixel by Sebastian Weber. A tiny font
2 | // with all characters within a 6 pixel height.
3 |
4 | const uint8_t PicopixelBitmaps[] PROGMEM = {
5 | 0xE8, 0xB4, 0x57, 0xD5, 0xF5, 0x00, 0x4E, 0x3E, 0x80, 0xA5, 0x4A, 0x4A,
6 | 0x5A, 0x50, 0xC0, 0x6A, 0x40, 0x95, 0x80, 0xAA, 0x80, 0x5D, 0x00, 0x60,
7 | 0xE0, 0x80, 0x25, 0x48, 0x56, 0xD4, 0x75, 0x40, 0xC5, 0x4E, 0xC5, 0x1C,
8 | 0x97, 0x92, 0xF3, 0x1C, 0x53, 0x54, 0xE5, 0x48, 0x55, 0x54, 0x55, 0x94,
9 | 0xA0, 0x46, 0x64, 0xE3, 0x80, 0x98, 0xC5, 0x04, 0x56, 0xC6, 0x57, 0xDA,
10 | 0xD7, 0x5C, 0x72, 0x46, 0xD6, 0xDC, 0xF3, 0xCE, 0xF3, 0x48, 0x72, 0xD4,
11 | 0xB7, 0xDA, 0xF8, 0x24, 0xD4, 0xBB, 0x5A, 0x92, 0x4E, 0x8E, 0xEB, 0x58,
12 | 0x80, 0x9D, 0xB9, 0x90, 0x56, 0xD4, 0xD7, 0x48, 0x56, 0xD4, 0x40, 0xD7,
13 | 0x5A, 0x71, 0x1C, 0xE9, 0x24, 0xB6, 0xD4, 0xB6, 0xA4, 0x8C, 0x6B, 0x55,
14 | 0x00, 0xB5, 0x5A, 0xB5, 0x24, 0xE5, 0x4E, 0xEA, 0xC0, 0x91, 0x12, 0xD5,
15 | 0xC0, 0x54, 0xF0, 0x90, 0xC7, 0xF0, 0x93, 0x5E, 0x71, 0x80, 0x25, 0xDE,
16 | 0x5E, 0x30, 0x6E, 0x80, 0x77, 0x9C, 0x93, 0x5A, 0xB8, 0x45, 0x60, 0x92,
17 | 0xEA, 0xAA, 0x40, 0xD5, 0x6A, 0xD6, 0x80, 0x55, 0x00, 0xD7, 0x40, 0x75,
18 | 0x90, 0xE8, 0x71, 0xE0, 0xBA, 0x40, 0xB5, 0x80, 0xB5, 0x00, 0x8D, 0x54,
19 | 0xAA, 0x80, 0xAC, 0xE0, 0xE5, 0x70, 0x6A, 0x26, 0xFC, 0xC8, 0xAC, 0x5A };
20 |
21 | const GFXglyph PicopixelGlyphs[] PROGMEM = {
22 | { 0, 0, 0, 2, 0, 1 }, // 0x20 ' '
23 | { 0, 1, 5, 2, 0, -4 }, // 0x21 '!'
24 | { 1, 3, 2, 4, 0, -4 }, // 0x22 '"'
25 | { 2, 5, 5, 6, 0, -4 }, // 0x23 '#'
26 | { 6, 3, 6, 4, 0, -4 }, // 0x24 '$'
27 | { 9, 3, 5, 4, 0, -4 }, // 0x25 '%'
28 | { 11, 4, 5, 5, 0, -4 }, // 0x26 '&'
29 | { 14, 1, 2, 2, 0, -4 }, // 0x27 '''
30 | { 15, 2, 5, 3, 0, -4 }, // 0x28 '('
31 | { 17, 2, 5, 3, 0, -4 }, // 0x29 ')'
32 | { 19, 3, 3, 4, 0, -3 }, // 0x2A '*'
33 | { 21, 3, 3, 4, 0, -3 }, // 0x2B '+'
34 | { 23, 2, 2, 3, 0, 0 }, // 0x2C ','
35 | { 24, 3, 1, 4, 0, -2 }, // 0x2D '-'
36 | { 25, 1, 1, 2, 0, 0 }, // 0x2E '.'
37 | { 26, 3, 5, 4, 0, -4 }, // 0x2F '/'
38 | { 28, 3, 5, 4, 0, -4 }, // 0x30 '0'
39 | { 30, 2, 5, 3, 0, -4 }, // 0x31 '1'
40 | { 32, 3, 5, 4, 0, -4 }, // 0x32 '2'
41 | { 34, 3, 5, 4, 0, -4 }, // 0x33 '3'
42 | { 36, 3, 5, 4, 0, -4 }, // 0x34 '4'
43 | { 38, 3, 5, 4, 0, -4 }, // 0x35 '5'
44 | { 40, 3, 5, 4, 0, -4 }, // 0x36 '6'
45 | { 42, 3, 5, 4, 0, -4 }, // 0x37 '7'
46 | { 44, 3, 5, 4, 0, -4 }, // 0x38 '8'
47 | { 46, 3, 5, 4, 0, -4 }, // 0x39 '9'
48 | { 48, 1, 3, 2, 0, -3 }, // 0x3A ':'
49 | { 49, 2, 4, 3, 0, -3 }, // 0x3B ';'
50 | { 50, 2, 3, 3, 0, -3 }, // 0x3C '<'
51 | { 51, 3, 3, 4, 0, -3 }, // 0x3D '='
52 | { 53, 2, 3, 3, 0, -3 }, // 0x3E '>'
53 | { 54, 3, 5, 4, 0, -4 }, // 0x3F '?'
54 | { 56, 3, 5, 4, 0, -4 }, // 0x40 '@'
55 | { 58, 3, 5, 4, 0, -4 }, // 0x41 'A'
56 | { 60, 3, 5, 4, 0, -4 }, // 0x42 'B'
57 | { 62, 3, 5, 4, 0, -4 }, // 0x43 'C'
58 | { 64, 3, 5, 4, 0, -4 }, // 0x44 'D'
59 | { 66, 3, 5, 4, 0, -4 }, // 0x45 'E'
60 | { 68, 3, 5, 4, 0, -4 }, // 0x46 'F'
61 | { 70, 3, 5, 4, 0, -4 }, // 0x47 'G'
62 | { 72, 3, 5, 4, 0, -4 }, // 0x48 'H'
63 | { 74, 1, 5, 2, 0, -4 }, // 0x49 'I'
64 | { 75, 3, 5, 4, 0, -4 }, // 0x4A 'J'
65 | { 77, 3, 5, 4, 0, -4 }, // 0x4B 'K'
66 | { 79, 3, 5, 4, 0, -4 }, // 0x4C 'L'
67 | { 81, 5, 5, 6, 0, -4 }, // 0x4D 'M'
68 | { 85, 4, 5, 5, 0, -4 }, // 0x4E 'N'
69 | { 88, 3, 5, 4, 0, -4 }, // 0x4F 'O'
70 | { 90, 3, 5, 4, 0, -4 }, // 0x50 'P'
71 | { 92, 3, 6, 4, 0, -4 }, // 0x51 'Q'
72 | { 95, 3, 5, 4, 0, -4 }, // 0x52 'R'
73 | { 97, 3, 5, 4, 0, -4 }, // 0x53 'S'
74 | { 99, 3, 5, 4, 0, -4 }, // 0x54 'T'
75 | { 101, 3, 5, 4, 0, -4 }, // 0x55 'U'
76 | { 103, 3, 5, 4, 0, -4 }, // 0x56 'V'
77 | { 105, 5, 5, 6, 0, -4 }, // 0x57 'W'
78 | { 109, 3, 5, 4, 0, -4 }, // 0x58 'X'
79 | { 111, 3, 5, 4, 0, -4 }, // 0x59 'Y'
80 | { 113, 3, 5, 4, 0, -4 }, // 0x5A 'Z'
81 | { 115, 2, 5, 3, 0, -4 }, // 0x5B '['
82 | { 117, 3, 5, 4, 0, -4 }, // 0x5C '\'
83 | { 119, 2, 5, 3, 0, -4 }, // 0x5D ']'
84 | { 121, 3, 2, 4, 0, -4 }, // 0x5E '^'
85 | { 122, 4, 1, 4, 0, 1 }, // 0x5F '_'
86 | { 123, 2, 2, 3, 0, -4 }, // 0x60 '`'
87 | { 124, 3, 4, 4, 0, -3 }, // 0x61 'a'
88 | { 126, 3, 5, 4, 0, -4 }, // 0x62 'b'
89 | { 128, 3, 3, 4, 0, -2 }, // 0x63 'c'
90 | { 130, 3, 5, 4, 0, -4 }, // 0x64 'd'
91 | { 132, 3, 4, 4, 0, -3 }, // 0x65 'e'
92 | { 134, 2, 5, 3, 0, -4 }, // 0x66 'f'
93 | { 136, 3, 5, 4, 0, -3 }, // 0x67 'g'
94 | { 138, 3, 5, 4, 0, -4 }, // 0x68 'h'
95 | { 140, 1, 5, 2, 0, -4 }, // 0x69 'i'
96 | { 141, 2, 6, 3, 0, -4 }, // 0x6A 'j'
97 | { 143, 3, 5, 4, 0, -4 }, // 0x6B 'k'
98 | { 145, 2, 5, 3, 0, -4 }, // 0x6C 'l'
99 | { 147, 5, 3, 6, 0, -2 }, // 0x6D 'm'
100 | { 149, 3, 3, 4, 0, -2 }, // 0x6E 'n'
101 | { 151, 3, 3, 4, 0, -2 }, // 0x6F 'o'
102 | { 153, 3, 4, 4, 0, -2 }, // 0x70 'p'
103 | { 155, 3, 4, 4, 0, -2 }, // 0x71 'q'
104 | { 157, 2, 3, 3, 0, -2 }, // 0x72 'r'
105 | { 158, 3, 4, 4, 0, -3 }, // 0x73 's'
106 | { 160, 2, 5, 3, 0, -4 }, // 0x74 't'
107 | { 162, 3, 3, 4, 0, -2 }, // 0x75 'u'
108 | { 164, 3, 3, 4, 0, -2 }, // 0x76 'v'
109 | { 166, 5, 3, 6, 0, -2 }, // 0x77 'w'
110 | { 168, 3, 3, 4, 0, -2 }, // 0x78 'x'
111 | { 170, 3, 4, 4, 0, -2 }, // 0x79 'y'
112 | { 172, 3, 4, 4, 0, -3 }, // 0x7A 'z'
113 | { 174, 3, 5, 4, 0, -4 }, // 0x7B '{'
114 | { 176, 1, 6, 2, 0, -4 }, // 0x7C '|'
115 | { 177, 3, 5, 4, 0, -4 }, // 0x7D '}'
116 | { 179, 4, 2, 5, 0, -3 } }; // 0x7E '~'
117 |
118 | const GFXfont Picopixel PROGMEM = {
119 | (uint8_t *)PicopixelBitmaps,
120 | (GFXglyph *)PicopixelGlyphs,
121 | 0x20, 0x7E, 7 };
122 |
123 | // Approx. 852 bytes
124 |
--------------------------------------------------------------------------------
/cordova/www/libs/evothings/util/util.js:
--------------------------------------------------------------------------------
1 | // File: util.js
2 |
3 | evothings = window.evothings || {};
4 |
5 | /**
6 | * @namespace
7 | * @author Aaron Ardiri
8 | * @author Fredrik Eldh
9 | * @description Utilities for byte arrays.
10 | */
11 | evothings.util = {};
12 |
13 | ;(function()
14 | {
15 | /**
16 | * Interpret byte buffer as little endian 8 bit integer.
17 | * Returns converted number.
18 | * @param {ArrayBuffer} data - Input buffer.
19 | * @param {number} offset - Start of data.
20 | * @return Converted number.
21 | * @public
22 | */
23 | evothings.util.littleEndianToInt8 = function(data, offset)
24 | {
25 | var x = evothings.util.littleEndianToUint8(data, offset)
26 | if (x & 0x80) x = x - 256
27 | return x
28 | }
29 |
30 | /**
31 | * Interpret byte buffer as unsigned little endian 8 bit integer.
32 | * Returns converted number.
33 | * @param {ArrayBuffer} data - Input buffer.
34 | * @param {number} offset - Start of data.
35 | * @return Converted number.
36 | * @public
37 | */
38 | evothings.util.littleEndianToUint8 = function(data, offset)
39 | {
40 | return data[offset]
41 | }
42 |
43 | /**
44 | * Interpret byte buffer as little endian 16 bit integer.
45 | * Returns converted number.
46 | * @param {ArrayBuffer} data - Input buffer.
47 | * @param {number} offset - Start of data.
48 | * @return Converted number.
49 | * @public
50 | */
51 | evothings.util.littleEndianToInt16 = function(data, offset)
52 | {
53 | return (evothings.util.littleEndianToInt8(data, offset + 1) << 8) +
54 | evothings.util.littleEndianToUint8(data, offset)
55 | }
56 |
57 | /**
58 | * Interpret byte buffer as unsigned little endian 16 bit integer.
59 | * Returns converted number.
60 | * @param {ArrayBuffer} data - Input buffer.
61 | * @param {number} offset - Start of data.
62 | * @return Converted number.
63 | * @public
64 | */
65 | evothings.util.littleEndianToUint16 = function(data, offset)
66 | {
67 | return (evothings.util.littleEndianToUint8(data, offset + 1) << 8) +
68 | evothings.util.littleEndianToUint8(data, offset)
69 | }
70 |
71 | /**
72 | * Interpret byte buffer as unsigned little endian 32 bit integer.
73 | * Returns converted number.
74 | * @param {ArrayBuffer} data - Input buffer.
75 | * @param {number} offset - Start of data.
76 | * @return Converted number.
77 | * @public
78 | */
79 | evothings.util.littleEndianToUint32 = function(data, offset)
80 | {
81 | return (evothings.util.littleEndianToUint8(data, offset + 3) << 24) +
82 | (evothings.util.littleEndianToUint8(data, offset + 2) << 16) +
83 | (evothings.util.littleEndianToUint8(data, offset + 1) << 8) +
84 | evothings.util.littleEndianToUint8(data, offset)
85 | }
86 |
87 |
88 | /**
89 | * Interpret byte buffer as signed big endian 16 bit integer.
90 | * Returns converted number.
91 | * @param {ArrayBuffer} data - Input buffer.
92 | * @param {number} offset - Start of data.
93 | * @return Converted number.
94 | * @public
95 | */
96 | evothings.util.bigEndianToInt16 = function(data, offset)
97 | {
98 | return (evothings.util.littleEndianToInt8(data, offset) << 8) +
99 | evothings.util.littleEndianToUint8(data, offset + 1)
100 | }
101 |
102 | /**
103 | * Interpret byte buffer as unsigned big endian 16 bit integer.
104 | * Returns converted number.
105 | * @param {ArrayBuffer} data - Input buffer.
106 | * @param {number} offset - Start of data.
107 | * @return Converted number.
108 | * @public
109 | */
110 | evothings.util.bigEndianToUint16 = function(data, offset)
111 | {
112 | return (evothings.util.littleEndianToUint8(data, offset) << 8) +
113 | evothings.util.littleEndianToUint8(data, offset + 1)
114 | }
115 |
116 | /**
117 | * Interpret byte buffer as unsigned big endian 32 bit integer.
118 | * Returns converted number.
119 | * @param {ArrayBuffer} data - Input buffer.
120 | * @param {number} offset - Start of data.
121 | * @return Converted number.
122 | * @public
123 | */
124 | evothings.util.bigEndianToUint32 = function(data, offset)
125 | {
126 | return (evothings.util.littleEndianToUint8(data, offset) << 24) +
127 | (evothings.util.littleEndianToUint8(data, offset + 1) << 16) +
128 | (evothings.util.littleEndianToUint8(data, offset + 2) << 8) +
129 | evothings.util.littleEndianToUint8(data, offset + 3)
130 | }
131 |
132 | /**
133 | * Converts a single Base64 character to a 6-bit integer.
134 | * @private
135 | */
136 | function b64ToUint6(nChr) {
137 | return nChr > 64 && nChr < 91 ?
138 | nChr - 65
139 | : nChr > 96 && nChr < 123 ?
140 | nChr - 71
141 | : nChr > 47 && nChr < 58 ?
142 | nChr + 4
143 | : nChr === 43 ?
144 | 62
145 | : nChr === 47 ?
146 | 63
147 | :
148 | 0;
149 | }
150 |
151 | /**
152 | * Decodes a Base64 string. Returns a Uint8Array.
153 | * nBlocksSize is optional.
154 | * @param {String} sBase64
155 | * @param {int} nBlocksSize
156 | * @return {Uint8Array}
157 | * @public
158 | */
159 | evothings.util.base64DecToArr = function(sBase64, nBlocksSize) {
160 | var sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, "");
161 | var nInLen = sB64Enc.length;
162 | var nOutLen = nBlocksSize ?
163 | Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize
164 | : nInLen * 3 + 1 >> 2;
165 | var taBytes = new Uint8Array(nOutLen);
166 |
167 | for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
168 | nMod4 = nInIdx & 3;
169 | nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4;
170 | if (nMod4 === 3 || nInLen - nInIdx === 1) {
171 | for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
172 | taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
173 | }
174 | nUint24 = 0;
175 | }
176 | }
177 |
178 | return taBytes;
179 | }
180 |
181 | /**
182 | * Returns the integer i in hexadecimal string form,
183 | * with leading zeroes, such that
184 | * the resulting string is at least byteCount*2 characters long.
185 | * @param {int} i
186 | * @param {int} byteCount
187 | * @public
188 | */
189 | evothings.util.toHexString = function(i, byteCount) {
190 | var string = (new Number(i)).toString(16);
191 | while(string.length < byteCount*2) {
192 | string = '0'+string;
193 | }
194 | return string;
195 | }
196 |
197 | /**
198 | * Takes a ArrayBuffer or TypedArray and returns its hexadecimal representation.
199 | * No spaces or linebreaks.
200 | * @param data
201 | * @public
202 | */
203 | evothings.util.typedArrayToHexString = function(data) {
204 | // view data as a Uint8Array, unless it already is one.
205 | if(data.buffer) {
206 | if(!(data instanceof Uint8Array))
207 | data = new Uint8Array(data.buffer);
208 | } else if(data instanceof ArrayBuffer) {
209 | data = new Uint8Array(data);
210 | } else {
211 | throw "not an ArrayBuffer or TypedArray.";
212 | }
213 | var str = '';
214 | for(var i=0; iFunctions for loading scripts asynchronously,
12 | * detecting platform, and other common application functionality.
13 | * @alias evothings
14 | * @public
15 | */
16 | var evothings = window.evothings;
17 |
18 | /* ------------------ Script loading ------------------ */
19 |
20 | var mScriptLoadingCounter = 0;
21 | var mLoadedScripts = {};
22 | var mScriptsLoadedCallbacks = [];
23 |
24 | /**
25 | * Make sure to catch any DOMContentLoaded events occurring before
26 | * asynchronous loading of scripts. Those scripts, like ui.js, should check
27 | * this variable before listening for the event.
28 | */
29 | evothings.gotDOMContentLoaded = false;
30 |
31 | window.addEventListener('DOMContentLoaded', function(e)
32 | {
33 | evothings.gotDOMContentLoaded = true;
34 | })
35 |
36 | /**
37 | * Load a script.
38 | * @param {string} url - URL or path to the script. Relative paths are
39 | * relative to the HTML file that initiated script loading.
40 | * @param {function} successCallback - Optional parameterless function that
41 | * will be called when the script has loaded.
42 | * @param {function} errorCallback - Optional function that will be called
43 | * if loading the script fails, takes an error object as parameter.
44 | * @public
45 | */
46 | evothings.loadScript = function(url, successCallback, errorCallback)
47 | {
48 | // If script is already loaded call callback directly and return.
49 | if (mLoadedScripts[url] == 'loadingcomplete')
50 | {
51 | successCallback && successCallback();
52 | return;
53 | }
54 |
55 | // Add script to dictionary of loaded scripts.
56 | mLoadedScripts[url] = 'loadingstarted';
57 | ++mScriptLoadingCounter;
58 |
59 | // Create script tag.
60 | var script = document.createElement('script');
61 | script.type = 'text/javascript';
62 | script.src = url;
63 |
64 | // Bind the onload event.
65 | script.onload = function()
66 | {
67 | // Mark as loaded.
68 | mLoadedScripts[url] = 'loadingcomplete';
69 | --mScriptLoadingCounter;
70 |
71 | // Call success callback if given.
72 | successCallback && successCallback();
73 |
74 | // Call scripts loaded callbacks if this was the last script loaded.
75 | if (0 == mScriptLoadingCounter)
76 | {
77 | for (var i = 0; i < mScriptsLoadedCallbacks.length; ++i)
78 | {
79 | var loadedCallback = mScriptsLoadedCallbacks[i];
80 | loadedCallback && loadedCallback();
81 | }
82 |
83 | // Clear callbacks - should we do this???
84 | mScriptsLoadedCallbacks = [];
85 | }
86 | };
87 |
88 | // onerror fires for things like malformed URLs and 404's.
89 | // If this function is called, the matching onload will not be called and
90 | // scriptsLoaded will not fire.
91 | script.onerror = function(error)
92 | {
93 | errorCallback && errorCallback(error);
94 | };
95 |
96 | // Attaching the script tag to the document starts loading the script.
97 | document.head.appendChild(script);
98 | };
99 |
100 | /**
101 | * Load array of scripts.
102 | * @param {array} array - Array of URL or path name stringa.
103 | * Relative paths are relative to the HTML file that initiated
104 | * script loading.
105 | * @param {function} loadedCallback - Optional parameterless
106 | * function called when all scripts in the array has loaded.
107 | * @public
108 | */
109 | evothings.loadScripts = function(array, loadedCallback)
110 | {
111 | var lib = array.shift();
112 | if (!lib)
113 | {
114 | // Array is empty and all scripts are loaded.
115 | loadedCallback && loadedCallback();
116 | }
117 | else
118 | {
119 | // Load next script.
120 | evothings.loadScript(lib, function() {
121 | evothings.loadScripts(array, loadedCallback);
122 | });
123 | }
124 | };
125 |
126 | /**
127 | * Experimental.
128 | * Mark a script as loaded. This is useful if a script is designed
129 | * to be included both in HTML and in JavaScript.
130 | * @param {string} pathOrURL - URL or path to the script. Relative paths are
131 | * relative to the HTML file that initiated script loading.
132 | * @public
133 | */
134 | evothings.markScriptAsLoaded = function(pathOrURL)
135 | {
136 | mLoadedScripts[url] = 'loadingcomplete';
137 | };
138 |
139 | /**
140 | * Add a callback that will be called when all scripts are loaded.
141 | * It is good practise to always use this function when
142 | * loading script asynchronously or using a library that does so.
143 | * @param {function} callback - Parameterless function that will
144 | * be called when all scripts have finished loading.
145 | * @public
146 | */
147 | evothings.scriptsLoaded = function(callback)
148 | {
149 | // If scripts are already loaded call the callback directly,
150 | // else add the callback to the callbacks array.
151 | if (0 != Object.keys(mLoadedScripts).length &&
152 | 0 == mScriptLoadingCounter)
153 | {
154 | callback && callback();
155 | }
156 | else
157 | {
158 | mScriptsLoadedCallbacks.push(callback);
159 | }
160 | };
161 |
162 | /* ------------------ Debugging ------------------ */
163 |
164 | /**
165 | * Print a JavaScript object (dictionary). For debugging.
166 | *
167 | * @param {Object} obj - Object to print.
168 | * @param {function} printFun - print function (optional - defaults to
169 | * console.log if not given).
170 | *
171 | * @example
172 | * var obj = { company: 'Evothings', field: 'IoT' };
173 | * evothings.printObject(obj);
174 | * evothings.printObject(obj, console.log);
175 | *
176 | * @public
177 | */
178 | evothings.printObject = function(obj, printFun)
179 | {
180 | printFun = printFun || console.log;
181 | function print(obj, level)
182 | {
183 | var indent = new Array(level + 1).join(' ');
184 | for (var prop in obj)
185 | {
186 | if (obj.hasOwnProperty(prop))
187 | {
188 | var value = obj[prop];
189 | if (typeof value == 'object')
190 | {
191 | printFun(indent + prop + ':');
192 | print(value, level + 1);
193 | }
194 | else
195 | {
196 | printFun(indent + prop + ': ' + value);
197 | }
198 | }
199 | }
200 | }
201 | print(obj, 0);
202 | };
203 |
204 | /* ------------------ Platform check ------------------ */
205 |
206 | /**
207 | * @namespace
208 | * @description Namespace for platform check functions.
209 | */
210 | evothings.os = {};
211 |
212 | /**
213 | * Returns true if current platform is iOS, false if not.
214 | * @return {boolean} true if platform is iOS, false if not.
215 | * @public
216 | */
217 | evothings.os.isIOS = function()
218 | {
219 | return /iP(hone|ad|od)/.test(navigator.userAgent);
220 | };
221 |
222 | /**
223 | * Returns true if current platform is iOS 7, false if not.
224 | * @return {boolean} true if platform is iOS 7, false if not.
225 | * @public
226 | */
227 | evothings.os.isIOS7 = function()
228 | {
229 | return /iP(hone|ad|od).*OS 7/.test(navigator.userAgent);
230 | };
231 |
232 | /**
233 | * Returns true if current platform is Android, false if not.
234 | * @return {boolean} true if platform is Android, false if not.
235 | * @public
236 | */
237 | evothings.os.isAndroid = function()
238 | {
239 | return /Android|android/.test(navigator.userAgent);
240 | };
241 |
242 | /**
243 | * Returns true if current platform is Windows Phone, false if not.
244 | * @return {boolean} true if platform is Windows Phone, false if not.
245 | * @public
246 | */
247 | evothings.os.isWP = function()
248 | {
249 | return /Windows Phone/.test(navigator.userAgent);
250 | };
251 | })();
252 |
--------------------------------------------------------------------------------
/Adafruit_GFX.h:
--------------------------------------------------------------------------------
1 | #ifndef _ADAFRUIT_GFX_H
2 | #define _ADAFRUIT_GFX_H
3 |
4 | #if ARDUINO >= 100
5 | #include "Arduino.h"
6 | #include "Print.h"
7 | #else
8 | #include "WProgram.h"
9 | #endif
10 | #include "gfxfont.h"
11 |
12 | class Adafruit_GFX : public Print {
13 |
14 | public:
15 |
16 | Adafruit_GFX(int16_t w, int16_t h); // Constructor
17 |
18 | // This MUST be defined by the subclass:
19 | virtual void drawPixel(int16_t x, int16_t y, uint16_t color) = 0;
20 |
21 | // TRANSACTION API / CORE DRAW API
22 | // These MAY be overridden by the subclass to provide device-specific
23 | // optimized code. Otherwise 'generic' versions are used.
24 | virtual void startWrite(void);
25 | virtual void writePixel(int16_t x, int16_t y, uint16_t color);
26 | virtual void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
27 | virtual void writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
28 | virtual void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
29 | virtual void writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
30 | virtual void endWrite(void);
31 |
32 | // CONTROL API
33 | // These MAY be overridden by the subclass to provide device-specific
34 | // optimized code. Otherwise 'generic' versions are used.
35 | virtual void setRotation(uint8_t r);
36 | virtual void invertDisplay(boolean i);
37 |
38 | // BASIC DRAW API
39 | // These MAY be overridden by the subclass to provide device-specific
40 | // optimized code. Otherwise 'generic' versions are used.
41 | virtual void
42 | // It's good to implement those, even if using transaction API
43 | drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color),
44 | drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color),
45 | fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color),
46 | fillScreen(uint16_t color),
47 | // Optional and probably not necessary to change
48 | drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color),
49 | drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
50 |
51 | // These exist only with Adafruit_GFX (no subclass overrides)
52 | void
53 | drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color),
54 | drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername,
55 | uint16_t color),
56 | fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color),
57 | fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername,
58 | int16_t delta, uint16_t color),
59 | drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
60 | int16_t x2, int16_t y2, uint16_t color),
61 | fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
62 | int16_t x2, int16_t y2, uint16_t color),
63 | drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h,
64 | int16_t radius, uint16_t color),
65 | fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h,
66 | int16_t radius, uint16_t color),
67 | drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
68 | int16_t w, int16_t h, uint16_t color),
69 | drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
70 | int16_t w, int16_t h, uint16_t color, uint16_t bg),
71 | drawBitmap(int16_t x, int16_t y, uint8_t *bitmap,
72 | int16_t w, int16_t h, uint16_t color),
73 | drawBitmap(int16_t x, int16_t y, uint8_t *bitmap,
74 | int16_t w, int16_t h, uint16_t color, uint16_t bg),
75 | drawXBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
76 | int16_t w, int16_t h, uint16_t color),
77 | drawGrayscaleBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
78 | int16_t w, int16_t h),
79 | drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap,
80 | int16_t w, int16_t h),
81 | drawGrayscaleBitmap(int16_t x, int16_t y,
82 | const uint8_t bitmap[], const uint8_t mask[],
83 | int16_t w, int16_t h),
84 | drawGrayscaleBitmap(int16_t x, int16_t y,
85 | uint8_t *bitmap, uint8_t *mask, int16_t w, int16_t h),
86 | drawRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[],
87 | int16_t w, int16_t h),
88 | drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap,
89 | int16_t w, int16_t h),
90 | drawRGBBitmap(int16_t x, int16_t y,
91 | const uint16_t bitmap[], const uint8_t mask[],
92 | int16_t w, int16_t h),
93 | drawRGBBitmap(int16_t x, int16_t y,
94 | uint16_t *bitmap, uint8_t *mask, int16_t w, int16_t h),
95 | drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color,
96 | uint16_t bg, uint8_t size),
97 | setCursor(int16_t x, int16_t y),
98 | setTextColor(uint16_t c),
99 | setTextColor(uint16_t c, uint16_t bg),
100 | setTextSize(uint8_t s),
101 | setTextWrap(boolean w),
102 | cp437(boolean x=true),
103 | setFont(const GFXfont *f = NULL),
104 | getTextBounds(char *string, int16_t x, int16_t y,
105 | int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h),
106 | getTextBounds(const __FlashStringHelper *s, int16_t x, int16_t y,
107 | int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h);
108 |
109 | #if ARDUINO >= 100
110 | virtual size_t write(uint8_t);
111 | #else
112 | virtual void write(uint8_t);
113 | #endif
114 |
115 | int16_t height(void) const;
116 | int16_t width(void) const;
117 |
118 | uint8_t getRotation(void) const;
119 |
120 | // get current cursor position (get rotation safe maximum values, using: width() for x, height() for y)
121 | int16_t getCursorX(void) const;
122 | int16_t getCursorY(void) const;
123 |
124 | protected:
125 | void
126 | charBounds(char c, int16_t *x, int16_t *y,
127 | int16_t *minx, int16_t *miny, int16_t *maxx, int16_t *maxy);
128 | const int16_t
129 | WIDTH, HEIGHT; // This is the 'raw' display w/h - never changes
130 | int16_t
131 | _width, _height, // Display w/h as modified by current rotation
132 | cursor_x, cursor_y;
133 | uint16_t
134 | textcolor, textbgcolor;
135 | uint8_t
136 | textsize,
137 | rotation;
138 | boolean
139 | wrap, // If set, 'wrap' text at right edge of display
140 | _cp437; // If set, use correct CP437 charset (default is off)
141 | GFXfont
142 | *gfxFont;
143 | };
144 |
145 | class Adafruit_GFX_Button {
146 |
147 | public:
148 | Adafruit_GFX_Button(void);
149 | // "Classic" initButton() uses center & size
150 | void initButton(Adafruit_GFX *gfx, int16_t x, int16_t y,
151 | uint16_t w, uint16_t h, uint16_t outline, uint16_t fill,
152 | uint16_t textcolor, char *label, uint8_t textsize);
153 | // New/alt initButton() uses upper-left corner & size
154 | void initButtonUL(Adafruit_GFX *gfx, int16_t x1, int16_t y1,
155 | uint16_t w, uint16_t h, uint16_t outline, uint16_t fill,
156 | uint16_t textcolor, char *label, uint8_t textsize);
157 | void drawButton(boolean inverted = false);
158 | boolean contains(int16_t x, int16_t y);
159 |
160 | void press(boolean p);
161 | boolean isPressed();
162 | boolean justPressed();
163 | boolean justReleased();
164 |
165 | private:
166 | Adafruit_GFX *_gfx;
167 | int16_t _x1, _y1; // Coordinates of top-left corner
168 | uint16_t _w, _h;
169 | uint8_t _textsize;
170 | uint16_t _outlinecolor, _fillcolor, _textcolor;
171 | char _label[10];
172 |
173 | boolean currstate, laststate;
174 | };
175 |
176 | class GFXcanvas1 : public Adafruit_GFX {
177 | public:
178 | GFXcanvas1(uint16_t w, uint16_t h);
179 | ~GFXcanvas1(void);
180 | void drawPixel(int16_t x, int16_t y, uint16_t color),
181 | fillScreen(uint16_t color);
182 | uint8_t *getBuffer(void);
183 | private:
184 | uint8_t *buffer;
185 | };
186 |
187 | class GFXcanvas8 : public Adafruit_GFX {
188 | public:
189 | GFXcanvas8(uint16_t w, uint16_t h);
190 | ~GFXcanvas8(void);
191 | void drawPixel(int16_t x, int16_t y, uint16_t color),
192 | fillScreen(uint16_t color),
193 | writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
194 |
195 | uint8_t *getBuffer(void);
196 | private:
197 | uint8_t *buffer;
198 | };
199 |
200 | class GFXcanvas16 : public Adafruit_GFX {
201 | public:
202 | GFXcanvas16(uint16_t w, uint16_t h);
203 | ~GFXcanvas16(void);
204 | void drawPixel(int16_t x, int16_t y, uint16_t color),
205 | fillScreen(uint16_t color);
206 | uint16_t *getBuffer(void);
207 | private:
208 | uint16_t *buffer;
209 | };
210 |
211 | #endif // _ADAFRUIT_GFX_H
212 |
--------------------------------------------------------------------------------
/glcdfont.c:
--------------------------------------------------------------------------------
1 | // This is the 'classic' fixed-space bitmap font for Adafruit_GFX since 1.0.
2 | // See gfxfont.h for newer custom bitmap font info.
3 |
4 | #ifndef FONT5X7_H
5 | #define FONT5X7_H
6 |
7 | #ifdef __AVR__
8 | #include
9 | #include
10 | #elif defined(ESP8266)
11 | #include
12 | #else
13 | #define PROGMEM
14 | #endif
15 |
16 | // Standard ASCII 5x7 font
17 |
18 | static const unsigned char font[] PROGMEM = {
19 | 0x00, 0x00, 0x00, 0x00, 0x00,
20 | 0x3E, 0x5B, 0x4F, 0x5B, 0x3E,
21 | 0x3E, 0x6B, 0x4F, 0x6B, 0x3E,
22 | 0x1C, 0x3E, 0x7C, 0x3E, 0x1C,
23 | 0x18, 0x3C, 0x7E, 0x3C, 0x18,
24 | 0x1C, 0x57, 0x7D, 0x57, 0x1C,
25 | 0x1C, 0x5E, 0x7F, 0x5E, 0x1C,
26 | 0x00, 0x18, 0x3C, 0x18, 0x00,
27 | 0xFF, 0xE7, 0xC3, 0xE7, 0xFF,
28 | 0x00, 0x18, 0x24, 0x18, 0x00,
29 | 0xFF, 0xE7, 0xDB, 0xE7, 0xFF,
30 | 0x30, 0x48, 0x3A, 0x06, 0x0E,
31 | 0x26, 0x29, 0x79, 0x29, 0x26,
32 | 0x40, 0x7F, 0x05, 0x05, 0x07,
33 | 0x40, 0x7F, 0x05, 0x25, 0x3F,
34 | 0x5A, 0x3C, 0xE7, 0x3C, 0x5A,
35 | 0x7F, 0x3E, 0x1C, 0x1C, 0x08,
36 | 0x08, 0x1C, 0x1C, 0x3E, 0x7F,
37 | 0x14, 0x22, 0x7F, 0x22, 0x14,
38 | 0x5F, 0x5F, 0x00, 0x5F, 0x5F,
39 | 0x06, 0x09, 0x7F, 0x01, 0x7F,
40 | 0x00, 0x66, 0x89, 0x95, 0x6A,
41 | 0x60, 0x60, 0x60, 0x60, 0x60,
42 | 0x94, 0xA2, 0xFF, 0xA2, 0x94,
43 | 0x08, 0x04, 0x7E, 0x04, 0x08,
44 | 0x10, 0x20, 0x7E, 0x20, 0x10,
45 | 0x08, 0x08, 0x2A, 0x1C, 0x08,
46 | 0x08, 0x1C, 0x2A, 0x08, 0x08,
47 | 0x1E, 0x10, 0x10, 0x10, 0x10,
48 | 0x0C, 0x1E, 0x0C, 0x1E, 0x0C,
49 | 0x30, 0x38, 0x3E, 0x38, 0x30,
50 | 0x06, 0x0E, 0x3E, 0x0E, 0x06,
51 | 0x00, 0x00, 0x00, 0x00, 0x00,
52 | 0x00, 0x00, 0x5F, 0x00, 0x00,
53 | 0x00, 0x07, 0x00, 0x07, 0x00,
54 | 0x14, 0x7F, 0x14, 0x7F, 0x14,
55 | 0x24, 0x2A, 0x7F, 0x2A, 0x12,
56 | 0x23, 0x13, 0x08, 0x64, 0x62,
57 | 0x36, 0x49, 0x56, 0x20, 0x50,
58 | 0x00, 0x08, 0x07, 0x03, 0x00,
59 | 0x00, 0x1C, 0x22, 0x41, 0x00,
60 | 0x00, 0x41, 0x22, 0x1C, 0x00,
61 | 0x2A, 0x1C, 0x7F, 0x1C, 0x2A,
62 | 0x08, 0x08, 0x3E, 0x08, 0x08,
63 | 0x00, 0x80, 0x70, 0x30, 0x00,
64 | 0x08, 0x08, 0x08, 0x08, 0x08,
65 | 0x00, 0x00, 0x60, 0x60, 0x00,
66 | 0x20, 0x10, 0x08, 0x04, 0x02,
67 | 0x3E, 0x51, 0x49, 0x45, 0x3E,
68 | 0x00, 0x42, 0x7F, 0x40, 0x00,
69 | 0x72, 0x49, 0x49, 0x49, 0x46,
70 | 0x21, 0x41, 0x49, 0x4D, 0x33,
71 | 0x18, 0x14, 0x12, 0x7F, 0x10,
72 | 0x27, 0x45, 0x45, 0x45, 0x39,
73 | 0x3C, 0x4A, 0x49, 0x49, 0x31,
74 | 0x41, 0x21, 0x11, 0x09, 0x07,
75 | 0x36, 0x49, 0x49, 0x49, 0x36,
76 | 0x46, 0x49, 0x49, 0x29, 0x1E,
77 | 0x00, 0x00, 0x14, 0x00, 0x00,
78 | 0x00, 0x40, 0x34, 0x00, 0x00,
79 | 0x00, 0x08, 0x14, 0x22, 0x41,
80 | 0x14, 0x14, 0x14, 0x14, 0x14,
81 | 0x00, 0x41, 0x22, 0x14, 0x08,
82 | 0x02, 0x01, 0x59, 0x09, 0x06,
83 | 0x3E, 0x41, 0x5D, 0x59, 0x4E,
84 | 0x7C, 0x12, 0x11, 0x12, 0x7C,
85 | 0x7F, 0x49, 0x49, 0x49, 0x36,
86 | 0x3E, 0x41, 0x41, 0x41, 0x22,
87 | 0x7F, 0x41, 0x41, 0x41, 0x3E,
88 | 0x7F, 0x49, 0x49, 0x49, 0x41,
89 | 0x7F, 0x09, 0x09, 0x09, 0x01,
90 | 0x3E, 0x41, 0x41, 0x51, 0x73,
91 | 0x7F, 0x08, 0x08, 0x08, 0x7F,
92 | 0x00, 0x41, 0x7F, 0x41, 0x00,
93 | 0x20, 0x40, 0x41, 0x3F, 0x01,
94 | 0x7F, 0x08, 0x14, 0x22, 0x41,
95 | 0x7F, 0x40, 0x40, 0x40, 0x40,
96 | 0x7F, 0x02, 0x1C, 0x02, 0x7F,
97 | 0x7F, 0x04, 0x08, 0x10, 0x7F,
98 | 0x3E, 0x41, 0x41, 0x41, 0x3E,
99 | 0x7F, 0x09, 0x09, 0x09, 0x06,
100 | 0x3E, 0x41, 0x51, 0x21, 0x5E,
101 | 0x7F, 0x09, 0x19, 0x29, 0x46,
102 | 0x26, 0x49, 0x49, 0x49, 0x32,
103 | 0x03, 0x01, 0x7F, 0x01, 0x03,
104 | 0x3F, 0x40, 0x40, 0x40, 0x3F,
105 | 0x1F, 0x20, 0x40, 0x20, 0x1F,
106 | 0x3F, 0x40, 0x38, 0x40, 0x3F,
107 | 0x63, 0x14, 0x08, 0x14, 0x63,
108 | 0x03, 0x04, 0x78, 0x04, 0x03,
109 | 0x61, 0x59, 0x49, 0x4D, 0x43,
110 | 0x00, 0x7F, 0x41, 0x41, 0x41,
111 | 0x02, 0x04, 0x08, 0x10, 0x20,
112 | 0x00, 0x41, 0x41, 0x41, 0x7F,
113 | 0x04, 0x02, 0x01, 0x02, 0x04,
114 | 0x40, 0x40, 0x40, 0x40, 0x40,
115 | 0x00, 0x03, 0x07, 0x08, 0x00,
116 | 0x20, 0x54, 0x54, 0x78, 0x40,
117 | 0x7F, 0x28, 0x44, 0x44, 0x38,
118 | 0x38, 0x44, 0x44, 0x44, 0x28,
119 | 0x38, 0x44, 0x44, 0x28, 0x7F,
120 | 0x38, 0x54, 0x54, 0x54, 0x18,
121 | 0x00, 0x08, 0x7E, 0x09, 0x02,
122 | 0x18, 0xA4, 0xA4, 0x9C, 0x78,
123 | 0x7F, 0x08, 0x04, 0x04, 0x78,
124 | 0x00, 0x44, 0x7D, 0x40, 0x00,
125 | 0x20, 0x40, 0x40, 0x3D, 0x00,
126 | 0x7F, 0x10, 0x28, 0x44, 0x00,
127 | 0x00, 0x41, 0x7F, 0x40, 0x00,
128 | 0x7C, 0x04, 0x78, 0x04, 0x78,
129 | 0x7C, 0x08, 0x04, 0x04, 0x78,
130 | 0x38, 0x44, 0x44, 0x44, 0x38,
131 | 0xFC, 0x18, 0x24, 0x24, 0x18,
132 | 0x18, 0x24, 0x24, 0x18, 0xFC,
133 | 0x7C, 0x08, 0x04, 0x04, 0x08,
134 | 0x48, 0x54, 0x54, 0x54, 0x24,
135 | 0x04, 0x04, 0x3F, 0x44, 0x24,
136 | 0x3C, 0x40, 0x40, 0x20, 0x7C,
137 | 0x1C, 0x20, 0x40, 0x20, 0x1C,
138 | 0x3C, 0x40, 0x30, 0x40, 0x3C,
139 | 0x44, 0x28, 0x10, 0x28, 0x44,
140 | 0x4C, 0x90, 0x90, 0x90, 0x7C,
141 | 0x44, 0x64, 0x54, 0x4C, 0x44,
142 | 0x00, 0x08, 0x36, 0x41, 0x00,
143 | 0x00, 0x00, 0x77, 0x00, 0x00,
144 | 0x00, 0x41, 0x36, 0x08, 0x00,
145 | 0x02, 0x01, 0x02, 0x04, 0x02,
146 | 0x3C, 0x26, 0x23, 0x26, 0x3C,
147 | 0x1E, 0xA1, 0xA1, 0x61, 0x12,
148 | 0x3A, 0x40, 0x40, 0x20, 0x7A,
149 | 0x38, 0x54, 0x54, 0x55, 0x59,
150 | 0x21, 0x55, 0x55, 0x79, 0x41,
151 | 0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut
152 | 0x21, 0x55, 0x54, 0x78, 0x40,
153 | 0x20, 0x54, 0x55, 0x79, 0x40,
154 | 0x0C, 0x1E, 0x52, 0x72, 0x12,
155 | 0x39, 0x55, 0x55, 0x55, 0x59,
156 | 0x39, 0x54, 0x54, 0x54, 0x59,
157 | 0x39, 0x55, 0x54, 0x54, 0x58,
158 | 0x00, 0x00, 0x45, 0x7C, 0x41,
159 | 0x00, 0x02, 0x45, 0x7D, 0x42,
160 | 0x00, 0x01, 0x45, 0x7C, 0x40,
161 | 0x7D, 0x12, 0x11, 0x12, 0x7D, // A-umlaut
162 | 0xF0, 0x28, 0x25, 0x28, 0xF0,
163 | 0x7C, 0x54, 0x55, 0x45, 0x00,
164 | 0x20, 0x54, 0x54, 0x7C, 0x54,
165 | 0x7C, 0x0A, 0x09, 0x7F, 0x49,
166 | 0x32, 0x49, 0x49, 0x49, 0x32,
167 | 0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut
168 | 0x32, 0x4A, 0x48, 0x48, 0x30,
169 | 0x3A, 0x41, 0x41, 0x21, 0x7A,
170 | 0x3A, 0x42, 0x40, 0x20, 0x78,
171 | 0x00, 0x9D, 0xA0, 0xA0, 0x7D,
172 | 0x3D, 0x42, 0x42, 0x42, 0x3D, // O-umlaut
173 | 0x3D, 0x40, 0x40, 0x40, 0x3D,
174 | 0x3C, 0x24, 0xFF, 0x24, 0x24,
175 | 0x48, 0x7E, 0x49, 0x43, 0x66,
176 | 0x2B, 0x2F, 0xFC, 0x2F, 0x2B,
177 | 0xFF, 0x09, 0x29, 0xF6, 0x20,
178 | 0xC0, 0x88, 0x7E, 0x09, 0x03,
179 | 0x20, 0x54, 0x54, 0x79, 0x41,
180 | 0x00, 0x00, 0x44, 0x7D, 0x41,
181 | 0x30, 0x48, 0x48, 0x4A, 0x32,
182 | 0x38, 0x40, 0x40, 0x22, 0x7A,
183 | 0x00, 0x7A, 0x0A, 0x0A, 0x72,
184 | 0x7D, 0x0D, 0x19, 0x31, 0x7D,
185 | 0x26, 0x29, 0x29, 0x2F, 0x28,
186 | 0x26, 0x29, 0x29, 0x29, 0x26,
187 | 0x30, 0x48, 0x4D, 0x40, 0x20,
188 | 0x38, 0x08, 0x08, 0x08, 0x08,
189 | 0x08, 0x08, 0x08, 0x08, 0x38,
190 | 0x2F, 0x10, 0xC8, 0xAC, 0xBA,
191 | 0x2F, 0x10, 0x28, 0x34, 0xFA,
192 | 0x00, 0x00, 0x7B, 0x00, 0x00,
193 | 0x08, 0x14, 0x2A, 0x14, 0x22,
194 | 0x22, 0x14, 0x2A, 0x14, 0x08,
195 | 0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old code
196 | 0xAA, 0x55, 0xAA, 0x55, 0xAA, // 50% block
197 | 0xFF, 0x55, 0xFF, 0x55, 0xFF, // 75% block
198 | 0x00, 0x00, 0x00, 0xFF, 0x00,
199 | 0x10, 0x10, 0x10, 0xFF, 0x00,
200 | 0x14, 0x14, 0x14, 0xFF, 0x00,
201 | 0x10, 0x10, 0xFF, 0x00, 0xFF,
202 | 0x10, 0x10, 0xF0, 0x10, 0xF0,
203 | 0x14, 0x14, 0x14, 0xFC, 0x00,
204 | 0x14, 0x14, 0xF7, 0x00, 0xFF,
205 | 0x00, 0x00, 0xFF, 0x00, 0xFF,
206 | 0x14, 0x14, 0xF4, 0x04, 0xFC,
207 | 0x14, 0x14, 0x17, 0x10, 0x1F,
208 | 0x10, 0x10, 0x1F, 0x10, 0x1F,
209 | 0x14, 0x14, 0x14, 0x1F, 0x00,
210 | 0x10, 0x10, 0x10, 0xF0, 0x00,
211 | 0x00, 0x00, 0x00, 0x1F, 0x10,
212 | 0x10, 0x10, 0x10, 0x1F, 0x10,
213 | 0x10, 0x10, 0x10, 0xF0, 0x10,
214 | 0x00, 0x00, 0x00, 0xFF, 0x10,
215 | 0x10, 0x10, 0x10, 0x10, 0x10,
216 | 0x10, 0x10, 0x10, 0xFF, 0x10,
217 | 0x00, 0x00, 0x00, 0xFF, 0x14,
218 | 0x00, 0x00, 0xFF, 0x00, 0xFF,
219 | 0x00, 0x00, 0x1F, 0x10, 0x17,
220 | 0x00, 0x00, 0xFC, 0x04, 0xF4,
221 | 0x14, 0x14, 0x17, 0x10, 0x17,
222 | 0x14, 0x14, 0xF4, 0x04, 0xF4,
223 | 0x00, 0x00, 0xFF, 0x00, 0xF7,
224 | 0x14, 0x14, 0x14, 0x14, 0x14,
225 | 0x14, 0x14, 0xF7, 0x00, 0xF7,
226 | 0x14, 0x14, 0x14, 0x17, 0x14,
227 | 0x10, 0x10, 0x1F, 0x10, 0x1F,
228 | 0x14, 0x14, 0x14, 0xF4, 0x14,
229 | 0x10, 0x10, 0xF0, 0x10, 0xF0,
230 | 0x00, 0x00, 0x1F, 0x10, 0x1F,
231 | 0x00, 0x00, 0x00, 0x1F, 0x14,
232 | 0x00, 0x00, 0x00, 0xFC, 0x14,
233 | 0x00, 0x00, 0xF0, 0x10, 0xF0,
234 | 0x10, 0x10, 0xFF, 0x10, 0xFF,
235 | 0x14, 0x14, 0x14, 0xFF, 0x14,
236 | 0x10, 0x10, 0x10, 0x1F, 0x00,
237 | 0x00, 0x00, 0x00, 0xF0, 0x10,
238 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
239 | 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
240 | 0xFF, 0xFF, 0xFF, 0x00, 0x00,
241 | 0x00, 0x00, 0x00, 0xFF, 0xFF,
242 | 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
243 | 0x38, 0x44, 0x44, 0x38, 0x44,
244 | 0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta
245 | 0x7E, 0x02, 0x02, 0x06, 0x06,
246 | 0x02, 0x7E, 0x02, 0x7E, 0x02,
247 | 0x63, 0x55, 0x49, 0x41, 0x63,
248 | 0x38, 0x44, 0x44, 0x3C, 0x04,
249 | 0x40, 0x7E, 0x20, 0x1E, 0x20,
250 | 0x06, 0x02, 0x7E, 0x02, 0x02,
251 | 0x99, 0xA5, 0xE7, 0xA5, 0x99,
252 | 0x1C, 0x2A, 0x49, 0x2A, 0x1C,
253 | 0x4C, 0x72, 0x01, 0x72, 0x4C,
254 | 0x30, 0x4A, 0x4D, 0x4D, 0x30,
255 | 0x30, 0x48, 0x78, 0x48, 0x30,
256 | 0xBC, 0x62, 0x5A, 0x46, 0x3D,
257 | 0x3E, 0x49, 0x49, 0x49, 0x00,
258 | 0x7E, 0x01, 0x01, 0x01, 0x7E,
259 | 0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
260 | 0x44, 0x44, 0x5F, 0x44, 0x44,
261 | 0x40, 0x51, 0x4A, 0x44, 0x40,
262 | 0x40, 0x44, 0x4A, 0x51, 0x40,
263 | 0x00, 0x00, 0xFF, 0x01, 0x03,
264 | 0xE0, 0x80, 0xFF, 0x00, 0x00,
265 | 0x08, 0x08, 0x6B, 0x6B, 0x08,
266 | 0x36, 0x12, 0x36, 0x24, 0x36,
267 | 0x06, 0x0F, 0x09, 0x0F, 0x06,
268 | 0x00, 0x00, 0x18, 0x18, 0x00,
269 | 0x00, 0x00, 0x10, 0x10, 0x00,
270 | 0x30, 0x40, 0xFF, 0x01, 0x01,
271 | 0x00, 0x1F, 0x01, 0x01, 0x1E,
272 | 0x00, 0x19, 0x1D, 0x17, 0x12,
273 | 0x00, 0x3C, 0x3C, 0x3C, 0x3C,
274 | 0x00, 0x00, 0x00, 0x00, 0x00 // #255 NBSP
275 | };
276 | #endif // FONT5X7_H
277 |
--------------------------------------------------------------------------------
/cordova/www/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
11 | Blume
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
Blume
286 |
287 |
290 |
291 |
292 |
293 |
294 |
295 |
320 |
321 |
326 |
327 |
328 |
329 | Brightness
330 |
331 |
332 |
336 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
355 |
356 |
357 |
358 | Brightness
359 |
360 |
361 |
365 | Size
366 |
369 |
372 |
375 |
376 |
377 |
378 |
391 |
392 |
398 |
399 |
400 |
401 | Brightness
402 |
403 |
404 |
405 | Display Time
406 |
407 |
sec
408 |
409 |
410 |
411 | Use Added Settings
412 |
413 |
414 |
415 | Randomize All Modes
416 |
417 |
Next!
418 |
Clear All Added
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
--------------------------------------------------------------------------------
/cordova/www/libs/spectrum/spectrum.css:
--------------------------------------------------------------------------------
1 | /***
2 | Spectrum Colorpicker v1.8.0
3 | https://github.com/bgrins/spectrum
4 | Author: Brian Grinstead
5 | License: MIT
6 | ***/
7 |
8 | .sp-container {
9 | position:absolute;
10 | top:0;
11 | left:0;
12 | display:inline-block;
13 | *display: inline;
14 | *zoom: 1;
15 | /* https://github.com/bgrins/spectrum/issues/40 */
16 | z-index: 9999994;
17 | overflow: hidden;
18 | }
19 | .sp-container.sp-flat {
20 | position: relative;
21 | }
22 |
23 | /* Fix for * { box-sizing: border-box; } */
24 | .sp-container,
25 | .sp-container * {
26 | -webkit-box-sizing: content-box;
27 | -moz-box-sizing: content-box;
28 | box-sizing: content-box;
29 | }
30 |
31 | /* http://ansciath.tumblr.com/post/7347495869/css-aspect-ratio */
32 | .sp-top {
33 | position:relative;
34 | width: 100%;
35 | display:inline-block;
36 | }
37 | .sp-top-inner {
38 | position:absolute;
39 | top:0;
40 | left:0;
41 | bottom:0;
42 | right:0;
43 | }
44 | .sp-color {
45 | position: absolute;
46 | top:0;
47 | left:0;
48 | bottom:0;
49 | right:20%;
50 | }
51 | .sp-hue {
52 | position: absolute;
53 | top:0;
54 | right:0;
55 | bottom:0;
56 | left:84%;
57 | height: 100%;
58 | }
59 |
60 | .sp-clear-enabled .sp-hue {
61 | top:33px;
62 | height: 77.5%;
63 | }
64 |
65 | .sp-fill {
66 | padding-top: 80%;
67 | }
68 | .sp-sat, .sp-val {
69 | position: absolute;
70 | top:0;
71 | left:0;
72 | right:0;
73 | bottom:0;
74 | }
75 |
76 | .sp-alpha-enabled .sp-top {
77 | margin-bottom: 18px;
78 | }
79 | .sp-alpha-enabled .sp-alpha {
80 | display: block;
81 | }
82 | .sp-alpha-handle {
83 | position:absolute;
84 | top:-4px;
85 | bottom: -4px;
86 | width: 6px;
87 | left: 50%;
88 | cursor: pointer;
89 | border: 1px solid black;
90 | background: white;
91 | opacity: .8;
92 | }
93 | .sp-alpha {
94 | display: none;
95 | position: absolute;
96 | bottom: -14px;
97 | right: 0;
98 | left: 0;
99 | height: 8px;
100 | }
101 | .sp-alpha-inner {
102 | border: solid 1px #333;
103 | }
104 |
105 | .sp-clear {
106 | display: none;
107 | }
108 |
109 | .sp-clear.sp-clear-display {
110 | background-position: center;
111 | }
112 |
113 | .sp-clear-enabled .sp-clear {
114 | display: block;
115 | position:absolute;
116 | top:0px;
117 | right:0;
118 | bottom:0;
119 | left:84%;
120 | height: 28px;
121 | }
122 |
123 | /* Don't allow text selection */
124 | .sp-container, .sp-replacer, .sp-preview, .sp-dragger, .sp-slider, .sp-alpha, .sp-clear, .sp-alpha-handle, .sp-container.sp-dragging .sp-input, .sp-container button {
125 | -webkit-user-select:none;
126 | -moz-user-select: -moz-none;
127 | -o-user-select:none;
128 | user-select: none;
129 | }
130 |
131 | .sp-container.sp-input-disabled .sp-input-container {
132 | display: none;
133 | }
134 | .sp-container.sp-buttons-disabled .sp-button-container {
135 | display: none;
136 | }
137 | .sp-container.sp-palette-buttons-disabled .sp-palette-button-container {
138 | display: none;
139 | }
140 | .sp-palette-only .sp-picker-container {
141 | display: none;
142 | }
143 | .sp-palette-disabled .sp-palette-container {
144 | display: none;
145 | }
146 |
147 | .sp-initial-disabled .sp-initial {
148 | display: none;
149 | }
150 |
151 |
152 | /* Gradients for hue, saturation and value instead of images. Not pretty... but it works */
153 | .sp-sat {
154 | background-image: -webkit-gradient(linear, 0 0, 100% 0, from(#FFF), to(rgba(204, 154, 129, 0)));
155 | background-image: -webkit-linear-gradient(left, #FFF, rgba(204, 154, 129, 0));
156 | background-image: -moz-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
157 | background-image: -o-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
158 | background-image: -ms-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
159 | background-image: linear-gradient(to right, #fff, rgba(204, 154, 129, 0));
160 | -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr=#FFFFFFFF, endColorstr=#00CC9A81)";
161 | filter : progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr='#FFFFFFFF', endColorstr='#00CC9A81');
162 | }
163 | .sp-val {
164 | background-image: -webkit-gradient(linear, 0 100%, 0 0, from(#000000), to(rgba(204, 154, 129, 0)));
165 | background-image: -webkit-linear-gradient(bottom, #000000, rgba(204, 154, 129, 0));
166 | background-image: -moz-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
167 | background-image: -o-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
168 | background-image: -ms-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
169 | background-image: linear-gradient(to top, #000, rgba(204, 154, 129, 0));
170 | -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#00CC9A81, endColorstr=#FF000000)";
171 | filter : progid:DXImageTransform.Microsoft.gradient(startColorstr='#00CC9A81', endColorstr='#FF000000');
172 | }
173 |
174 | .sp-hue {
175 | background: -moz-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
176 | background: -ms-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
177 | background: -o-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
178 | background: -webkit-gradient(linear, left top, left bottom, from(#ff0000), color-stop(0.17, #ffff00), color-stop(0.33, #00ff00), color-stop(0.5, #00ffff), color-stop(0.67, #0000ff), color-stop(0.83, #ff00ff), to(#ff0000));
179 | background: -webkit-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
180 | background: linear-gradient(to bottom, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
181 | }
182 |
183 | /* IE filters do not support multiple color stops.
184 | Generate 6 divs, line them up, and do two color gradients for each.
185 | Yes, really.
186 | */
187 | .sp-1 {
188 | height:17%;
189 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0000', endColorstr='#ffff00');
190 | }
191 | .sp-2 {
192 | height:16%;
193 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff00', endColorstr='#00ff00');
194 | }
195 | .sp-3 {
196 | height:17%;
197 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ff00', endColorstr='#00ffff');
198 | }
199 | .sp-4 {
200 | height:17%;
201 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffff', endColorstr='#0000ff');
202 | }
203 | .sp-5 {
204 | height:16%;
205 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0000ff', endColorstr='#ff00ff');
206 | }
207 | .sp-6 {
208 | height:17%;
209 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff00ff', endColorstr='#ff0000');
210 | }
211 |
212 | .sp-hidden {
213 | display: none !important;
214 | }
215 |
216 | /* Clearfix hack */
217 | .sp-cf:before, .sp-cf:after { content: ""; display: table; }
218 | .sp-cf:after { clear: both; }
219 | .sp-cf { *zoom: 1; }
220 |
221 | .sp-dragger {
222 | border-radius: 5px;
223 | height: 5px;
224 | width: 5px;
225 | border: 1px solid #fff;
226 | background: #000;
227 | cursor: pointer;
228 | position:absolute;
229 | top:0;
230 | left: 0;
231 | }
232 | .sp-slider {
233 | position: absolute;
234 | top:0;
235 | cursor:pointer;
236 | height: 3px;
237 | left: -1px;
238 | right: -1px;
239 | border: 1px solid #000;
240 | background: white;
241 | opacity: .8;
242 | }
243 |
244 | /*
245 | Theme authors:
246 | Here are the basic themeable display options (colors, fonts, global widths).
247 | See http://bgrins.github.io/spectrum/themes/ for instructions.
248 | */
249 |
250 | .sp-container {
251 | border-radius: 0;
252 | background-color: #ECECEC;
253 | border: solid 1px #f0c49B;
254 | padding: 0;
255 | }
256 | .sp-container, .sp-container button, .sp-container input, .sp-color, .sp-hue, .sp-clear {
257 | font: normal 12px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;
258 | -webkit-box-sizing: border-box;
259 | -moz-box-sizing: border-box;
260 | -ms-box-sizing: border-box;
261 | box-sizing: border-box;
262 | }
263 | .sp-top {
264 | margin-bottom: 3px;
265 | }
266 | .sp-color, .sp-hue, .sp-clear {
267 | border: solid 1px #666;
268 | }
269 |
270 | /* Input */
271 | .sp-input-container {
272 | float:right;
273 | width: 100px;
274 | margin-bottom: 4px;
275 | }
276 | .sp-initial-disabled .sp-input-container {
277 | width: 100%;
278 | }
279 | .sp-input {
280 | font-size: 12px !important;
281 | border: 1px inset;
282 | padding: 4px 5px;
283 | margin: 0;
284 | width: 100%;
285 | background:transparent;
286 | border-radius: 3px;
287 | color: #222;
288 | }
289 | .sp-input:focus {
290 | border: 1px solid orange;
291 | }
292 | .sp-input.sp-validation-error {
293 | border: 1px solid red;
294 | background: #fdd;
295 | }
296 | .sp-picker-container , .sp-palette-container {
297 | float:left;
298 | position: relative;
299 | padding: 10px;
300 | padding-bottom: 300px;
301 | margin-bottom: -290px;
302 | }
303 | .sp-picker-container {
304 | width: 172px;
305 | border-left: solid 1px #fff;
306 | }
307 |
308 | /* Palettes */
309 | .sp-palette-container {
310 | border-right: solid 1px #ccc;
311 | }
312 |
313 | .sp-palette-only .sp-palette-container {
314 | border: 0;
315 | }
316 |
317 | .sp-palette .sp-thumb-el {
318 | display: block;
319 | position:relative;
320 | float:left;
321 | width: 24px;
322 | height: 15px;
323 | margin: 3px;
324 | cursor: pointer;
325 | border:solid 2px transparent;
326 | }
327 | .sp-palette .sp-thumb-el:hover, .sp-palette .sp-thumb-el.sp-thumb-active {
328 | border-color: orange;
329 | }
330 | .sp-thumb-el {
331 | position:relative;
332 | }
333 |
334 | /* Initial */
335 | .sp-initial {
336 | float: left;
337 | border: solid 1px #333;
338 | }
339 | .sp-initial span {
340 | width: 30px;
341 | height: 25px;
342 | border:none;
343 | display:block;
344 | float:left;
345 | margin:0;
346 | }
347 |
348 | .sp-initial .sp-clear-display {
349 | background-position: center;
350 | }
351 |
352 | /* Buttons */
353 | .sp-palette-button-container,
354 | .sp-button-container {
355 | float: right;
356 | }
357 |
358 | /* Replacer (the little preview div that shows up instead of the ) */
359 | .sp-replacer {
360 | margin:0;
361 | overflow:hidden;
362 | cursor:pointer;
363 | padding: 4px;
364 | display:inline-block;
365 | *zoom: 1;
366 | *display: inline;
367 | border: solid 1px #91765d;
368 | background: #eee;
369 | color: #333;
370 | vertical-align: middle;
371 | }
372 | .sp-replacer:hover, .sp-replacer.sp-active {
373 | border-color: #F0C49B;
374 | color: #111;
375 | }
376 | .sp-replacer.sp-disabled {
377 | cursor:default;
378 | border-color: silver;
379 | color: silver;
380 | }
381 | .sp-dd {
382 | padding: 2px 0;
383 | height: 16px;
384 | line-height: 16px;
385 | float:left;
386 | font-size:10px;
387 | }
388 | .sp-preview {
389 | position:relative;
390 | width:25px;
391 | height: 20px;
392 | border: solid 1px #222;
393 | margin-right: 5px;
394 | float:left;
395 | z-index: 0;
396 | }
397 |
398 | .sp-palette {
399 | *width: 220px;
400 | max-width: 220px;
401 | }
402 | .sp-palette .sp-thumb-el {
403 | width:16px;
404 | height: 16px;
405 | margin:2px 1px;
406 | border: solid 1px #d0d0d0;
407 | }
408 |
409 | .sp-container {
410 | padding-bottom:0;
411 | }
412 |
413 |
414 | /* Buttons: http://hellohappy.org/css3-buttons/ */
415 | .sp-container button {
416 | background-color: #eeeeee;
417 | background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc);
418 | background-image: -moz-linear-gradient(top, #eeeeee, #cccccc);
419 | background-image: -ms-linear-gradient(top, #eeeeee, #cccccc);
420 | background-image: -o-linear-gradient(top, #eeeeee, #cccccc);
421 | background-image: linear-gradient(to bottom, #eeeeee, #cccccc);
422 | border: 1px solid #ccc;
423 | border-bottom: 1px solid #bbb;
424 | border-radius: 3px;
425 | color: #333;
426 | font-size: 14px;
427 | line-height: 1;
428 | padding: 5px 4px;
429 | text-align: center;
430 | text-shadow: 0 1px 0 #eee;
431 | vertical-align: middle;
432 | }
433 | .sp-container button:hover {
434 | background-color: #dddddd;
435 | background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb);
436 | background-image: -moz-linear-gradient(top, #dddddd, #bbbbbb);
437 | background-image: -ms-linear-gradient(top, #dddddd, #bbbbbb);
438 | background-image: -o-linear-gradient(top, #dddddd, #bbbbbb);
439 | background-image: linear-gradient(to bottom, #dddddd, #bbbbbb);
440 | border: 1px solid #bbb;
441 | border-bottom: 1px solid #999;
442 | cursor: pointer;
443 | text-shadow: 0 1px 0 #ddd;
444 | }
445 | .sp-container button:active {
446 | border: 1px solid #aaa;
447 | border-bottom: 1px solid #888;
448 | -webkit-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
449 | -moz-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
450 | -ms-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
451 | -o-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
452 | box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
453 | }
454 | .sp-cancel {
455 | font-size: 11px;
456 | color: #d93f3f !important;
457 | margin:0;
458 | padding:2px;
459 | margin-right: 5px;
460 | vertical-align: middle;
461 | text-decoration:none;
462 |
463 | }
464 | .sp-cancel:hover {
465 | color: #d93f3f !important;
466 | text-decoration: underline;
467 | }
468 |
469 |
470 | .sp-palette span:hover, .sp-palette span.sp-thumb-active {
471 | border-color: #000;
472 | }
473 |
474 | .sp-preview, .sp-alpha, .sp-thumb-el {
475 | position:relative;
476 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
477 | }
478 | .sp-preview-inner, .sp-alpha-inner, .sp-thumb-inner {
479 | display:block;
480 | position:absolute;
481 | top:0;left:0;bottom:0;right:0;
482 | }
483 |
484 | .sp-palette .sp-thumb-inner {
485 | background-position: 50% 50%;
486 | background-repeat: no-repeat;
487 | }
488 |
489 | .sp-palette .sp-thumb-light.sp-thumb-active .sp-thumb-inner {
490 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIVJREFUeNpiYBhsgJFMffxAXABlN5JruT4Q3wfi/0DsT64h8UD8HmpIPCWG/KemIfOJCUB+Aoacx6EGBZyHBqI+WsDCwuQ9mhxeg2A210Ntfo8klk9sOMijaURm7yc1UP2RNCMbKE9ODK1HM6iegYLkfx8pligC9lCD7KmRof0ZhjQACDAAceovrtpVBRkAAAAASUVORK5CYII=);
491 | }
492 |
493 | .sp-palette .sp-thumb-dark.sp-thumb-active .sp-thumb-inner {
494 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAMdJREFUOE+tkgsNwzAMRMugEAahEAahEAZhEAqlEAZhEAohEAYh81X2dIm8fKpEspLGvudPOsUYpxE2BIJCroJmEW9qJ+MKaBFhEMNabSy9oIcIPwrB+afvAUFoK4H0tMaQ3XtlrggDhOVVMuT4E5MMG0FBbCEYzjYT7OxLEvIHQLY2zWwQ3D+9luyOQTfKDiFD3iUIfPk8VqrKjgAiSfGFPecrg6HN6m/iBcwiDAo7WiBeawa+Kwh7tZoSCGLMqwlSAzVDhoK+6vH4G0P5wdkAAAAASUVORK5CYII=);
495 | }
496 |
497 | .sp-clear-display {
498 | background-repeat:no-repeat;
499 | background-position: center;
500 | background-image: url(data:image/gif;base64,R0lGODlhFAAUAPcAAAAAAJmZmZ2dnZ6enqKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq/Hx8fLy8vT09PX19ff39/j4+Pn5+fr6+vv7+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAUABQAAAihAP9FoPCvoMGDBy08+EdhQAIJCCMybCDAAYUEARBAlFiQQoMABQhKUJBxY0SPICEYHBnggEmDKAuoPMjS5cGYMxHW3IiT478JJA8M/CjTZ0GgLRekNGpwAsYABHIypcAgQMsITDtWJYBR6NSqMico9cqR6tKfY7GeBCuVwlipDNmefAtTrkSzB1RaIAoXodsABiZAEFB06gIBWC1mLVgBa0AAOw==);
501 | }
502 |
503 | /* Mobile devices, make hue slider bigger so it is easier to slide */
504 | @media (max-device-width: 480px) {
505 | .sp-color { right: 40%; }
506 | .sp-hue { left: 63%; width: 30%; }
507 | .sp-fill { padding-top: 60%; }
508 | .sp-container { width: 100%; }
509 | .sp-picker-container { width: 100%; }
510 | }
511 |
--------------------------------------------------------------------------------
/cordova/www/ui/css/evothings-app.css:
--------------------------------------------------------------------------------
1 | /* Common styles for Evothings examples and client. */
2 |
3 | @import '../fonts/ProximaNova.css';
4 |
5 | html, body {
6 | height: 100%;
7 | margin: 0;
8 | padding: 0;
9 |
10 | font-family: 'Proxima Nova Regular', sans-serif;
11 | font-weight: normal;
12 |
13 | background: rgb(255,255,255);
14 |
15 | -webkit-touch-callout: none;
16 | -webkit-user-select: none;
17 | -khtml-user-select: none;
18 | -moz-user-select: none;
19 | -ms-user-select: none;
20 | user-select: none;
21 | -webkit-tap-highlight-color: rgba(0,0,0,0);
22 | }
23 | body {
24 | margin: 0 0.9em 0 0.9em;
25 | font-size: 150%;
26 | font-size: 7.5vw;
27 | }
28 |
29 | /* Add a top margin to the content corresponding to the height of the header. */
30 | body::before,
31 | section::before {
32 | display: block;
33 | width: 100%;
34 | height: 1.1em; /* Should correspond to header's height. */
35 | margin-bottom: 0.4em;
36 |
37 | content: '\0000a0'; /* Non-breaking space ( ) */
38 | font-size: 180%; /* Same as the header's font size. */
39 | line-height: 200%; /* Slighly more than the header's line-height. */
40 | }
41 |
42 | /* Add a bottom margin. */
43 | body::after,
44 | section::after {
45 | display:block;
46 | content: '';
47 | height: 1em;
48 | }
49 |
50 |
51 |
52 |
53 | /* ----------------------------------------------------------
54 | GENERAL
55 | ---------------------------------------------------------- */
56 |
57 | .hidden {
58 | display: none;
59 | }
60 |
61 |
62 |
63 |
64 | /* ----------------------------------------------------------
65 | HEADER
66 | ---------------------------------------------------------- */
67 |
68 | /* The header's height is determined by the contained text's font size. */
69 | header {
70 | box-sizing: border-box; /* Width & height includes padding & border. */
71 |
72 | position: fixed;
73 | top: 0;
74 | left: 0;
75 | right: 0;
76 |
77 | width: 100%;
78 | height: 1.1em;
79 |
80 | /* Left and right margins should correspond to body margins. */
81 | margin: 0 0 3.45% 0;
82 | /* Left padding should correspond to the back button's width. */
83 | padding: 1% 2% 1% 2%;
84 |
85 | font-size: 180%;
86 | line-height: 100%;
87 | text-align: center;
88 | vertical-align: middle;
89 |
90 | background: #f3f3f3;
91 |
92 | z-index: 1000;
93 | }
94 | header button {
95 | display: block;
96 | position: absolute;
97 | top: 50%;
98 | max-height: 70%;
99 |
100 | margin: 0;
101 |
102 | font-size: 30%;
103 |
104 | -webkit-transform: translateY(-50%);
105 | -ms-transform: translateY(-50%);
106 | transform: translateY(-50%);
107 | }
108 | header button.back {
109 | position: absolute;
110 | left: 0;
111 |
112 | height: 100%;
113 | margin: 0;
114 | padding: 0 0.9em;
115 |
116 | /* Font size is percentage of the header's font size. */
117 | font-size: 50%;
118 | color: #000;
119 |
120 | text-align: left;
121 |
122 | background: none;
123 |
124 | border-radius: 0;
125 | border: none;
126 |
127 | box-shadow: none;
128 | }
129 | header button.back img {
130 | height: 0.5em;
131 | }
132 | header img.logotype {
133 | position: absolute;
134 | top: 50%;
135 | left: 0;
136 | right: 0;
137 |
138 | height: 60%;
139 |
140 | margin: 0 auto;
141 |
142 | -webkit-transform: translateY(-50%);
143 | -ms-transform: translateY(-50%);
144 | transform: translateY(-50%);
145 | }
146 |
147 |
148 |
149 |
150 | /* ----------------------------------------------------------
151 | MENU
152 | ---------------------------------------------------------- */
153 |
154 | header button#menu-button {
155 | position: absolute;
156 | right: 0;
157 |
158 | width: 3em;
159 | height: 100%;
160 | max-height: 100%;
161 | margin: 0;
162 | padding: 0 0.9em;
163 |
164 | /* Font size is percentage of the header's font size. */
165 | font-size: 50%;
166 | color: #000;
167 |
168 | text-align: right;
169 |
170 | background: none;
171 |
172 | border-radius: 0;
173 | border: none;
174 |
175 | box-shadow: none;
176 | }
177 | header button#menu-button img {
178 | height: 34%;
179 | }
180 | header button#menu-button:focus {
181 | pointer-events: none;
182 | outline: none;
183 | }
184 | header button#menu-button:focus::-moz-focus-inner {
185 | border: 0;
186 | }
187 | header button#menu-button:focus + menu {
188 | opacity: 1;
189 | visibility: visible;
190 | }
191 | header menu {
192 | display: block;
193 | position: absolute;
194 | top: 100%;
195 | right: 0;
196 |
197 | margin: 0;
198 | padding: 0;
199 |
200 | background: #f3f3f3;
201 |
202 | opacity: 0;
203 | visibility: hidden;
204 | transition: visibility 0.5s;
205 | }
206 |
207 | header menu > * {
208 | display: block;
209 |
210 | padding: 0 1em 0 1em;
211 |
212 | font-family: 'Proxima Nova Bold';
213 | font-size: 35%;
214 | text-align: right;
215 | }
216 | header menu a {
217 | color: #000;
218 | text-decoration: none;
219 | }
220 |
221 | header h1 {
222 | width: 100%;
223 | margin: 0;
224 | padding: 0;
225 |
226 | font-size: inherit;
227 | line-height: inherit;
228 | font-weight: normal;
229 | text-align: center;
230 | vertical-align: middle;
231 | color: #eee;
232 |
233 | overflow: hidden;
234 | white-space: nowrap;
235 | text-overflow: ellipsis;
236 | }
237 |
238 |
239 |
240 |
241 | /* ----------------------------------------------------------
242 | SECTIONS
243 | ---------------------------------------------------------- */
244 |
245 | main {
246 | z-index: 0;
247 | }
248 |
249 | main footer {
250 | box-sizing: border-box; /* Width & height includes padding & border. */
251 |
252 | position: fixed;
253 | bottom: 0;
254 |
255 | width: 100%;
256 |
257 | margin: 0 -5% 0 -5%;
258 | padding: 0 5% 0 5%;
259 |
260 | background-color: #fff;
261 |
262 | box-shadow: inset 0 5px 9px -9px #000;
263 |
264 | z-index: 2;
265 | }
266 |
267 | section {
268 | display: none;
269 |
270 | box-sizing: border-box; /* Width & height includes padding & border. */
271 |
272 | position: absolute;
273 |
274 | top: 0;
275 | right: 0;
276 | bottom: 0;
277 | left: 0;
278 |
279 | width: 100%;
280 |
281 | padding: 0 0.9em 0 0.9em;
282 |
283 | background-color: #fff;
284 |
285 | z-index: 2;
286 |
287 | }
288 |
289 | section:target {
290 | display: block;
291 | }
292 |
293 |
294 |
295 | /* ----------------------------------------------------------
296 | INPUT FIELDS
297 | ---------------------------------------------------------- */
298 |
299 | input {
300 | margin: 0;
301 | padding: 0;
302 |
303 | border: none;
304 |
305 | font-family: inherit;
306 | font-size: 63%;
307 | }
308 |
309 | input[type="text"],input[type="url"],input[type="tel"],input[type="password"],
310 | input[type="email"],input[type="search"],input[type="number"],
311 | input[type="date"],input[type="month"],input[type="week"],input[type="time"] {
312 | width: 50%;
313 |
314 | padding: 7px 14px 7px 14px;
315 |
316 | font-family: inherit;
317 | font-size: 63%;
318 | color: #000;
319 |
320 | border-radius: 4px;
321 | border: none;
322 |
323 | background-color: #f3f3f3; /* Default color: "Arctic". */
324 | }
325 |
326 | textarea {
327 | box-sizing: border-box; /* Width & height includes padding & border. */
328 | width: 100%;
329 |
330 | padding: 7px 14px 7px 14px;
331 |
332 | border-radius: 4px;
333 | border: none;
334 |
335 | font-family: inherit;
336 | font-size: 63%;
337 | line-height: 1.5;
338 |
339 | background-color: #f3f3f3; /* Default color: "Arctic". */
340 | }
341 |
342 |
343 |
344 |
345 | /* ----------------------------------------------------------
346 | BUTTONS
347 | ---------------------------------------------------------- */
348 |
349 | button.clear {
350 | margin-left: 1%;
351 | background-color: #e15a64; /* Default color: "Soft Red". */
352 | }
353 |
354 | button,
355 | input.btn-group + label,
356 | .button {
357 | display: inline-block;
358 | box-sizing: border-box; /* Width & height includes padding & border. */
359 |
360 | font-family: 'Proxima Nova Black';
361 | font-size: inherit;
362 | line-height: 100%;
363 | text-align: center;
364 | text-transform: uppercase;
365 |
366 | color: #fff;
367 |
368 | margin: 0.5em 0 0.5em 0;
369 | padding: 9px 13px 8px 13px;
370 |
371 | border-radius: 4px;
372 | border: none;
373 |
374 | box-shadow: none;
375 |
376 | background-color: #b9b9b9; /* Default color: "Charcoal" */
377 | }
378 | body > button,
379 | body > input.btn-group + label,
380 | main > button,
381 | main > input.btn-group + label {
382 | font-size: 63% !important;
383 | }
384 | button:active, button.pressed,
385 | input.btn-group:active + label, input.btn-group + label:active,
386 | input.btn-group:checked + label {
387 | box-shadow: inset 0 8px 9px -9px #000;
388 | background-color: #9d9d9d; /* Default color: "Charcoal" */
389 | }
390 | button:focus,
391 | input.btn-group:focus {
392 | outline: none;
393 | }
394 |
395 | button.half,
396 | input.btn-group.half + label,
397 | input.btn-group + label.half {
398 | float: left;
399 | width: 50%; /* Fallback in case calc() is unsupported. */
400 | width: calc(50% - 4px);
401 | margin-left: 2px;
402 | margin-right: 2px;
403 | }
404 |
405 | button.third,
406 | input.btn-group.third + label,
407 | input.btn-group + label.third {
408 | float: left;
409 | width: 33%; /* Fallback in case calc() is unsupported. */
410 | width: calc(33% - 4px);
411 | margin-left: 2px;
412 | margin-right: 2px;
413 | }
414 |
415 | button.quarter,
416 | input.btn-group.quarter + label,
417 | input.btn-group + label.quarter {
418 | float: left;
419 | width: 25%; /* Fallback in case calc() is unsupported. */
420 | width: calc(25% - 4px);
421 | margin-left: 2px;
422 | margin-right: 2px;
423 | }
424 |
425 | button.wide,
426 | input.btn-group.wide + label,
427 | input.btn-group + label.wide {
428 | width: 100%;
429 | }
430 |
431 | button.big,
432 | input.btn-group.big + label,
433 | input.btn-group + label.big {
434 | font-size: 150%;
435 | line-height: 3.0;
436 | }
437 |
438 | /* Input with class btn-group should be accompanied by a label acting as a
439 | button. */
440 | input.btn-group {
441 | display: none;
442 | }
443 |
444 |
445 |
446 |
447 | /* ----------------------------------------------------------
448 | VARIOUS ELEMENTS
449 | ---------------------------------------------------------- */
450 |
451 | canvas {
452 | padding: 7px;
453 |
454 | border-radius: 4px;
455 | border: none;
456 |
457 | background-color: #f3f3f3; /* Default color: "Arctic" */
458 | }
459 |
460 | figcaption {
461 | font-size: 63%;
462 | }
463 |
464 | h1 {
465 | padding: 0;
466 | margin: 1em 0 0 0;
467 |
468 | font-family: 'Proxima Nova Black';
469 | font-size: 100%;
470 | line-height: 1.5;
471 |
472 | vertical-align: middle;
473 | }
474 | header + h1, body h1:first-child, main h1:first-child, article h1:first-child {
475 | margin-top: 0;
476 | }
477 |
478 | h2,h3,h4,h5,h6 {
479 | margin: 1em 0 0 0;
480 | padding: 0;
481 |
482 | font-family: 'Proxima Nova Bold';
483 | font-size: 63%;
484 |
485 | line-height: 1.5;
486 |
487 | vertical-align: middle;
488 | }
489 |
490 | a {
491 | color: #52afb8; /* Default color: "Blue Hue" (dark variant) */
492 | }
493 |
494 | p {
495 | margin: 0.4em 0 0.4em 0;
496 | padding: 0;
497 |
498 | font-size: 63%;
499 | line-height: 1.5;
500 | }
501 |
502 | table {
503 | table-layout: fixed;
504 | width: 100%;
505 | font-size: 63%;
506 | text-align: left;
507 | }
508 | thead {
509 | font-family: 'Proxima Nova Bold';
510 | }
511 |
512 | strong {
513 | font-family: 'Proxima Nova Bold';
514 | }
515 |
516 | code {
517 | font-size: inherit;
518 | }
519 |
520 | ol,ul {
521 | list-style-type: none;
522 | list-style-position: inside;
523 | counter-reset: item;
524 |
525 | margin: 0.5em 0 0 0;
526 | padding: 0;
527 |
528 | font-size: 63%;
529 | }
530 | ol li, ul li {
531 | padding: 0.5em 0 0.5em 0;
532 |
533 | font-size: inherit;
534 | line-height: 1.5;
535 | }
536 | ol li:before {
537 | content: counter(item) ". ";
538 | counter-increment: item;
539 | font-family: 'Proxima Nova Bold';
540 | }
541 | ul.dynamic {
542 | list-style-type: none;
543 | padding: 0;
544 | }
545 | ul.dynamic li {
546 | padding: 4% 15% 4% 4%;
547 |
548 | border-radius: 4px;
549 | border: none;
550 |
551 | box-shadow: inset 0 -7px 12px -12px #000;
552 |
553 | background-color: #f3f3f3;
554 | }
555 | ul.dynamic li:last-child {
556 | box-shadow: none;
557 | }
558 | ul.dynamic li a {
559 | color: #000;
560 | }
561 | ul.dynamic.arrow li,
562 | ul.dynamic li.arrow {
563 | background-image: url('../images/arrow-right.svg');
564 | background-position: 94% center; /* CSS 2 fallback */
565 | background-position: right 5% center; /* requires CSS 3 */
566 | background-size: 1em auto;
567 | background-repeat: no-repeat;
568 | }
569 |
570 | article {
571 | display: none;
572 | z-index: 2;
573 |
574 | font-size: 100%;
575 | }
576 |
577 |
578 |
579 |
580 | /* ----------------------------------------------------------
581 | COLORS
582 | ---------------------------------------------------------- */
583 |
584 | /* Color: "Soft Red"
585 | Class name: red
586 | ---------------- */
587 | .color_softred { color: #e15a64; }
588 | .bg_red, input.red, button.red, ul.red li, label.red,
589 | input.red + label {
590 | background-color: #e15a64;
591 | }
592 | /* Downpressed button. */
593 | button.red:active, button.red.pressed,
594 | /* Input element with adjacent label. */
595 | input.btn-group:active + label.red,
596 | input.btn-group.red:active + label,
597 | input.btn-group:checked + label.red,
598 | input.btn-group.red:checked + label {
599 | background-color: #cb414b;
600 | }
601 |
602 | /* Color: "Bright Light"
603 | Class name: yellow
604 | -------------------- */
605 | .color_brightlight { color: #ecd53b; }
606 | .bg_yellow, input.yellow, button.yellow, ul.yellow li, label.yellow,
607 | input.yellow + label {
608 | background-color: #ecd53b;
609 | }
610 | /* Downpressed button. */
611 | button.yellow:active, button.yellow.pressed,
612 | /* Input element with adjacent label. */
613 | input.btn-group:active + label.yellow,
614 | input.btn-group.yellow:active + label,
615 | input.btn-group:checked + label.yellow,
616 | input.btn-group.yellow:checked + label {
617 | background-color: #d9c022;
618 | }
619 |
620 | /* Color: "Wave Green"
621 | Class name: green
622 | ------------------ */
623 | .color_wavegreen { color: #54dfb3; }
624 | .bg_green, input.green, button.green, ul.green li, label.green,
625 | input.green + label {
626 | background-color: #54dfb3;
627 | }
628 | /* Downpressed button. */
629 | button.green:active, button.green.pressed,
630 | /* Input element with adjacent label. */
631 | input.btn-group:active + label.green,
632 | input.btn-group.green:active + label,
633 | input.btn-group:checked + label.green,
634 | input.btn-group.green:checked + label {
635 | background-color: #3aca9c;
636 | }
637 |
638 | /* Color: "Blue Hue"
639 | Clas name: blue
640 | ------------------ */
641 | .color_bluehue { color: #6bc6ce; }
642 | .bg_blue, input.blue, button.blue, ul.blue li, label.blue,
643 | input.blue + label {
644 | background-color: #6bc6ce;
645 | }
646 | /* Downpressed button. */
647 | button.blue:active, button.blue.pressed,
648 | /* Input element with adjacent label. */
649 | input.btn-group:active + label.blue,
650 | input.btn-group.blue:active + label,
651 | input.btn-group:checked + label.blue,
652 | input.btn-group.blue:checked + label {
653 | background-color: #52afb8;
654 | }
655 |
656 | /* Color: "Indigo"
657 | Class name: indigo
658 | ------------------ */
659 | .color_indigo { color: #b48b9b; }
660 | .bg_indigo, input.indigo, button.indigo, ul.indigo li, label.indigo,
661 | input.indigo + label {
662 | background-color: #b48b9b;
663 | }
664 | /* Downpressed button. */
665 | button.indigo:active, button.indigo.pressed,
666 | /* Input element with adjacent label. */
667 | input.btn-group:active + label.indigo,
668 | input.btn-group.indigo:active + label,
669 | input.btn-group:checked + label.indigo,
670 | input.btn-group.indigo:checked + label {
671 | background-color: #9c7283;
672 | }
673 |
674 | /* Color: "Arctic"
675 | Class name: arctic
676 | ------------------ */
677 | .color_arctic { color: #f3f3f3; }
678 | .bg_arctic, input.arctic, button.arctic, ul.arctic li,
679 | input.arctic + label {
680 | background-color: #f3f3f3;
681 | }
682 | /* Downpressed button. */
683 | button.arctic:active, button.arctic.pressed,
684 | /* Input element with adjacent label. */
685 | input.btn-group:active + label.arctic,
686 | input.btn-group.arctic:active + label,
687 | input.btn-group:checked + label.arctic,
688 | input.btn-group.arctic:checked + label {
689 | background-color: #656565;
690 | }
691 |
692 | /* Color: "Aluminium"
693 | Class name: aluminium
694 | ------------------ */
695 | .color_aluminium { color: #e2e2e2 }
696 | .bg_aluminium, input.aluminium, button.aluminium, ul.aluminium li,
697 | input.aluminium + label {
698 | background-color: #e2e2e2;
699 | }
700 | /* Downpressed button. */
701 | button.aluminium:active, button.aluminium.pressed,
702 | /* Input element with adjacent label. */
703 | input.btn-group:active + label.aluminium,
704 | input.btn-group.aluminium:active + label,
705 | input.btn-group:checked + label.aluminium,
706 | input.btn-group.aluminium:checked + label {
707 | background-color: #c0c0c0;
708 | }
709 |
710 | /* Color: "Charcoal"
711 | Class name: charcoal
712 | ------------------ */
713 | .color_charcoal { color: #b9b9b9; }
714 | .bg_charcoal, input.charcoal, button.charcoal, ul.charcoal li,
715 | input.charcoal + label {
716 | background-color: #b9b9b9;
717 | }
718 | /* Downpressed button. */
719 | button.charcoal:active, button.charcoal.pressed,
720 | /* Input element with adjacent label. */
721 | input.btn-group:active + label.charcoal,
722 | input.btn-group.charcoal:active + label,
723 | input.btn-group:checked + label.charcoal,
724 | input.btn-group.charcoal:checked + label {
725 | background-color: #9d9d9d;
726 | }
727 |
728 | /* Color: "Stone"
729 | Class name: stone
730 | ------------------ */
731 | .color_stone { color: #777777 }
732 | .bg_stone, input.stone, button.stone, ul.stone li,
733 | input.stone + label {
734 | background-color: #777777;
735 | }
736 | /* Downpressed button. */
737 | button.stone:active, button.stone.pressed,
738 | /* Input element with adjacent label. */
739 | input.btn-group:active + label.stone,
740 | input.btn-group.stone:active + label,
741 | input.btn-group:checked + label.stone,
742 | input.btn-group.stone:checked + label {
743 | background-color: #656565;
744 | }
745 |
746 | /* Color: "Jet Black"
747 | Class name: jetblack
748 | ------------------ */
749 | .color_black { color: #070707 }
750 | .bg_black, input.black, button.black, ul.black li,
751 | input.black + label {
752 | background-color: #070707;
753 | }
754 | /* Downpressed button. */
755 | button.black:active, button.black.pressed,
756 | /* Input element with adjacent label. */
757 | input.btn-group:active + label.black,
758 | input.btn-group.black:active + label,
759 | input.btn-group:checked + label.black,
760 | input.btn-group.black:checked + label {
761 | background-color: #000000;
762 | }
763 |
764 | @media screen and (orientation: landscape) and (-webkit-min-device-pixel-ratio : 2),
765 | screen and (orientation: landscape) and (min--moz-device-pixel-ratio: 2),
766 | screen and (orientation: landscape) and (min-device-pixel-ratio : 2) {
767 | body { font-size: 4.5vw; }
768 | }
769 |
--------------------------------------------------------------------------------
/cordova/www/app.js:
--------------------------------------------------------------------------------
1 | // Route all console logs to Evothings studio log
2 | if (window.hyper && window.hyper.log) { console.log = hyper.log; };
3 |
4 | if (window.cordova) {
5 | // We're in the Cordova app; initialize when the device is ready.
6 | // Do CodePush sync when the device is ready and every time we're
7 | // foregrounded.
8 | document.addEventListener('deviceready', function() {
9 | window.codePush.sync();
10 | window.codePush.getCurrentPackage(function(currentPackage) {
11 | if (currentPackage) {
12 | $('#codePushVersion').html(currentPackage.label);
13 | }
14 | });
15 | evothings.scriptsLoaded(app.initialize);
16 | });
17 | document.addEventListener("resume", function() {
18 | window.codePush.sync();
19 | });
20 | } else {
21 | // No Cordova. Wait until jQuery's available (actually, we just need to wait
22 | // until app.initialize() is defined below, but this gives us a reasonable
23 | // delay), then initialize.
24 | $(function() {
25 | app.initialize();
26 | });
27 | }
28 |
29 | // Define globally for easy console debugging
30 | app = {};
31 | app.devices = {};
32 | app.DFRBLU_SERVICE_UUID = '0000dfb0-0000-1000-8000-00805f9b34fb';
33 | app.DFRBLU_CHAR_RXTX_UUID = '0000dfb1-0000-1000-8000-00805f9b34fb';
34 | app.PAINT_DIR = 'img/paint/'
35 |
36 | // Number of devices that still need to be sent the current command.
37 | var pendingSends = 0;
38 | // If truthy, this will be executed and unset when pendingSends returns to 0.
39 | var doPostSend = null;
40 |
41 | var speedSlider = document.getElementById('movement_speed');
42 | var sizeSlider = document.getElementById('movement_size');
43 | var imgBrightSlider = document.getElementById('img_bright_slider');
44 | var imgWidthSlider = document.getElementById('img_width_slider');
45 | var blobsBrightSlider = document.getElementById('blobs_bright_slider');
46 | var blobsSpeedSlider = document.getElementById('blobs_speed_slider');
47 | var blobsRedSizeSlider = document.getElementById('blobs_red_size_slider');
48 | var blobsGreenSizeSlider = document.getElementById('blobs_green_size_slider');
49 | var blobsBlueSizeSlider = document.getElementById('blobs_blue_size_slider');
50 | var demoBrightSlider = document.getElementById('demo_bright_slider');
51 | var demoTimeSlider = document.getElementById('demo_time_slider');
52 |
53 | function sendColor() {
54 | if (!app.initialized) {
55 | return;
56 | }
57 | if (pendingSends) {
58 | doPostSend = function() {
59 | sendColor();
60 | };
61 | return;
62 | }
63 | var color = $("#picker").spectrum("get");
64 | var hsv = color.toHsv();
65 | hsv.h = Math.round(hsv.h / 360.0 * 255.0);
66 | hsv.s = Math.round(hsv.s * 255.0);
67 | hsv.v = Math.round(hsv.v * 255.0);
68 | var rainbows = $('#rainbows').is(':checked');
69 | var speed = parseInt(speedSlider.noUiSlider.get());
70 | var size = parseInt(sizeSlider.noUiSlider.get());
71 | if ($('#movement').is(':checked')) {
72 | app.sendCommand(null, hsv.v,
73 | $('#movement_random').is(':checked') ? 'Z' : 'M',
74 | hsv.h, hsv.s,
75 | speed + (
76 | rainbows ? 101 : 0),
77 | size + (
78 | $('#movement_vertical').is(':checked') ? 0 : 101));
79 | } else {
80 | app.sendCommand(null, hsv.v, rainbows ? 'R' : 'C', hsv.h, hsv.s);
81 | }
82 | };
83 |
84 | function imageDataPixelToByte(imageData, x, y) {
85 | // Takes an ImageData object and x,y and returns
86 | // the pixel at that coordiante in RRRGGGBB format.
87 | var idx = (x + y * imageData.width) * 4;
88 | return (
89 | (Math.floor(imageData.data[idx + 0] / 32) << 5) +
90 | (Math.floor(imageData.data[idx + 1] / 32) << 2) +
91 | (Math.floor(imageData.data[idx + 2] / 64) << 0));
92 | }
93 |
94 | handleGif = function(file) {
95 | var brightness = parseInt(imgBrightSlider.noUiSlider.get());
96 | var frameTime = parseInt(imgWidthSlider.noUiSlider.get());
97 | var canvas = document.getElementById('canvas');
98 | gifler(file).get().then(function(animator) {
99 | window.animator = animator; // XXX TEMP
100 | animator.onDrawFrame = function(ctx, frame) {
101 | for (var i in app.devices) {
102 | var dev = app.devices[i];
103 | if (!dev.isConnected()) {
104 | // Don't do anything with disconnected devices.
105 | continue;
106 | }
107 | if (!dev.maxFrames) {
108 | console.log("Skipping device with maxFrames=0", dev);
109 | continue;
110 | }
111 | if (!dev.imgArr) {
112 | dev.imgArr = new Uint8Array(
113 | dev.height * dev.width * 3 * animator._frames.length);
114 | } else if (animator._loops > 0) {
115 | app.sendCommand(
116 | dev, brightness, 'P',
117 | animator._frames.length, frameTime,
118 | 24);
119 | app.sendData(dev, dev.imgArr);
120 | dev.imgArr = null;
121 | continue;
122 | }
123 |
124 | // Draw the image on the canvas to scale it.
125 | ctx.fillStyle = 'black';
126 | ctx.fillRect(0, 0, canvas.width, canvas.height);
127 | ctx.drawImage(frame.buffer, 0, 0, dev.width, dev.height);
128 | // Grab scaled pixel data and put it in our data array.
129 | var data = ctx.getImageData(0, 0, dev.width, dev.height);
130 | for (var x = 0; x < dev.width; x++) {
131 | for (var y = 0; y < dev.height; y++) {
132 | // Loop through R, G, B
133 | for (var i = 0; i < 3; i++) {
134 | dev.imgArr[(x + dev.width * y +
135 | animator._frameIndex * dev.width * dev.height) * 3 + i] =
136 | data.data[(x + y * dev.width) * 4 + i];
137 | }
138 | }
139 | }
140 | }
141 | if (animator._loops > 0) {
142 | animator.stop();
143 | }
144 | };
145 | //var canvas = gifler.Gif.getCanvasElement('#canvas');
146 | animator.animateInCanvas(canvas, false);
147 | }, function() {
148 | console.log("animation error??", arguments);
149 | });
150 | }
151 |
152 | sendImage = function(file) {
153 | if (!app.initialized) {
154 | return;
155 | }
156 | if (pendingSends) {
157 | // Don't send a new image, but do sent the latest brightness/width
158 | doPostSend = function() {
159 | sendImage();
160 | };
161 | return;
162 | }
163 | var brightness = parseInt(imgBrightSlider.noUiSlider.get());
164 | var frameTime = parseInt(imgWidthSlider.noUiSlider.get());
165 | if (!file) {
166 | // No file specified: just tell poi new brightness/frame-time
167 | app.sendCommand(null, brightness, 'P', 0, frameTime);
168 | return;
169 | }
170 |
171 | /*
172 | * EXPERIMENTAL: Animated GIF support.
173 | * Kinda works, but doesn't check for maxFrames,
174 | * only supports 24-bit color,
175 | * is very slow (needs a full loop of the gif before sending),
176 | * and some frames don't appear as expected.
177 | if (file.endsWith('.gif')) {
178 | handleGif(file);
179 | return;
180 | }
181 | */
182 |
183 | var img = new Image();
184 | img.src = file;
185 | var canvas = document.getElementById('canvas');
186 | var ctx = canvas.getContext('2d');
187 | img.onload = function() {
188 | // TODO: Avoid recomputing all this stuff for 2 (poi) devices
189 | // of the same dimensions.
190 | for (var i in app.devices) {
191 | var dev = app.devices[i];
192 | if (!dev.isConnected()) {
193 | // Don't do anything with disconnected devices.
194 | continue;
195 | }
196 | if (!dev.maxFrames) {
197 | console.log("Skipping device with maxFrames=0", dev);
198 | continue;
199 | }
200 |
201 | // This is a persistence-of-vision display if its width is 1.
202 | // Otherwise it's two-dimensional.
203 | var pov = dev.width == 1;
204 | // Frames of image data we will send.
205 | var numFrames = pov ?
206 | // POV display: scale the image proportinally if we can;
207 | // otherwise make it maxFrames wide.
208 | Math.min(
209 | Math.round(img.naturalWidth * dev.height / img.naturalHeight),
210 | dev.maxFrames) :
211 | // 2D display: only sending one frame.
212 | 1;
213 | // Image will be scaled to numFrames for POV imagery
214 | // and scaled to the device's width for 2D imagery
215 | var width = pov ? numFrames : dev.width;
216 | // If maxFrames is at least 3x numFrames, we can send in 24-bit color
217 | // instead of 8-bit color.
218 | var trueColor = dev.maxFrames >= numFrames * 3;
219 |
220 | // Draw the image on the canvas to scale it.
221 | ctx.fillStyle = 'black';
222 | ctx.fillRect(0, 0, canvas.width, canvas.height);
223 | ctx.drawImage(img, 0, 0, width, dev.height);
224 | // TODO: Needed?
225 | img.style.display = 'none';
226 | // Multiply array size by 3 for 24-bit color.
227 | var arr = new Uint8Array(dev.height * width * (trueColor ? 3 : 1));
228 | // Grab scaled pixel data and put it in our data array.
229 | var data = ctx.getImageData(0, 0, width, dev.height);
230 | for (var x = 0; x < width; x++) {
231 | for (var y = 0; y < dev.height; y++) {
232 | var outputIdx = pov ?
233 | // POV images are sent by column,
234 | x * dev.height + y :
235 | // while 2D images are standardized to be sent as progressive
236 | // rows of data.
237 | x + width * y;
238 | if (trueColor) {
239 | // Loop through R, G, B
240 | for (var i = 0; i < 3; i++) {
241 | // Image data is in RGBA format
242 | arr[outputIdx * 3 + i] = data.data[(x + y * width) * 4 + i];
243 | }
244 | } else {
245 | arr[outputIdx] = imageDataPixelToByte(data, x, y);
246 | }
247 | }
248 | }
249 | app.sendCommand(
250 | dev, brightness, 'P',
251 | numFrames, frameTime,
252 | trueColor ? 24 : 8);
253 | app.sendData(dev, arr);
254 | }
255 | };
256 | };
257 |
258 | sendBlobs = function() {
259 | if (!app.initialized) {
260 | return;
261 | }
262 | if (pendingSends) {
263 | doPostSend = function() {
264 | sendBlobs();
265 | };
266 | return;
267 | }
268 | var bright = parseInt(blobsBrightSlider.noUiSlider.get());
269 | var speed = parseInt(blobsSpeedSlider.noUiSlider.get());
270 | var rs = parseInt(blobsRedSizeSlider.noUiSlider.get());
271 | var gs = parseInt(blobsGreenSizeSlider.noUiSlider.get());
272 | var bs = parseInt(blobsBlueSizeSlider.noUiSlider.get());
273 | app.sendCommand(null, bright, 'B', speed, rs, gs, bs);
274 | };
275 |
276 | sendText = function() {
277 | if (!app.initialized) {
278 | return;
279 | }
280 | if (pendingSends) {
281 | doPostSend = function() {
282 | sendText();
283 | };
284 | return;
285 | }
286 | var color = $("#textColorPicker").spectrum("get");
287 | var hsv = color.toHsv();
288 | hsv.h = Math.round(hsv.h / 360.0 * 255.0);
289 | hsv.s = Math.round(hsv.s * 255.0);
290 | hsv.v = Math.round(hsv.v * 255.0);
291 | app.sendCommand(null, hsv.v, 'T', hsv.h, hsv.s);
292 | var txt = new TextEncoder("ascii").encode($('#textInput').val() + '\0');
293 | app.sendData(null, txt);
294 | }
295 |
296 | sendDemo = function() {
297 | if (!app.initialized) {
298 | return;
299 | }
300 | if (pendingSends) {
301 | doPostSend = function() {
302 | sendDemo();
303 | };
304 | return;
305 | }
306 | var bright = parseInt(demoBrightSlider.noUiSlider.get());
307 | var time = parseInt(demoTimeSlider.noUiSlider.get());
308 | $('#demoTimeSeconds').html(time == 256 ? 'infinite': time);
309 | var mode = $('input[type=radio][name=demo-mode]:checked').val();
310 | app.sendCommand(null, bright, 'd', time == 256 ? 0 : time, mode == 'added' ? 0 : 1);
311 | }
312 | saveDemoCurrent = function() {
313 | app.sendAsk(null, 'ds');
314 | }
315 | moveDemoNext = function() {
316 | app.sendAsk(null, 'dn');
317 | }
318 | clearDemoSaved = function() {
319 | app.sendAsk(null, 'dc');
320 | }
321 |
322 | app.initialize = function() {
323 |
324 | $('#accordion .collapse').on('show.bs.collapse', function() {
325 | if (this.id === 'collapseColor') {
326 | // Switch to color mode: send the latest
327 | sendColor();
328 | } else if (this.id === 'collapseImage') {
329 | // Switch to image mode: we won't send any image until the user selects
330 | // one, so revert the selection to None.
331 | $('#img_select_none').click();
332 | } else if (this.id === 'collapseBlobs') {
333 | sendBlobs();
334 | } else if (this.id === 'collapseText') {
335 | sendText();
336 | } else if (this.id === 'collapseDemo') {
337 | sendDemo();
338 | }
339 | });
340 |
341 | $('#picker').spectrum({
342 | color: "#FF0000",
343 | flat: true,
344 | showInput: false,
345 | showButtons: false,
346 | preferredFormat: "hex",
347 | move: sendColor
348 | });
349 |
350 | noUiSlider.create(speedSlider, {
351 | start: 45,
352 | range: { min: 0, max: 100},
353 | });
354 | speedSlider.noUiSlider.on('update', function() {
355 | sendColor();
356 | });
357 | noUiSlider.create(sizeSlider, {
358 | start: 45,
359 | range: { min: 0, max: 100},
360 | });
361 | sizeSlider.noUiSlider.on('update', function() {
362 | sendColor();
363 | });
364 |
365 | $('#rainbows').change(function() {
366 | sendColor();
367 | });
368 | $('#movement').change(function() {
369 | $('.ctl-movement').toggle(this.checked);
370 | sendColor();
371 | }).change();
372 | $('.ctl-movement').change(function() {
373 | sendColor();
374 | });
375 |
376 | if (!!window.cordova) {
377 | window.resolveLocalFileSystemURL(
378 | cordova.file.applicationDirectory + 'www/' + app.PAINT_DIR,
379 | function (fileSystem) {
380 | var reader = fileSystem.createReader();
381 | reader.readEntries(
382 | function (entries) {
383 | var images = [];
384 | for (var i in entries) {
385 | if (entries[i].isFile && !entries[i].isDirectory) {
386 | images.push(entries[i].name);
387 | }
388 | }
389 | initImages(images);
390 | },
391 | function (err) {
392 | console.log(err);
393 | }
394 | );
395 | }, function (err) {
396 | console.log(err);
397 | }
398 | );
399 | } else {
400 | // Not cordova? Rely on a static list, maybe up to date.
401 | initImages(['mario.png', 'fire.jpg', 'pacman.png', 'space_invaders.png']);
402 | }
403 | noUiSlider.create(imgBrightSlider, {
404 | start: 255,
405 | range: { min: 0, max: 255},
406 | });
407 | noUiSlider.create(imgWidthSlider, {
408 | start: 0,
409 | range: { min: 0, max: 255},
410 | });
411 | imgBrightSlider.noUiSlider.on('update', function() {
412 | sendImage();
413 | });
414 | imgWidthSlider.noUiSlider.on('update', function() {
415 | sendImage();
416 | });
417 |
418 | noUiSlider.create(blobsBrightSlider, {
419 | start: 255,
420 | range: { min: 0, max: 255 },
421 | });
422 | blobsBrightSlider.noUiSlider.on('update', function() {
423 | sendBlobs();
424 | });
425 | [blobsSpeedSlider, blobsRedSizeSlider, blobsGreenSizeSlider,
426 | blobsBlueSizeSlider].forEach(function(slider) {
427 | noUiSlider.create(slider, {
428 | start: 64,
429 | range: { min: 0, max: 255 },
430 | });
431 | slider.noUiSlider.on('update', function() {
432 | sendBlobs();
433 | });
434 | });
435 |
436 | $('#textColorPicker').spectrum({
437 | color: "#FF0000",
438 | flat: true,
439 | showInput: false,
440 | showButtons: false,
441 | preferredFormat: "hex",
442 | move: sendText
443 | });
444 | $('#textInput').on('input', sendText);
445 |
446 | $('#demoAddCurrent').click(function(e) {
447 | saveDemoCurrent();
448 | e.stopPropagation();
449 | });
450 | noUiSlider.create(demoBrightSlider, {
451 | start: 255,
452 | range: { min: 0, max: 255 },
453 | });
454 | demoBrightSlider.noUiSlider.on('update', function() {
455 | sendDemo();
456 | });
457 | noUiSlider.create(demoTimeSlider, {
458 | start: 30,
459 | range: { min: 1, max: 256 },
460 | });
461 | demoTimeSlider.noUiSlider.on('update', function() {
462 | sendDemo();
463 | });
464 | $('input[type=radio][name=demo-mode]').change(function() {
465 | sendDemo();
466 | });
467 | $('#demoNext').click(function() {
468 | moveDemoNext();
469 | });
470 | $('#demoClear').click(function() {
471 | if (window.confirm(
472 | "WARNING! This will clear ALL saved demo settings on ALL connected devices."
473 | )) {
474 | clearDemoSaved();
475 | }
476 | });
477 |
478 | app.initialized = true;
479 | app.startScan();
480 | listDevices();
481 |
482 | setTimeout(function() {
483 | if (Object.keys(app.devices).length === 0) {
484 | // We have no devices after a while. Offer the fake device.
485 | var fake = {
486 | fake: true,
487 | connected: false,
488 | isConnected: function() { return this.connected; },
489 | close: function() { this.connected = false; },
490 | width: 1,
491 | height: 18,
492 | numLeds: 18,
493 | maxFrames: 64,
494 | maxText: 32,
495 | name: 'Fake Blume',
496 | address: 'abcdefghi'
497 | };
498 | app.devices[fake.address] = fake;
499 | listDevices();
500 | }
501 | }, 3000);
502 | };
503 |
504 | function initImages(files) {
505 | var none = $('#img_select_none');
506 | for (var i in files) {
507 | var file = files[i];
508 | var clone = none.clone().attr('id', 'img_select_' + file)
509 | .attr('imageid', file).removeClass('img-select-selected');
510 | clone.html(' ');
511 | none.after(clone);
512 | }
513 | $('.img-select').click(function() {
514 | if ($(this).hasClass('img-select-selected')) {
515 | return;
516 | }
517 | if (pendingSends) {
518 | return;
519 | }
520 | $('.img-select').removeClass('img-select-selected');
521 | $(this).addClass('img-select-selected');
522 | var img = $(this).attr('imageid');
523 | if (img) {
524 | if (img === 'url') {
525 | $('#img_url').focus();
526 | var val = $('#img_url').val();
527 | if (val) {
528 | sendImage(val);
529 | }
530 | } else {
531 | sendImage(app.PAINT_DIR + img);
532 | }
533 | }
534 | });
535 | $('#img_url').change(function() {
536 | if ($(this).val()) {
537 | $('#img_select_url').removeClass('img-select-selected').click();
538 | }
539 | });
540 | }
541 |
542 | function listDevices() {
543 | $('#scanResultView').empty();
544 | if (Object.keys(app.devices).length === 0) {
545 | $('#loadingIndicator').show();
546 | $('#controlView').hide();
547 | return;
548 | } else {
549 | $('#loadingIndicator').hide();
550 | $('#scanResultView').show();
551 | }
552 | var anyConnected = false;
553 | var anyImage = false;
554 | var anyText = false;
555 | // Set the max text length to the lowest supported maxText of any device.
556 | var minText = 999;
557 | for (var i in app.devices) {
558 | var dev = app.devices[i];
559 | var htmlString = '' +
573 | '
' + dev.name;
574 | if (dev.connectPending) {
575 | htmlString += ' Connecting... ';
576 | }
577 | htmlString += '
' +
578 | '
' + dev.address + '
' +
579 | '
';
580 | $('#scanResultView').append($(htmlString));
581 | }
582 | $('#controlView').toggle(anyConnected);
583 | $('#imageCard').toggle(anyImage);
584 | $('#textCard').toggle(anyText);
585 | $('#textInput').attr('maxlength', minText);
586 | };
587 |
588 | app.startScan = function() {
589 | if (!window.cordova) {
590 | return;
591 | }
592 | console.log('Scanning started...');
593 |
594 | var htmlString =
595 | ' ' +
597 | ' Scanning...
';
598 |
599 | $('#scanResultView').append($(htmlString));
600 |
601 | $('#scanResultView').show();
602 |
603 | function onScanSuccess(dev) {
604 | if (dev.name &&
605 | (dev.name.indexOf('Blume') !== -1 ||
606 | dev.name.indexOf('Bluno') !== -1)) {
607 | app.devices[dev.address] = dev;
608 |
609 | console.log(
610 | 'Found: ' + dev.name + ', ' +
611 | dev.address + ', ' + dev.rssi);
612 |
613 | listDevices();
614 | }
615 | }
616 |
617 | function onScanFailure(errorCode) {
618 | // Write debug information to console.
619 | console.log('Error ' + errorCode);
620 | }
621 |
622 | evothings.easyble.reportDeviceOnce(true);
623 | evothings.easyble.startScan(onScanSuccess, onScanFailure);
624 |
625 | };
626 |
627 | app.toggleConnect = function(address) {
628 | var dev = app.devices[address];
629 | if (dev && dev.isConnected()) {
630 | app.disconnectFrom(address);
631 | } else {
632 | app.connectTo(address);
633 | }
634 | }
635 |
636 | app.connectTo = function(address) {
637 | var dev = app.devices[address];
638 | if (dev.fake) {
639 | dev.connectPending = true;
640 | listDevices();
641 | setTimeout(function() {
642 | dev.connectPending = false;
643 | dev.connected = true;
644 | listDevices();
645 | }, 1000);
646 | return;
647 | }
648 |
649 | function onConnectSuccess(dev) {
650 | function onServiceSuccess(dev) {
651 |
652 | console.log('Connected to ' + dev.name);
653 |
654 | dev.enableNotification(
655 | app.DFRBLU_SERVICE_UUID,
656 | app.DFRBLU_CHAR_RXTX_UUID,
657 | app.receivedData.bind(dev),
658 | function(errorCode) {
659 | console.log('BLE enableNotification error: ' + errorCode);
660 | },
661 | { writeConfigDescriptor: false });
662 |
663 | dev.connectPending = false;
664 | listDevices();
665 |
666 | app.sendAsk(dev, 'D');
667 | }
668 |
669 | function onServiceFailure(errorCode) {
670 | // Write debug information to console.
671 | console.log('Error reading services: ' + errorCode);
672 | dev.connectPending = false;
673 | listDevices();
674 | }
675 |
676 | // Connect to the appropriate BLE service
677 | dev.readServices([app.DFRBLU_SERVICE_UUID], onServiceSuccess, onServiceFailure);
678 | }
679 |
680 | function onConnectFailure(errorCode) {
681 | // Write debug information to console
682 | console.log('Error ' + errorCode);
683 | if (errorCode === 133) {
684 | // On Android this just means we should retry!
685 | console.log("Attempting reconnect to " + dev.name);
686 | app.connectTo(address);
687 | } else {
688 | dev.connectPending = false;
689 | listDevices();
690 | }
691 | }
692 |
693 | // Connect to our device
694 | console.log('Identifying service for communication');
695 | dev.connect(onConnectSuccess, onConnectFailure);
696 | dev.connectPending = true;
697 | listDevices();
698 | };
699 |
700 | app.disconnectFrom = function(address) {
701 | var dev = app.devices[address];
702 | dev.connectPending = false;
703 | dev.close();
704 | listDevices();
705 | };
706 |
707 | app.sendCommand = function(devices, brightness, mode) {
708 | var data = Array.prototype.slice.call(arguments, 3);
709 | // Commands always have brightness
710 | var cmd = [brightness];
711 | // Add mode if it was passed in
712 | if (mode) {
713 | // Convert one-character string to character code
714 | cmd.push(mode.charCodeAt(0));
715 | }
716 | // !, length of the rest, the rest!
717 | app.sendData(devices, [0x21, cmd.length + data.length].concat(cmd, data));
718 | }
719 |
720 | app.sendAsk = function(devices, question) {
721 | var data = [0x21, 0];
722 | for (var i = 0; i < question.length; i++) {
723 | data.push(question.charCodeAt(i));
724 | }
725 | app.sendData(devices, data);
726 | }
727 |
728 | function toHexString(byteArray) {
729 | var s = '0x';
730 | for (var i in byteArray) {
731 | s += ('0' + (byteArray[i] & 0xFF).toString(16)).slice(-2);
732 | };
733 | return s;
734 | }
735 |
736 | function sendEnd() {
737 | pendingSends--;
738 | if (!pendingSends && doPostSend) {
739 | doPostSend();
740 | doPostSend = null;
741 | }
742 | }
743 |
744 | app.sendData = function(devices, data) {
745 | var tosend;
746 | if ((typeof data) == 'string') {
747 | tosend = new TextEncoder("ascii").encode(data);
748 | } else if (Array.isArray(data)) {
749 | tosend = new Uint8Array(data);
750 | } else {
751 | tosend = data;
752 | }
753 | if (tosend.length > 16) {
754 | //console.log("chunking data of length", tosend.length);
755 | for (var i = 0; i < tosend.length; i += 16) {
756 | app.sendData(devices, tosend.slice(i, i+16));
757 | }
758 | return;
759 | }
760 | //console.log("sending data of length", tosend.length);
761 | if (!devices) {
762 | devices = app.devices;
763 | } else if (!Array.isArray(devices)) {
764 | devices = [devices];
765 | }
766 |
767 | for (var i in devices) {
768 | var dev = devices[i];
769 | if (!dev.isConnected() || dev.fake) {
770 | continue;
771 | }
772 | pendingSends++;
773 | dev.writeCharacteristic(
774 | app.DFRBLU_CHAR_RXTX_UUID,
775 | tosend,
776 | function() {
777 | //console.log("SENT: " + toHexString(tosend));
778 | sendEnd();
779 | },
780 | function(err) {
781 | console.log("ERR:", err, pendingSends);
782 | sendEnd();
783 | }
784 | );
785 | }
786 | };
787 |
788 | function buf2hex(buffer) { // buffer is an ArrayBuffer
789 | return Array.prototype.map.call(new Uint8Array(buffer), function(x) { return ('00' + x.toString(16)).slice(-2); }).join('');
790 | }
791 |
792 | app.receivedData = function(data) {
793 | var data = new Uint8Array(data);
794 | console.log('RECV:' + toHexString(data));
795 |
796 | if (data[0] === 0x58) {
797 | console.log("EEP!");
798 | } else if (data[0] === 0x21) {
799 | console.log("question?!", this);
800 | if (data[1] === 0x44) {
801 | // Got dimensions result. "this" is the sending device.
802 | this.width = data[2];
803 | this.height = data[3];
804 | this.numLeds = data[4];
805 | this.maxFrames = data[5];
806 | this.maxText = data[6];
807 | listDevices();
808 | }
809 | }
810 | };
811 |
812 |
--------------------------------------------------------------------------------
/cordova/www/libs/evothings/ui/fastclick.js:
--------------------------------------------------------------------------------
1 | ;(function () {
2 | 'use strict';
3 |
4 | /**
5 | * @preserve FastClick: polyfill to remove click delays on browsers with touch UIs.
6 | *
7 | * @codingstandard ftlabs-jsv2
8 | * @copyright The Financial Times Limited [All Rights Reserved]
9 | * @license MIT License (see LICENSE.txt)
10 | */
11 |
12 | /*jslint browser:true, node:true*/
13 | /*global define, Event, Node*/
14 |
15 |
16 | /**
17 | * Instantiate fast-clicking listeners on the specified layer.
18 | *
19 | * @constructor
20 | * @param {Element} layer The layer to listen on
21 | * @param {Object} [options={}] The options to override the defaults
22 | */
23 | function FastClick(layer, options) {
24 | var oldOnClick;
25 |
26 | options = options || {};
27 |
28 | /**
29 | * Whether a click is currently being tracked.
30 | *
31 | * @type boolean
32 | */
33 | this.trackingClick = false;
34 |
35 |
36 | /**
37 | * Timestamp for when click tracking started.
38 | *
39 | * @type number
40 | */
41 | this.trackingClickStart = 0;
42 |
43 |
44 | /**
45 | * The element being tracked for a click.
46 | *
47 | * @type EventTarget
48 | */
49 | this.targetElement = null;
50 |
51 |
52 | /**
53 | * X-coordinate of touch start event.
54 | *
55 | * @type number
56 | */
57 | this.touchStartX = 0;
58 |
59 |
60 | /**
61 | * Y-coordinate of touch start event.
62 | *
63 | * @type number
64 | */
65 | this.touchStartY = 0;
66 |
67 |
68 | /**
69 | * ID of the last touch, retrieved from Touch.identifier.
70 | *
71 | * @type number
72 | */
73 | this.lastTouchIdentifier = 0;
74 |
75 |
76 | /**
77 | * Touchmove boundary, beyond which a click will be cancelled.
78 | *
79 | * @type number
80 | */
81 | this.touchBoundary = options.touchBoundary || 10;
82 |
83 |
84 | /**
85 | * The FastClick layer.
86 | *
87 | * @type Element
88 | */
89 | this.layer = layer;
90 |
91 | /**
92 | * The minimum time between tap(touchstart and touchend) events
93 | *
94 | * @type number
95 | */
96 | this.tapDelay = options.tapDelay || 200;
97 |
98 | /**
99 | * The maximum time for a tap
100 | *
101 | * @type number
102 | */
103 | this.tapTimeout = options.tapTimeout || 700;
104 |
105 | if (FastClick.notNeeded(layer)) {
106 | return;
107 | }
108 |
109 | // Some old versions of Android don't have Function.prototype.bind
110 | function bind(method, context) {
111 | return function() { return method.apply(context, arguments); };
112 | }
113 |
114 |
115 | var methods = ['onMouse', 'onClick', 'onTouchStart', 'onTouchMove', 'onTouchEnd', 'onTouchCancel'];
116 | var context = this;
117 | for (var i = 0, l = methods.length; i < l; i++) {
118 | context[methods[i]] = bind(context[methods[i]], context);
119 | }
120 |
121 | // Set up event handlers as required
122 | if (deviceIsAndroid) {
123 | layer.addEventListener('mouseover', this.onMouse, true);
124 | layer.addEventListener('mousedown', this.onMouse, true);
125 | layer.addEventListener('mouseup', this.onMouse, true);
126 | }
127 |
128 | layer.addEventListener('click', this.onClick, true);
129 | layer.addEventListener('touchstart', this.onTouchStart, false);
130 | layer.addEventListener('touchmove', this.onTouchMove, false);
131 | layer.addEventListener('touchend', this.onTouchEnd, false);
132 | layer.addEventListener('touchcancel', this.onTouchCancel, false);
133 |
134 | // Hack is required for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
135 | // which is how FastClick normally stops click events bubbling to callbacks registered on the FastClick
136 | // layer when they are cancelled.
137 | if (!Event.prototype.stopImmediatePropagation) {
138 | layer.removeEventListener = function(type, callback, capture) {
139 | var rmv = Node.prototype.removeEventListener;
140 | if (type === 'click') {
141 | rmv.call(layer, type, callback.hijacked || callback, capture);
142 | } else {
143 | rmv.call(layer, type, callback, capture);
144 | }
145 | };
146 |
147 | layer.addEventListener = function(type, callback, capture) {
148 | var adv = Node.prototype.addEventListener;
149 | if (type === 'click') {
150 | adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {
151 | if (!event.propagationStopped) {
152 | callback(event);
153 | }
154 | }), capture);
155 | } else {
156 | adv.call(layer, type, callback, capture);
157 | }
158 | };
159 | }
160 |
161 | // If a handler is already declared in the element's onclick attribute, it will be fired before
162 | // FastClick's onClick handler. Fix this by pulling out the user-defined handler function and
163 | // adding it as listener.
164 | if (typeof layer.onclick === 'function') {
165 |
166 | // Android browser on at least 3.2 requires a new reference to the function in layer.onclick
167 | // - the old one won't work if passed to addEventListener directly.
168 | oldOnClick = layer.onclick;
169 | layer.addEventListener('click', function(event) {
170 | oldOnClick(event);
171 | }, false);
172 | layer.onclick = null;
173 | }
174 | }
175 |
176 | /**
177 | * Windows Phone 8.1 fakes user agent string to look like Android and iPhone.
178 | *
179 | * @type boolean
180 | */
181 | var deviceIsWindowsPhone = navigator.userAgent.indexOf("Windows Phone") >= 0;
182 |
183 | /**
184 | * Android requires exceptions.
185 | *
186 | * @type boolean
187 | */
188 | var deviceIsAndroid = navigator.userAgent.indexOf('Android') > 0 && !deviceIsWindowsPhone;
189 |
190 |
191 | /**
192 | * iOS requires exceptions.
193 | *
194 | * @type boolean
195 | */
196 | var deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent) && !deviceIsWindowsPhone;
197 |
198 |
199 | /**
200 | * iOS 4 requires an exception for select elements.
201 | *
202 | * @type boolean
203 | */
204 | var deviceIsIOS4 = deviceIsIOS && (/OS 4_\d(_\d)?/).test(navigator.userAgent);
205 |
206 |
207 | /**
208 | * iOS 6.0-7.* requires the target element to be manually derived
209 | *
210 | * @type boolean
211 | */
212 | var deviceIsIOSWithBadTarget = deviceIsIOS && (/OS [6-7]_\d/).test(navigator.userAgent);
213 |
214 | /**
215 | * BlackBerry requires exceptions.
216 | *
217 | * @type boolean
218 | */
219 | var deviceIsBlackBerry10 = navigator.userAgent.indexOf('BB10') > 0;
220 |
221 | /**
222 | * Determine whether a given element requires a native click.
223 | *
224 | * @param {EventTarget|Element} target Target DOM element
225 | * @returns {boolean} Returns true if the element needs a native click
226 | */
227 | FastClick.prototype.needsClick = function(target) {
228 | switch (target.nodeName.toLowerCase()) {
229 |
230 | // Don't send a synthetic click to disabled inputs (issue #62)
231 | case 'button':
232 | case 'select':
233 | case 'textarea':
234 | if (target.disabled) {
235 | return true;
236 | }
237 |
238 | break;
239 | case 'input':
240 |
241 | // File inputs need real clicks on iOS 6 due to a browser bug (issue #68)
242 | if ((deviceIsIOS && target.type === 'file') || target.disabled) {
243 | return true;
244 | }
245 |
246 | break;
247 | case 'label':
248 | case 'iframe': // iOS8 homescreen apps can prevent events bubbling into frames
249 | case 'video':
250 | return true;
251 | }
252 |
253 | return (/\bneedsclick\b/).test(target.className);
254 | };
255 |
256 |
257 | /**
258 | * Determine whether a given element requires a call to focus to simulate click into element.
259 | *
260 | * @param {EventTarget|Element} target Target DOM element
261 | * @returns {boolean} Returns true if the element requires a call to focus to simulate native click.
262 | */
263 | FastClick.prototype.needsFocus = function(target) {
264 | switch (target.nodeName.toLowerCase()) {
265 | case 'textarea':
266 | return true;
267 | case 'select':
268 | return !deviceIsAndroid;
269 | case 'input':
270 | switch (target.type) {
271 | case 'button':
272 | case 'checkbox':
273 | case 'file':
274 | case 'image':
275 | case 'radio':
276 | case 'submit':
277 | return false;
278 | }
279 |
280 | // No point in attempting to focus disabled inputs
281 | return !target.disabled && !target.readOnly;
282 | default:
283 | return (/\bneedsfocus\b/).test(target.className);
284 | }
285 | };
286 |
287 |
288 | /**
289 | * Send a click event to the specified element.
290 | *
291 | * @param {EventTarget|Element} targetElement
292 | * @param {Event} event
293 | */
294 | FastClick.prototype.sendClick = function(targetElement, event) {
295 | var clickEvent, touch;
296 |
297 | // On some Android devices activeElement needs to be blurred otherwise the synthetic click will have no effect (#24)
298 | if (document.activeElement && document.activeElement !== targetElement) {
299 | document.activeElement.blur();
300 | }
301 |
302 | touch = event.changedTouches[0];
303 |
304 | // Synthesise a click event, with an extra attribute so it can be tracked
305 | clickEvent = document.createEvent('MouseEvents');
306 | clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
307 | clickEvent.forwardedTouchEvent = true;
308 | targetElement.dispatchEvent(clickEvent);
309 | };
310 |
311 | FastClick.prototype.determineEventType = function(targetElement) {
312 |
313 | //Issue #159: Android Chrome Select Box does not open with a synthetic click event
314 | if (deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select') {
315 | return 'mousedown';
316 | }
317 |
318 | return 'click';
319 | };
320 |
321 |
322 | /**
323 | * @param {EventTarget|Element} targetElement
324 | */
325 | FastClick.prototype.focus = function(targetElement) {
326 | var length;
327 |
328 | // Issue #160: on iOS 7, some input elements (e.g. date datetime month) throw a vague TypeError on setSelectionRange. These elements don't have an integer value for the selectionStart and selectionEnd properties, but unfortunately that can't be used for detection because accessing the properties also throws a TypeError. Just check the type instead. Filed as Apple bug #15122724.
329 | if (deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time' && targetElement.type !== 'month') {
330 | length = targetElement.value.length;
331 | targetElement.setSelectionRange(length, length);
332 | } else {
333 | targetElement.focus();
334 | }
335 | };
336 |
337 |
338 | /**
339 | * Check whether the given target element is a child of a scrollable layer and if so, set a flag on it.
340 | *
341 | * @param {EventTarget|Element} targetElement
342 | */
343 | FastClick.prototype.updateScrollParent = function(targetElement) {
344 | var scrollParent, parentElement;
345 |
346 | scrollParent = targetElement.fastClickScrollParent;
347 |
348 | // Attempt to discover whether the target element is contained within a scrollable layer. Re-check if the
349 | // target element was moved to another parent.
350 | if (!scrollParent || !scrollParent.contains(targetElement)) {
351 | parentElement = targetElement;
352 | do {
353 | if (parentElement.scrollHeight > parentElement.offsetHeight) {
354 | scrollParent = parentElement;
355 | targetElement.fastClickScrollParent = parentElement;
356 | break;
357 | }
358 |
359 | parentElement = parentElement.parentElement;
360 | } while (parentElement);
361 | }
362 |
363 | // Always update the scroll top tracker if possible.
364 | if (scrollParent) {
365 | scrollParent.fastClickLastScrollTop = scrollParent.scrollTop;
366 | }
367 | };
368 |
369 |
370 | /**
371 | * @param {EventTarget} targetElement
372 | * @returns {Element|EventTarget}
373 | */
374 | FastClick.prototype.getTargetElementFromEventTarget = function(eventTarget) {
375 |
376 | // On some older browsers (notably Safari on iOS 4.1 - see issue #56) the event target may be a text node.
377 | if (eventTarget.nodeType === Node.TEXT_NODE) {
378 | return eventTarget.parentNode;
379 | }
380 |
381 | return eventTarget;
382 | };
383 |
384 |
385 | /**
386 | * On touch start, record the position and scroll offset.
387 | *
388 | * @param {Event} event
389 | * @returns {boolean}
390 | */
391 | FastClick.prototype.onTouchStart = function(event) {
392 | var targetElement, touch, selection;
393 |
394 | // Ignore multiple touches, otherwise pinch-to-zoom is prevented if both fingers are on the FastClick element (issue #111).
395 | if (event.targetTouches.length > 1) {
396 | return true;
397 | }
398 |
399 | targetElement = this.getTargetElementFromEventTarget(event.target);
400 | touch = event.targetTouches[0];
401 |
402 | if (deviceIsIOS) {
403 |
404 | // Only trusted events will deselect text on iOS (issue #49)
405 | selection = window.getSelection();
406 | if (selection.rangeCount && !selection.isCollapsed) {
407 | return true;
408 | }
409 |
410 | if (!deviceIsIOS4) {
411 |
412 | // Weird things happen on iOS when an alert or confirm dialog is opened from a click event callback (issue #23):
413 | // when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched
414 | // with the same identifier as the touch event that previously triggered the click that triggered the alert.
415 | // Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an
416 | // immediately preceeding touch event (issue #52), so this fix is unavailable on that platform.
417 | // Issue 120: touch.identifier is 0 when Chrome dev tools 'Emulate touch events' is set with an iOS device UA string,
418 | // which causes all touch events to be ignored. As this block only applies to iOS, and iOS identifiers are always long,
419 | // random integers, it's safe to to continue if the identifier is 0 here.
420 | if (touch.identifier && touch.identifier === this.lastTouchIdentifier) {
421 | event.preventDefault();
422 | return false;
423 | }
424 |
425 | this.lastTouchIdentifier = touch.identifier;
426 |
427 | // If the target element is a child of a scrollable layer (using -webkit-overflow-scrolling: touch) and:
428 | // 1) the user does a fling scroll on the scrollable layer
429 | // 2) the user stops the fling scroll with another tap
430 | // then the event.target of the last 'touchend' event will be the element that was under the user's finger
431 | // when the fling scroll was started, causing FastClick to send a click event to that layer - unless a check
432 | // is made to ensure that a parent layer was not scrolled before sending a synthetic click (issue #42).
433 | this.updateScrollParent(targetElement);
434 | }
435 | }
436 |
437 | this.trackingClick = true;
438 | this.trackingClickStart = event.timeStamp;
439 | this.targetElement = targetElement;
440 |
441 | this.touchStartX = touch.pageX;
442 | this.touchStartY = touch.pageY;
443 |
444 | // Prevent phantom clicks on fast double-tap (issue #36)
445 | if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
446 | event.preventDefault();
447 | }
448 |
449 | return true;
450 | };
451 |
452 |
453 | /**
454 | * Based on a touchmove event object, check whether the touch has moved past a boundary since it started.
455 | *
456 | * @param {Event} event
457 | * @returns {boolean}
458 | */
459 | FastClick.prototype.touchHasMoved = function(event) {
460 | var touch = event.changedTouches[0], boundary = this.touchBoundary;
461 |
462 | if (Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary) {
463 | return true;
464 | }
465 |
466 | return false;
467 | };
468 |
469 |
470 | /**
471 | * Update the last position.
472 | *
473 | * @param {Event} event
474 | * @returns {boolean}
475 | */
476 | FastClick.prototype.onTouchMove = function(event) {
477 | if (!this.trackingClick) {
478 | return true;
479 | }
480 |
481 | // If the touch has moved, cancel the click tracking
482 | if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) {
483 | this.trackingClick = false;
484 | this.targetElement = null;
485 | }
486 |
487 | return true;
488 | };
489 |
490 |
491 | /**
492 | * Attempt to find the labelled control for the given label element.
493 | *
494 | * @param {EventTarget|HTMLLabelElement} labelElement
495 | * @returns {Element|null}
496 | */
497 | FastClick.prototype.findControl = function(labelElement) {
498 |
499 | // Fast path for newer browsers supporting the HTML5 control attribute
500 | if (labelElement.control !== undefined) {
501 | return labelElement.control;
502 | }
503 |
504 | // All browsers under test that support touch events also support the HTML5 htmlFor attribute
505 | if (labelElement.htmlFor) {
506 | return document.getElementById(labelElement.htmlFor);
507 | }
508 |
509 | // If no for attribute exists, attempt to retrieve the first labellable descendant element
510 | // the list of which is defined here: http://www.w3.org/TR/html5/forms.html#category-label
511 | return labelElement.querySelector('button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea');
512 | };
513 |
514 |
515 | /**
516 | * On touch end, determine whether to send a click event at once.
517 | *
518 | * @param {Event} event
519 | * @returns {boolean}
520 | */
521 | FastClick.prototype.onTouchEnd = function(event) {
522 | var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;
523 |
524 | if (!this.trackingClick) {
525 | return true;
526 | }
527 |
528 | // Prevent phantom clicks on fast double-tap (issue #36)
529 | if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
530 | this.cancelNextClick = true;
531 | return true;
532 | }
533 |
534 | if ((event.timeStamp - this.trackingClickStart) > this.tapTimeout) {
535 | return true;
536 | }
537 |
538 | // Reset to prevent wrong click cancel on input (issue #156).
539 | this.cancelNextClick = false;
540 |
541 | this.lastClickTime = event.timeStamp;
542 |
543 | trackingClickStart = this.trackingClickStart;
544 | this.trackingClick = false;
545 | this.trackingClickStart = 0;
546 |
547 | // On some iOS devices, the targetElement supplied with the event is invalid if the layer
548 | // is performing a transition or scroll, and has to be re-detected manually. Note that
549 | // for this to function correctly, it must be called *after* the event target is checked!
550 | // See issue #57; also filed as rdar://13048589 .
551 | if (deviceIsIOSWithBadTarget) {
552 | touch = event.changedTouches[0];
553 |
554 | // In certain cases arguments of elementFromPoint can be negative, so prevent setting targetElement to null
555 | targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement;
556 | targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent;
557 | }
558 |
559 | targetTagName = targetElement.tagName.toLowerCase();
560 | if (targetTagName === 'label') {
561 | forElement = this.findControl(targetElement);
562 | if (forElement) {
563 | this.focus(targetElement);
564 | if (deviceIsAndroid) {
565 | return false;
566 | }
567 |
568 | targetElement = forElement;
569 | }
570 | } else if (this.needsFocus(targetElement)) {
571 |
572 | // Case 1: If the touch started a while ago (best guess is 100ms based on tests for issue #36) then focus will be triggered anyway. Return early and unset the target element reference so that the subsequent click will be allowed through.
573 | // Case 2: Without this exception for input elements tapped when the document is contained in an iframe, then any inputted text won't be visible even though the value attribute is updated as the user types (issue #37).
574 | if ((event.timeStamp - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) {
575 | this.targetElement = null;
576 | return false;
577 | }
578 |
579 | this.focus(targetElement);
580 | this.sendClick(targetElement, event);
581 |
582 | // Select elements need the event to go through on iOS 4, otherwise the selector menu won't open.
583 | // Also this breaks opening selects when VoiceOver is active on iOS6, iOS7 (and possibly others)
584 | if (!deviceIsIOS || targetTagName !== 'select') {
585 | this.targetElement = null;
586 | event.preventDefault();
587 | }
588 |
589 | return false;
590 | }
591 |
592 | if (deviceIsIOS && !deviceIsIOS4) {
593 |
594 | // Don't send a synthetic click event if the target element is contained within a parent layer that was scrolled
595 | // and this tap is being used to stop the scrolling (usually initiated by a fling - issue #42).
596 | scrollParent = targetElement.fastClickScrollParent;
597 | if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) {
598 | return true;
599 | }
600 | }
601 |
602 | // Prevent the actual click from going though - unless the target node is marked as requiring
603 | // real clicks or if it is in the whitelist in which case only non-programmatic clicks are permitted.
604 | if (!this.needsClick(targetElement)) {
605 | event.preventDefault();
606 | this.sendClick(targetElement, event);
607 | }
608 |
609 | return false;
610 | };
611 |
612 |
613 | /**
614 | * On touch cancel, stop tracking the click.
615 | *
616 | * @returns {void}
617 | */
618 | FastClick.prototype.onTouchCancel = function() {
619 | this.trackingClick = false;
620 | this.targetElement = null;
621 | };
622 |
623 |
624 | /**
625 | * Determine mouse events which should be permitted.
626 | *
627 | * @param {Event} event
628 | * @returns {boolean}
629 | */
630 | FastClick.prototype.onMouse = function(event) {
631 |
632 | // If a target element was never set (because a touch event was never fired) allow the event
633 | if (!this.targetElement) {
634 | return true;
635 | }
636 |
637 | if (event.forwardedTouchEvent) {
638 | return true;
639 | }
640 |
641 | // Programmatically generated events targeting a specific element should be permitted
642 | if (!event.cancelable) {
643 | return true;
644 | }
645 |
646 | // Derive and check the target element to see whether the mouse event needs to be permitted;
647 | // unless explicitly enabled, prevent non-touch click events from triggering actions,
648 | // to prevent ghost/doubleclicks.
649 | if (!this.needsClick(this.targetElement) || this.cancelNextClick) {
650 |
651 | // Prevent any user-added listeners declared on FastClick element from being fired.
652 | if (event.stopImmediatePropagation) {
653 | event.stopImmediatePropagation();
654 | } else {
655 |
656 | // Part of the hack for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
657 | event.propagationStopped = true;
658 | }
659 |
660 | // Cancel the event
661 | event.stopPropagation();
662 | event.preventDefault();
663 |
664 | return false;
665 | }
666 |
667 | // If the mouse event is permitted, return true for the action to go through.
668 | return true;
669 | };
670 |
671 |
672 | /**
673 | * On actual clicks, determine whether this is a touch-generated click, a click action occurring
674 | * naturally after a delay after a touch (which needs to be cancelled to avoid duplication), or
675 | * an actual click which should be permitted.
676 | *
677 | * @param {Event} event
678 | * @returns {boolean}
679 | */
680 | FastClick.prototype.onClick = function(event) {
681 | var permitted;
682 |
683 | // It's possible for another FastClick-like library delivered with third-party code to fire a click event before FastClick does (issue #44). In that case, set the click-tracking flag back to false and return early. This will cause onTouchEnd to return early.
684 | if (this.trackingClick) {
685 | this.targetElement = null;
686 | this.trackingClick = false;
687 | return true;
688 | }
689 |
690 | // Very odd behaviour on iOS (issue #18): if a submit element is present inside a form and the user hits enter in the iOS simulator or clicks the Go button on the pop-up OS keyboard the a kind of 'fake' click event will be triggered with the submit-type input element as the target.
691 | if (event.target.type === 'submit' && event.detail === 0) {
692 | return true;
693 | }
694 |
695 | permitted = this.onMouse(event);
696 |
697 | // Only unset targetElement if the click is not permitted. This will ensure that the check for !targetElement in onMouse fails and the browser's click doesn't go through.
698 | if (!permitted) {
699 | this.targetElement = null;
700 | }
701 |
702 | // If clicks are permitted, return true for the action to go through.
703 | return permitted;
704 | };
705 |
706 |
707 | /**
708 | * Remove all FastClick's event listeners.
709 | *
710 | * @returns {void}
711 | */
712 | FastClick.prototype.destroy = function() {
713 | var layer = this.layer;
714 |
715 | if (deviceIsAndroid) {
716 | layer.removeEventListener('mouseover', this.onMouse, true);
717 | layer.removeEventListener('mousedown', this.onMouse, true);
718 | layer.removeEventListener('mouseup', this.onMouse, true);
719 | }
720 |
721 | layer.removeEventListener('click', this.onClick, true);
722 | layer.removeEventListener('touchstart', this.onTouchStart, false);
723 | layer.removeEventListener('touchmove', this.onTouchMove, false);
724 | layer.removeEventListener('touchend', this.onTouchEnd, false);
725 | layer.removeEventListener('touchcancel', this.onTouchCancel, false);
726 | };
727 |
728 |
729 | /**
730 | * Check whether FastClick is needed.
731 | *
732 | * @param {Element} layer The layer to listen on
733 | */
734 | FastClick.notNeeded = function(layer) {
735 | var metaViewport;
736 | var chromeVersion;
737 | var blackberryVersion;
738 | var firefoxVersion;
739 |
740 | // Devices that don't support touch don't need FastClick
741 | if (typeof window.ontouchstart === 'undefined') {
742 | return true;
743 | }
744 |
745 | // Chrome version - zero for other browsers
746 | chromeVersion = +(/Chrome\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];
747 |
748 | if (chromeVersion) {
749 |
750 | if (deviceIsAndroid) {
751 | metaViewport = document.querySelector('meta[name=viewport]');
752 |
753 | if (metaViewport) {
754 | // Chrome on Android with user-scalable="no" doesn't need FastClick (issue #89)
755 | if (metaViewport.content.indexOf('user-scalable=no') !== -1) {
756 | return true;
757 | }
758 | // Chrome 32 and above with width=device-width or less don't need FastClick
759 | if (chromeVersion > 31 && document.documentElement.scrollWidth <= window.outerWidth) {
760 | return true;
761 | }
762 | }
763 |
764 | // Chrome desktop doesn't need FastClick (issue #15)
765 | } else {
766 | return true;
767 | }
768 | }
769 |
770 | if (deviceIsBlackBerry10) {
771 | blackberryVersion = navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/);
772 |
773 | // BlackBerry 10.3+ does not require Fastclick library.
774 | // https://github.com/ftlabs/fastclick/issues/251
775 | if (blackberryVersion[1] >= 10 && blackberryVersion[2] >= 3) {
776 | metaViewport = document.querySelector('meta[name=viewport]');
777 |
778 | if (metaViewport) {
779 | // user-scalable=no eliminates click delay.
780 | if (metaViewport.content.indexOf('user-scalable=no') !== -1) {
781 | return true;
782 | }
783 | // width=device-width (or less than device-width) eliminates click delay.
784 | if (document.documentElement.scrollWidth <= window.outerWidth) {
785 | return true;
786 | }
787 | }
788 | }
789 | }
790 |
791 | // IE10 with -ms-touch-action: none or manipulation, which disables double-tap-to-zoom (issue #97)
792 | if (layer.style.msTouchAction === 'none' || layer.style.touchAction === 'manipulation') {
793 | return true;
794 | }
795 |
796 | // Firefox version - zero for other browsers
797 | firefoxVersion = +(/Firefox\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];
798 |
799 | if (firefoxVersion >= 27) {
800 | // Firefox 27+ does not have tap delay if the content is not zoomable - https://bugzilla.mozilla.org/show_bug.cgi?id=922896
801 |
802 | metaViewport = document.querySelector('meta[name=viewport]');
803 | if (metaViewport && (metaViewport.content.indexOf('user-scalable=no') !== -1 || document.documentElement.scrollWidth <= window.outerWidth)) {
804 | return true;
805 | }
806 | }
807 |
808 | // IE11: prefixed -ms-touch-action is no longer supported and it's recomended to use non-prefixed version
809 | // http://msdn.microsoft.com/en-us/library/windows/apps/Hh767313.aspx
810 | if (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') {
811 | return true;
812 | }
813 |
814 | return false;
815 | };
816 |
817 |
818 | /**
819 | * Factory method for creating a FastClick object
820 | *
821 | * @param {Element} layer The layer to listen on
822 | * @param {Object} [options={}] The options to override the defaults
823 | */
824 | FastClick.attach = function(layer, options) {
825 | return new FastClick(layer, options);
826 | };
827 |
828 |
829 | if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
830 |
831 | // AMD. Register as an anonymous module.
832 | define(function() {
833 | return FastClick;
834 | });
835 | } else if (typeof module !== 'undefined' && module.exports) {
836 | module.exports = FastClick.attach;
837 | module.exports.FastClick = FastClick;
838 | } else {
839 | window.FastClick = FastClick;
840 | }
841 | }());
842 |
--------------------------------------------------------------------------------