diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000000..298149bb09 --- /dev/null +++ b/.babelrc @@ -0,0 +1,7 @@ +{ + "presets": [ + "electron", + "react" + ], + "sourceMaps": "inline" +} diff --git a/.coffeelint.json b/.coffeelint.json new file mode 100644 index 0000000000..704978599a --- /dev/null +++ b/.coffeelint.json @@ -0,0 +1,106 @@ +{ + "arrow_spacing": { + "level": "ignore" + }, + "camel_case_classes": { + "level": "error" + }, + "coffeescript_error": { + "level": "error" + }, + "colon_assignment_spacing": { + "level": "ignore", + "spacing": { + "left": 0, + "right": 0 + } + }, + "cyclomatic_complexity": { + "value": 10, + "level": "ignore" + }, + "duplicate_key": { + "level": "error" + }, + "empty_constructor_needs_parens": { + "level": "ignore" + }, + "indentation": { + "value": 2, + "level": "error" + }, + "max_line_length": { + "value": 140, + "level": "error", + "limitComments": true + }, + "missing_fat_arrows": { + "level": "ignore" + }, + "newlines_after_classes": { + "value": 3, + "level": "ignore" + }, + "no_backticks": { + "level": "error" + }, + "no_debugger": { + "level": "warn" + }, + "no_empty_functions": { + "level": "ignore" + }, + "no_empty_param_list": { + "level": "ignore" + }, + "no_implicit_braces": { + "level": "ignore", + "strict": true + }, + "no_implicit_parens": { + "strict": true, + "level": "ignore" + }, + "no_interpolation_in_single_quotes": { + "level": "ignore" + }, + "no_plusplus": { + "level": "ignore" + }, + "no_stand_alone_at": { + "level": "ignore" + }, + "no_tabs": { + "level": "error" + }, + "no_throwing_strings": { + "level": "error" + }, + "no_trailing_semicolons": { + "level": "error" + }, + "no_trailing_whitespace": { + "level": "error", + "allowed_in_comments": false, + "allowed_in_empty_lines": true + }, + "no_unnecessary_double_quotes": { + "level": "ignore" + }, + "no_unnecessary_fat_arrows": { + "level": "warn" + }, + "non_empty_constructor_needs_parens": { + "level": "ignore" + }, + "prefer_english_operator": { + "level": "ignore", + "doubleNotLevel": "ignore" + }, + "space_operators": { + "level": "ignore" + }, + "spacing_after_comma": { + "level": "ignore" + } +} diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index d80f4ce50b..0000000000 --- a/.dockerignore +++ /dev/null @@ -1,6 +0,0 @@ -.git -.gitignore -README.md -Procfile* -*node_modules* -docs diff --git a/.env b/.env deleted file mode 100644 index 431b7e615a..0000000000 --- a/.env +++ /dev/null @@ -1,5 +0,0 @@ -DB_ENCRYPTION_ALGORITHM='aes-256-ctr' -DB_ENCRYPTION_PASSWORD='d6F3Efeq' -GMAIL_CLIENT_ID='271342407743-nibas08fua1itr1utq9qjladbkv3esdm.apps.googleusercontent.com' -GMAIL_CLIENT_SECRET='WhmxErj-ei6vJXLocNhBbfBF' -GMAIL_REDIRECT_URL='http://localhost:5100/auth/gmail/oauthcallback' diff --git a/.eslintrc b/.eslintrc index f3d5060407..cc2ae87600 100644 --- a/.eslintrc +++ b/.eslintrc @@ -43,7 +43,7 @@ "semi": "off", "no-mixed-operators": "off", "import/extensions": ["error", "never", { "json": "always" }], - "import/no-unresolved": ["error", {"ignore": ["nylas-exports", "nylas-component-kit", "electron", "nylas-store", "react-dom/server", "nylas-observables", "windows-shortcuts", "moment-round", "better-sqlite3", "chrono-node", "event-kit", "enzyme"]}], + "import/no-unresolved": ["error", {"ignore": ["nylas-exports", "nylas-component-kit", "electron", "nylas-store", "react-dom/server", "nylas-observables", "windows-shortcuts", "moment-round", "better-sqlite3", "chrono-node", "event-kit", "enzyme", "isomorphic-core"]}], "import/no-extraneous-dependencies": "off", "import/newline-after-import": "off", "import/prefer-default-export": "off", diff --git a/.gitignore b/.gitignore index e5ddc09e25..211f836467 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,75 @@ -.DS_Store +##### Elastic Beanstalk Files +.elasticbeanstalk/* +!.elasticbeanstalk/*.cfg.yml +!.elasticbeanstalk/*.global.yml + +##### Arcanist / Phab +**/.arcconfig +**/.arclint +**/arclib + +##### Client Private Plugins (symlinked) +**/client-app/src/error-logger-extensions +**/client-app/src/nylas-private-* +**/internal_packages/nylas-private-* +**/internal_packages/activity-list +**/internal_packages/composer-mail-merge +**/internal_packages/composer-scheduler +**/internal_packages/link-tracking +**/internal_packages/open-tracking +**/internal_packages/send-later +**/internal_packages/send-reminders +**/internal_packages/thread-sharing +**/internal_packages/client-sync + +##### Node modules node_modules -dump.rdb -*npm-debug.log -storage/ -lerna-debug.log -newrelic_agent.log +!packages/client-app/spec/fixtures/packages/package-with-incompatible-native-module/node_modules +**/npm-debug.log* +**/lerna-debug.log* + +##### Certs for building +**/build/resources/certs + +##### Misc swap files +**/*.swp +**/*.swo +**/*~ +**/*# +**/.DS_Store +**/Thumbs.db +**/#emacs + +# Built cloud files +n1_cloud_dist + +# Built Nylas Mail client +packages/client-app/dist + +# Tests +spec-saved-state.json + +# Symlinked Jasmine config files +**/jasmine/config.json +!packages/isomorphic-core/spec/jasmine/config.json + +# Symlinked isomorphic-core Specs +packages/client-app/spec/isomorphic-core # Elastic Beanstalk Files .elasticbeanstalk/* !.elasticbeanstalk/*.cfg.yml !.elasticbeanstalk/*.global.yml + +# Sqlite amalgamation for scripts +scripts/sqlite + +# Scripts for calculating statistics +scripts/toolbox +scripts/venv + +# Python +*.pyc + +# OAuth client secret for talking to Google Sheets +client_secret.json diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..cf1d9a97d8 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,59 @@ +# The private Nylas monorepo build script. This will build a full signed +# release for the Nylas Mail client +sudo: false + +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - build-essential + - clang + - fakeroot + - g++-4.8 + - git + - libgnome-keyring-dev + - xvfb + - rpm + - libxext-dev + - libxtst-dev + - libxkbfile-dev + +branches: + only: + - master + - /ci-.*/ + - /stable.*/ + +matrix: + include: + - os: linux + env: NODE_VERSION=6.9 CC=gcc-4.8 CXX=g++-4.8 DEBUG="electron-packager:*" INSTALL_TARGET=client + - os: osx + env: NODE_VERSION=6.9 CC=clang CXX=clang++ SIGN_BUILD=true DEBUG="electron-packager:*" INSTALL_TARGET=client + +before_install: +- openssl aes-256-cbc + -K $encrypted_faf2708e46e2_key + -iv $encrypted_faf2708e46e2_iv + -in packages/client-private-plugins/encrypted_certificates/travis/travis-files.tar.enc + -out packages/client-private-plugins/encrypted_certificates/travis/travis-files.tar + -d; +- mkdir packages/client-app/build/resources/certs; +- tar xvf packages/client-private-plugins/encrypted_certificates/travis/travis-files.tar + --directory=packages/client-app/build/resources/; +- source packages/client-app/build/resources/certs/mac/set_unix_env.sh; + +install: +- git clone https://github.com/creationix/nvm.git /tmp/.nvm +- source /tmp/.nvm/nvm.sh +- nvm install $NODE_VERSION +- nvm use --delete-prefix $NODE_VERSION + +script: +- npm install && npm run build-client && npm run upload-client + +cache: + directories: + - node_modules + - apm/node_modules diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..f671682104 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,760 @@ +# Nylas Mail Changelog + +### 2.0.15 (4/17/2017) + + + Correctly handle and inform users about database malformed errors that can + occur both in main process and/or window processes + +### 2.0.14 (4/14/2017) + +- Fixes: + + Prevent from adding duplicate accounts and sync workers due to account id changes + + Correctly remove sync worker reference when destroying it + + Correctly initialize SyncProcessManager with Identity + + Fix contact ranking runtime error + +### 2.0.13 (4/13/2017) + +- Fixes: + + Upload nupkg with correct name for win32 autoupdater to work + + Correctly handle window.unhandledrejection events + +### 2.0.12 (4/13/2017) + +- Fixes: + + Prevent NM from overwriting N1 binary on windows + + Fix runtime error in sync process + + Prevent old N1 config from getting wiped when installing Nylas Mail + +- Development: + + Remove useless docs + +### 2.0.11 (4/12/2017) + +- Fixes: + + Dispose of mail listener connection before getting new one. This will + prevent sync process from leaking Imap connections and getting stuck. + + Fix performance regression when polling for gmail attribute changes + + Don't double report unhandled rejections + + Fix unhandled rejection handling (fix ipc parse error) + + Fix regression when processing messages under a transaction + + Rate limit database malformed error reports to sentry + +### 2.0.10 (4/11/2017) + +- Fixes: + + Fix missing UID error when archiving threads after sending + + Ensure all mail folder exists before trying to access it + + Fix SyncbackMetadataTask dependency + +- Development: + + Don't report stuck sync processes to Sentry + + MessageFactory -> MessageUtils, SendUtils -> ModelUtils + +### 2.0.9 (4/11/2017) + +- Features: + + Re-add imap to the onboarding accounts page + +- Fixes: + + Correctly detect changes in labels, starred and unread for Gmail accounts + + Fix delta streaming connection retries + + Handle weird MIME edge case with @ symbol + +- Performance: + + Wrap message processing in transaction for better performance + + Increase sqlite `page_size` and `cache_size` + +- Cloud: + + Improve performance of reminders worker + + Add DataDog StatsD for heartbeats + + Restart automatically on unhandeld rejections + +- Development: + + Add benchmark mode + +### 2.0.8 (4/7/2017) + +- Fixes: + + Revamp SSL options during authentication to be able to properly auth against + SMTP and prevent sending failures + + Ensure IMAPConnnectionPool uses updated account credentials + + Always fetch and update identity regardless of environment + + Properly handle serialization errors for JSON columns in database + +- Cloud: + + Switch MySQL charset to utf8mb4 + + Add exponential backoff for cloud worker jobs when encountering errors + + Use IMAP connection pool in cloud workers to limit number of connections + + Properly generate metadata deltas when clearing expiration field + + Increment default imap connection socket timeout in cloud workers + +- Plugins: + + Correctly syncback metadata for send later + + Delete drafts after they are sent later + + Correctly ensure messages in sent folder for send later in gmail + + Fix send reminders version conflict error + + Correctly set metadata values for send reminders + + Fix imap folder names in send-reminders + + Fix send later access token refresh + +- Development: + + Add view of CloudJobs in n1.nylas.com/admin + + Ensure daily script grabs current version after pulling latest changes + +### 2.0.1 (4/5/2017) + +- Features: + + Limit search to focused perspective + +- Fixes: + + IMAPConnectionPool now correctly disposes connections + + Ensure we use refreshed access token for all imap connections during sync + + Prevent IMAP connection leaking in sync worker + + Fix send later button saving state and sending action + + Fix inline images for send later + + Correctly enable plugins on 2.0.1 + + Make sure app can update even after signing out of NylasID + + Don't make any requests when NylasID isn't present + +- Cloud: + + Make cloud workers more robust + + Remove old SignalFX reporter & add docs + + Log errors according to bunyan specs + +- Development: + + Add script to run benchmarks once per day at specified time + + Add script to upload benchmark data to Google Sheets + + Add better logging when restarting stuck sync worker + +### 2.0.0 (4/4/2017) + +Introducing Nylas Mail Pro + +- Features: + + Enable snooze, send later, and send reminders + + Add feature limits to reminders and send later + +- Fixes + + Don't assign duplicate folder roles + + Re-setup IdentityStore in new window + +- Development: + + Fix sqlite build for older versions of clang + + Remove rogue scripts-tmp folder + + Remove unecessary db setup for mail rules + +### 1.0.55 (3/31/2017) + +- Fixes + + Ensure open/link tracking work when sending multiple consecutive emails + + Fix performance of contact rankings database query + + Fix performance of thread search index database queries + + Fix performance of ANALYZE queries + +### 1.0.54 (3/31/2017) + +- Features: + + Add search support for `has:attachment` + +- Fixes: + + Reduce database thrashing caused by thread search indexing + + Interrupt long-running syncback tasks + + Fix performance of contact rankings db query + + Don't hit contact rankings endpoint until account is ready + + Ensure sync worker is stopped correctly when removing accounts or when + restarting it + +- Metrics: + + Report metrics about SyncbackTask runs + +- Perf: + + Delay building new hot window to improve win perf + +- Development: + + Add script to benchmarks new commits + + Add DEBUG flag to be able to log all query activity for both databases + + Add `DatabaseStore.write` which doesn't use Transactions + + Metadata test fixes + +### 1.0.52 (3/29/2017) + +- Fixes: + + Fix open and link tracking: + + No longer triggers your own opens & link clicks + + Link tracking indicator is now always present in sent messages + + Fix regression in DB query execution which would delay all queries in the + system. + + Reduce max retry backoff for DB queries, which could hold a query open for + too long + + Fix thread reindexing issues, which should help performance and correctly + index threads for search + + Fix `in:` search syntax for non-gmail search + + Fix references to RetryableError imports + +- Development: + + Add initial sync benchmarking script + + Clean up logging in DatabaseStore: differentiate background queries from + regular queries in the logs, only log queries that actually take more than + 100ms. + + Point the billing server URL to staging by default for easier development, + and allow it to be overriden + + Add index to expiration field on Metadata + +### 1.0.51 (3/28/2017) + +- Features: + + Restore contact rankings feature for better contact predictions in composer + recipient fields + +- Fixes: + + Correctly listen for new mail in between sync loops + + Verify SMTP credentials in /auth endpoint + + Also prioritize sent label for initial Gmail sync + + Properly relaunch windows on autoupdate + + Properly set up local /health endpoint by making sure to attach route files + ending in .es6 to local-api + +- Perf: + + Don't throttle while syncing first 500 threads + +- Metrics: + + Report battery state changes to Mixpanel + +- Development: + + Make deploy-it say what it's doing instead of hanging silently + + Make deploy-it print link to the EB console + + Make help message better on deploy-it + + Add `SHOW_HOT_WINDOW` env for prod debugging of window launches + + Correctly ignore `node_modules` in .ebignore for faster deploys + + Only bootstrap specific pkgs in postinstall for faster npm installs + +### 1.0.50 (3/28/2017) + +- Fixes: + + Fix SyncActivity errors introduced in 1.0.49 + +### 1.0.49 (3/27/2017) + +- Fixes: + + Ensure sync process does not get stuck + + Ensure the worker window is always available + + Retry database operations when encountering locking issues + +- Metrics: + + Detect and report when the worker window is unavailable + + Detect and report when a sync process is stuck + +- Development: + + Windows autoupdater fixes + + Add better documentation for windows autoupdater + + Remap windows dev shortcuts to match the ones used on darwin and linux + + When building app, only re-install for optional dependencies on darwin + +- Cloud: + + Timeout streaming API connections every 15 minutes + + Add missing database indexes from SQL review + +### 1.0.48 (3/27/2017) + +- Fixes: + + Reindex threads when they're updated + + Don't try to restart sync on every IdentityStore change + + Correctly remove inline images with x button + +### 1.0.47 (3/23/2017) + +- Fixes: + + Report hard crashes using Electron's built-in crash reporter + +- Development: + + Don't handle IMAP timeouts in the connection pool + + Record file download times + +### 1.0.46 (3/22/2017) + +- Fixes: + + Ensure files get transferred in forwarded messages + + Correctly sign out of NylasID + + Don't report non-reportable errors in delta connection + + Fix S3 attachment upload for send later + +- Development: + + Rename downloadDataForFile(s) -> getDownloadDataForFile(s) + + Switch type of Metadata value column + + Fix build condition + + Fix DraftFactory specs + + Refactor sync worker IMAPConnectionPool callbacks + +### 1.0.45 (3/21/2017) + +- Fixes: + + Correctly report unhandled errors caught in window. + + Fix passing cursor to delta streams + +### 1.0.44 (3/20/2017) + +- Fixes: + + Add error handling when creating syncback requests + + Fix path for tmp dir in daily script + +### 1.0.43 (3/17/2017) + +- Fixes: + + + Revert nodemailer to previous version + + Creating a folder no longer creates a non-existent duplicate subfolder + + Don't bump threads to the top of list when a message is sent: only update lastReceivedDate if the message was actually received + +### 1.0.42 (3/16/2017) + +- Fixes: + + Fix spellchecker regression (Don't exclude source maps in build) + +### 1.0.41 (3/16/2017) + +- Development: + + Upgrade nodemailer to latest version + +### 1.0.40 (3/15/2017) + +- Features: + + Add support for attachments in send later + +- Development: + + Improve build time + + Windows Autoupdater fixes + +### 1.0.39 (3/14/2017) + +- Fixes: + + Fix missing depedency for imap-provider-settings + +- Development: + + Only upload 7 characters of the commit hash for Windows build + +### 1.0.38 (3/13/2017) + +- Fixes: + + Restart sync when computer awakes from sleep + + Fix issue that made users log out of NylasID, restart, and then force them to log out and restart again in a loop (#3325) + + Don't start sync or delta connections without an identity + +- Development: + + Restore windows build + + Remove specs from production build + + Fix arc lint + + Specify Content-Type in developer bar curl commands + +### 1.0.37 (3/10/2017) + +- Fixes: + + Fix regression introduced in 1.0.36 in the message processor + + Correctly show auth error when we can't connect to n1cloud + + Fix error thrown sometimes when handling send errors + +### 1.0.36 (3/10/2017) + +- Fixes: + + Increase the IMAP connection pool size + + Shim sequelize to timeout after 1 minute on every database operation. This + is a safeguard to prevent unresolved db promises from halting the sync loop. + + Better error handling to prevent the message processor from halting sync + +- Development: + + Measure and report inline composer open times + + Refactor MessageProcessor to be more robust to errors + +### 1.0.35 (3/9/2017) + +- Fixes: + + Make sure delta connection is restarted when an account is re-authed + + More defensive error handling to prevent sync from halting + + Prevent delta streaming connection from retrying too much + + Fix error when attempting to report a fetch id error + + Prevent error restart loop when database is malformed + + Correctly cancel search when the search perspective is cleared + + When many search results are returned from the server, don't try to sync them all at once, otherwise would slow down the main sync process. + + When restarting the app, don't try to continue syncing search results from an old search + +- Development: + + Consolidate delta connection stores, remove `internal_package/deltas` + + Rename NylasSyncStatusStore to FolderSyncProgressStore + + Consolidate APIError status code that we should not report + + Don't report incorrect username or password to Sentry + + Rate limit error reporting for message processing errors + + Fix circular reference error when reporting errors + + Refactor file download IMAPConnectionPool usage + + Don't focus the Console tab in dev tools every time an error is logged + + Correctly set process title + +### 1.0.34 (3/8/2017) + +- Fixes: + + Sync should not get stuck anymore due to sequelize + + Delta Streaming connections now correctly retry after they are closed or an error occurs + + Handle errors when opening imap box correctly + +- Development: + + Add script/daily + + Provide better info to Sentry on sending errors + + Refactor and clean up delta streaming code + + Refactor message processing throttling + +### 1.0.33 (3/8/2017) + +- Features: + + + Add intitial support for send later + +- Fixes: + + + Fetch unknown message uids returned in search results + + Don't throttle message processing when syncing specific UIDs + +- Development: + + + Better grouping for APIError by URL also + + Don't generate sourceMapCache in prod mode + + Upload a next-version to S3 for autoupdate testing + + Windows build fixes + +### 1.0.32 (3/7/2017) + +- Development: + + + Report provider when reporting remove-from-threads-from-list + + Report provider when reporting send perf metrics + +### 1.0.31 (3/6/2017) + +- Fixes: + + + Improve initial sync speed by scaling number of messages synced based on + folder SELECT duration + + Immediately restore sync process when app comes back online after being + disconnected from the internet. + + Can now reply from within notifications again + +- Development: + + + Add basic rate limiting to Sentry + + Report all search performance metrics + + Prevent noisy uncaught errors when closing long connection + + Improve reporting of refresh access token errors + + Don't double report refresh access token API errors + + Replace `setImmediate` with `setTimeout` as Promise scheduler + + Use new Bluebird preferred `longStackTraces` syntax + + NylasAPIRequest refactored and cleaned up + + Search refactors and improvements + + Protect from operating on IMAP connection while opening a box + + Enable logging in prod builds + + Make deploy-it support -h/--help + + Restore cloud testing environments + +### 1.0.30 (2/28/2017) + +- Fixes: + + + Can properly add signatures and select them as default for different + accounts. + + Can now correctly reply to a thread and immediately archive it or move it to + another folder without throwing an error (#3290) + + Correctly fix IMAP connection timeout issues (#3232) + + Nylas Mail no longer opens an increasing number of IMAP connections which + caused some users to reach IMAP server connection limits (#3228) + + Fix memory leak while syncing which caused sync process to restart + sometimes. + + Correctly handle IMAP connections ending unexpectedly + + Correctly detect retryable IMAP errors during sync + detect more + retryable errors + + Correctly catch more authentication errors when sending + + Improve speed of processing messages during sync + + Prevent unnecessary re-renders of the thread list + +- Development: + + + Report performance metrics + + More Coffeescript to Javascript conversions + +### 1.0.29 (2/21/2017) + +- Fixes: + + + You can now click inline images in messages to open them + + More IMAP errors have been identified as retryable, which means users will + see less errors when syncing an account + + Improve performance of thread search indexing queries + + Correctly catch Invalid Login errors when sending + +- Development: + + + Developer bar in Worker window now shows single delta connection + + More code converted to Javascript + +### 1.0.28 (2/16/2017) + +- Fixes: + + + Fix offline notification bug that caused api outage + + We now properly handle gmail auth token errors in the middle of the sync loop. This means less red boxes for users! + + Less battery usage when initial sync has completed! + + No more errors when saving sent messages to sent folders (`auth or accountId` errors) + + No more `Lingering tasks in progress marked as failed errors` + + Syncback tasks will continue retrying even after closing app + + Syncback tasks retry more aggressively + + Detect more offline errors when sending, sending is more reliable + + Imap connection pooling (yet to land) + + More retryable IMAP errors, means less red boxes for users + + Offline notification now shows itself when we’re actually offline, shows countdown for next reconnect attempt + +- Development: + + + More tests + + Don't use breadcrumbs in dev mode + + Add a better reason when waking sync for syncback in the logs + + BackoffScheduler, BatteryManager added for reusability + +### 1.0.27 (2/14/17) + +- Fixes: + + + Offline notification fixes + +### 1.0.26 (2/10/17) + +- Fixes: + + + Downloads retry if they fail + + NylasID doesn't intermittently log out or throw errors + + Fix initial sync for Inbox Zero Gmail accounts + +### 1.0.25 (2/10/17) + +- Fixes: + + + When replying to a thread, properly add it to the sent folder + +- Development: + + + Can now once again run Nylas Mail test suite + +### 1.0.24 (2/9/17) + +- Fixes: + + + Fix error reporter when reporting an error without an identity (this would + crash the app) + +- Development: + + + Fix logging inside local-sync api requests + + Stop reporting handled API errors to Sentry + + Report thread-list perf metrics + +### 1.0.23 (2/8/17) + +- Fixes: + + + Fix emails occasionally being sent with an incomplete body (#3269) + + Correctly thread messages together when open/link tracking is enabled + + Fix `Mailbox does not exist` error for iCloud users (#3253) + + When adding account, correctly remove whitespace from emails + + Fix link in update notification to point to latest changelog + +- Performance: + + + Thread list actions no longer sporadically lag for ~1sec (this is especially + noticeable when many accounts have been added) + + No longer slow down sync process when more than 100,000 threads have been synced + +- Development: + + + Better logging in worker window + + You can now run a development build of Nylas Mail alongside a production + build + +### 1.0.22 (2/7/17) + +- Fixes: + + + New mail notification sounds on startup are combined when multiple new messages have arrived + + You can now correctly select threads using `cmd` and `shift` + + Improve message fetching by making sure we always fetch the most recent + messages first. + + Improve IMAP connection timeouts by incrementing the socket timeout (#3232) + + When adding a Google account, make sure to show the Account Chooser + +- Development: + + + Nylas Identity is no longer stored in config.json + +### 1.0.21 (2/3/17) + +- Fixes: + + + Fixed an issue where Nylas Mail could delete all accounts (addresses #3231) + + Correctly delete and archive threads when they contain sent messages (addresses #2706) + + Improve performance and prevent crashes when running several sync actions + + Improve error handling when sync actions fail + + Fix JSON serialization issue which could cause sync process to error. + +### 1.0.20 (2/1/17) + +- Fixes: + + + Properly clean up broken replies + +### 1.0.19 (1/31/17) + +- Fixes: + + + Replies on threads won't create duplicate-looking emails. This began + to happen on midnight February 1 UTC due to a date parsing bug + + Improve error handling in sync + + Better retrying of certain syncback actions + +- Development: + + + Now using Electron 1.4.15 + +### 1.0.18 (1/30/17) + +- Performance: + + + 60% reduction of CPU usage during initial sync due to optimizing + unnecessary rendering + +- Fixes: + + + New composer stays in "to" field when initially typing + +- Development: + + + Better documentation for Nylas Mail SDKs + + GitHub repository renamed from nylas/N1 to nylas/nylas-mail + + `master` branch now has Nylas Mail (1.0.x) + + `n1-pro` branch now has Nylas Pro (1.5.x) + +### 1.0.17 (1/27/17) + +- Fixes: + + + Fix send and archive: Can now archive after sending without errors + + Local search now includes more thread results + + Contact autocomplete in composer participant fields now includes more results + +### 1.0.16 (1/27/17) + +- Performance: + + + Improved typing performance in the composer, especially with + misspelled words + +- Fixes: + + + Nylas Mail plugins install properly + + Fix undo and occasional archive & move tasks failing due to not having uids + + Fix logging for auth + + Properly clean up after file downloads + + Properly recover from IMAP uid invalidity + +### 1.0.15 (1/25/17) + +- Features: + + + Improve CPU performance of idle windows + +- Fixes: + + + Correctly detect initial battery status for throttling. + + Correctly allow auth for Custom IMAP accounts only #3185 + +### 1.0.14 (1/25/17) + +- Features: + + + Improved spellchecker + +- Fixes: + + + Correctly update attributes like starred and unread when syncing folders. + Marking as read or starred will no longer bounce back. + + Correctly detect new mail while syncing Gmail inbox. + +### 1.0.13 (1/25/17) + +- Fixes: + + + Messages immediately appear in sent folder. No bouncing back. + + Login more likely to succeed. Waits longer for IMAP + + Doesn't allow invalid form submission + + Correctly handles token refresh failing + + Auto updater says "Nylas Mail" properly + + Sync drafts correctly on Gmail + +- Development: + + + Local sync account API deprecated + + Silence noisy queries in the logs + +### 1.0.12 (1/24/17) + +- Features: + + + New 'Debug' sync button that opens up the console + + Faster search + + Message processing now throttles when on battery + + Analytics for change mail tasks + +- Fixes: + + + Archive, Mark as Unread, and Move to trash don't "bounce back" + + Adding a new account is now smoother + + Improved threading + + Drafts are no longer in the inbox + +### 1.0.11 (1/19/17) + +- Features: + + + Nylas Mail's installer on Mac uses a DMG + +- Fixes: + + + Fixed app being occasionally unresponsive + + Decreased odds of failed logins (by bumping connection timeout value) + + Sync erroring notification no longer tripped by timeouts + +### 1.0.10 (1/19/17) + +- Features: + + + "Contact Support" button now auto-fills information + + Actions reach providers faster + +- Fixes: + + + Show errors on the GMail auth screen + + Show draft sending errors + + Can now correctly search threads via `from:` and `to:` + + Other error management improvements + + The database will now be reset if malformed + + Improve the offline notification + +- Development: + + + Update Thread indexing + + Add loadFromColumm option to Attribute + +### 1.0.9 (1/17/17) + +- Fixes: + + + All Fastmail domains now use the correct credentials + + Offline notification more reliable + + Fix error logging + +### 1.0.8 (1/17/17) + +- Introducing Nylas Mail Basic! Read more about it [here](https://blog.nylas.com/nylas-mail-is-now-free-8350d6a1044d) diff --git a/CONFIGURATION.md b/CONFIGURATION.md new file mode 100644 index 0000000000..afde751b0e --- /dev/null +++ b/CONFIGURATION.md @@ -0,0 +1,8 @@ +# Configuration + +This document outlines configuration options which aren't exposed via N1's +preferences interface but may be useful. + +## Other Config Options + +- `core.workspace.interfaceZoom`: If you'd like the N1 interface to be smaller or larger, this option allows you to scale the UI globally. (Default: 1) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..d104f12fbd --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,38 @@ +# Filing an Issue + +Thanks for checking out Nylas Mail! If you have a feature request, be sure to check out the [open source roadmap](http://trello.com/b/hxsqB6vx/n1-open-source-roadmap). If someone has already requested +the feature you have in mind, you can upvote the card on Trello—to keep things organized, we +often close feature requests on GitHub after creating Trello cards. + +If you've found a bug, try searching for similars issue before filing a new one. Please include +the version of Nylas Mail you're using, the platform you're using (Mac / Windows / Linux), and the +type of email account. (Gmail, Outlook 365, etc.) + +# Pull requests + +We require all authors sign our [Contributor License +Agreement](https://www.nylas.com/cla.html) before pull requests (even +minor ones) can be accepted. (It's similar to other projects, like NodeJS +Meteor, or React). I'm really sorry, but Legal made us do it. + +### Commit Format + +We decided to not impose super strict commit guidelines on the community. + +We're trusting you to be thoughtful, responsible, committers. + +We do have a few heuristics: + +- Keep commits fairly isolated. Don't jam lots of different functionality + in 1 squashed commit. `git bisect` and `git cherry-pick` should still be + reasonable things to do. +- Keep commits fairly significant. DO `squash` all those little file + changes and "fixmes". Don't make it difficult to browse our history. + Play the balance between this idea and the last point. If a commit + doesn't deserve your time to write a long thoughtful message about, then + squash it. +- Be hyper-descriptive in your commit messages. I care less about what + you did (I can read the code), **I want to know WHY you did it**. Put + that in the commit body (not the subject). Itemize the major semantic + changes that happened. +- Read "[How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/)" if you haven't already (but don't be too prescriptivist about it!) diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 7386cd58ee..0000000000 --- a/Dockerfile +++ /dev/null @@ -1,25 +0,0 @@ -# This Dockerfile builds a production-ready image of K2 to be used across all -# services. See the Dockerfile documentation here: -# https://docs.docker.com/engine/reference/builder/ - -# Use the latest Node 6 base docker image -# https://github.com/nodejs/docker-node -FROM node:6 - -# Copy everything (excluding what's in .dockerignore) into an empty dir -COPY . /home -WORKDIR /home - -RUN npm install --production - -# This will do an `npm install` for each of our modules and then link them all -# together. See more about Lerna here: https://github.com/lerna/lerna We have -# to run this separately from npm postinstall due to permission issues. -RUN node_modules/.bin/lerna bootstrap - -# External services run on port 5100. Expose it. -EXPOSE 5100 - -# We use a start-aws command that automatically spawns the correct process -# based on environment variables (which changes instance to instance) -CMD ./node_modules/pm2/bin/pm2 start --no-daemon ./pm2-prod-${AWS_SERVICE_NAME}.yml diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..91cede0765 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,37 @@ + + +##### Are there any related issues? + +... + +##### What operating system are you using? +... + +##### What version of Nylas Mail are you using? +... + +-- + +**Bug?** +##### Do you have any third-party plugins installed? If so, which ones? +... + +##### Is the issue related to a specific email provider (Gmail, Exchange, etc.)? +... + +##### Is the issue reproducible with a particular attachment, message, signature, etc? + +... + +-- + +**Feature Request?** +##### Does this feature exist in another mail client or tool you use? +... diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000000..5f8b06fe3c --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,675 @@ +### GNU GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +### Preamble + +The GNU General Public License is a free, copyleft license for +software and other kinds of works. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom +to share and change all versions of a program--to make sure it remains +free software for all its users. We, the Free Software Foundation, use +the GNU General Public License for most of our software; it applies +also to any other work released this way by its authors. You can apply +it to your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you +have certain responsibilities if you distribute copies of the +software, or if you modify it: responsibilities to respect the freedom +of others. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + +Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + +Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the +manufacturer can do so. This is fundamentally incompatible with the +aim of protecting users' freedom to change the software. The +systematic pattern of such abuse occurs in the area of products for +individuals to use, which is precisely where it is most unacceptable. +Therefore, we have designed this version of the GPL to prohibit the +practice for those products. If such problems arise substantially in +other domains, we stand ready to extend this provision to those +domains in future versions of the GPL, as needed to protect the +freedom of users. + +Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish +to avoid the special danger that patents applied to a free program +could make it effectively proprietary. To prevent this, the GPL +assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and +modification follow. + +### TERMS AND CONDITIONS + +#### 0. Definitions. + +"This License" refers to version 3 of the GNU General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of +an exact copy. The resulting work is called a "modified version" of +the earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user +through a computer network, with no transfer of a copy, is not +conveying. + +An interactive user interface displays "Appropriate Legal Notices" to +the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +#### 1. Source Code. + +The "source code" for a work means the preferred form of the work for +making modifications to it. "Object code" means any non-source form of +a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can +regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same +work. + +#### 2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, +without conditions so long as your license otherwise remains in force. +You may convey covered works to others for the sole purpose of having +them make modifications exclusively for you, or provide you with +facilities for running those works, provided that you comply with the +terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for +you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your +copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes +it unnecessary. + +#### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such +circumvention is effected by exercising rights under this License with +respect to the covered work, and you disclaim any intention to limit +operation or modification of the work as a means of enforcing, against +the work's users, your or third parties' legal rights to forbid +circumvention of technological measures. + +#### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +#### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these +conditions: + +- a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. +- b) The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in section 4 + to "keep intact all notices". +- c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +#### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of +sections 4 and 5, provided that you also convey the machine-readable +Corresponding Source under the terms of this License, in one of these +ways: + +- a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. +- b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the Corresponding + Source from a network server at no charge. +- c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. +- d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. +- e) Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the general + public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of +coverage. For a particular product received by a particular user, +"normally used" refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected +to use, the product. A product is a consumer product regardless of +whether the product has substantial commercial, industrial or +non-consumer uses, unless such uses represent the only significant +mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to +install and execute modified versions of a covered work in that User +Product from a modified version of its Corresponding Source. The +information must suffice to ensure that the continued functioning of +the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or +updates for a work that has been modified or installed by the +recipient, or for the User Product in which it has been modified or +installed. Access to a network may be denied when the modification +itself materially and adversely affects the operation of the network +or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +#### 7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + +- a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or +- b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or +- c) Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or +- d) Limiting the use for publicity purposes of names of licensors + or authors of the material; or +- e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or +- f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions + of it) with contractual assumptions of liability to the recipient, + for any liability that these contractual assumptions directly + impose on those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; the +above requirements apply either way. + +#### 8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +#### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run +a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +#### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +#### 11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned +or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on +the non-exercise of one or more of the rights that are specifically +granted under this License. You may not convey a covered work if you +are a party to an arrangement with a third party that is in the +business of distributing software, under which you make payment to the +third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties +who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by +you (or copies made from those copies), or (b) primarily for and in +connection with specific products or compilations that contain the +covered work, unless you entered into that arrangement, or that patent +license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +#### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not convey it at all. For example, if you agree to +terms that obligate you to collect a royalty for further conveying +from those to whom you convey the Program, the only way you could +satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +#### 13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + +#### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in +detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies that a certain numbered version of the GNU General Public +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that numbered version or +of any later version published by the Free Software Foundation. If the +Program does not specify a version number of the GNU General Public +License, you may choose any version ever published by the Free +Software Foundation. + +If the Program specifies that a proxy can decide which future versions +of the GNU General Public License can be used, that proxy's public +statement of acceptance of a version permanently authorizes you to +choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +#### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +#### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +#### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively state +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper +mail. + +If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands \`show w' and \`show c' should show the +appropriate parts of the General Public License. Of course, your +program's commands might be different; for a GUI interface, you would +use an "about box". + +You should also get your employer (if you work as a programmer) or +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. For more information on this, and how to apply and follow +the GNU GPL, see . + +The GNU General Public License does not permit incorporating your +program into proprietary programs. If your program is a subroutine +library, you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +GNU Lesser General Public License instead of this License. But first, +please read . diff --git a/README.md b/README.md index f998912d65..ea33e2d253 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,117 @@ -# K2 - Sync Engine Experiment +# Nylas Mail - the open-source, extensible mail client +![N1 Screenshot](https://nylas.com/static/img/nylas-mail/hero_graphic_mac@2x.png) -# Initial Setup: + **Nylas Mail is an open-source mail client built on the modern web with [Electron](https://github.com/atom/electron), [React](https://facebook.github.io/react/), and [Flux](https://facebook.github.io/flux/).** It is designed to be extensible, so it's easy to create new experiences and workflows around email. Want to learn more? Check out the [full documentation](https://nylas.github.io/nylas-mail/). -## New Computer (Mac): +[![Build Status](https://travis-ci.org/nylas/nylas-mail.svg?branch=master)](https://travis-ci.org/nylas/nylas-mail) +[![Slack Invite Button](http://slack-invite.nylas.com/badge.svg)](http://slack-invite.nylas.com) -1. Install [Homebrew](http://brew.sh/) -2. Install [VirtualBox 5+](https://www.virtualbox.org/wiki/Downloads) -3. Install [Docker for Mac](https://docs.docker.com/docker-for-mac/) -4. Install [NVM](https://github.com/creationix/nvm) `brew install nvm` -5. Install Node 6+ via NVM: `nvm install 6` -6. Install Redis locally `brew install redis` +#### Want to help build the future of email? [Nylas is hiring](https://jobs.lever.co/nylas)! -## New Computer (Linux - Debian/Ubuntu): -1. Install Node 6+ via NodeSource (trusted): - 1. `curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -` - 1. `sudo apt-get install -y nodejs` -2. Install Redis locally `sudo apt-get install -y redis-server redis-tools` +## Download Nylas Mail -## New to AWS: +You can download compiled versions of Nylas Mail for Windows, Mac OS X, and Linux (.deb) from [https://nylas.com/download](https://nylas.com/download). You can also build and run Nylas Mail (Previously N1) on Fedora. On Arch Linux, you can install **[n1](https://aur.archlinux.org/packages/n1/)** or **[n1-git](https://aur.archlinux.org/packages/n1-git/)** from the aur. -1. Install [Elastic Beanstalk CLI](http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb-cli3-install.html#eb-cli3-install-osx): `sudo pip install awsebcli` - 1. On Linux, you may need to install Python 3's pip via `sudo apt-get install python3-pip` and then run `pip3 install --user awsebcli`. This installs to your home directory and you need to have `~/.local/bin` in your $PATH. -2. Install [AWS CLI](https://aws.amazon.com/cli/): `brew install awscli` on Mac and `pip install --user awscli` on Linux. - 1. Add your AWS IAM Security Credentials to `aws configure`. - 1. These are at Console Home -> IAM -> Users -> {{Your Name}} -> Security - Credentials. Note that your private key was only shown unpon creation. If - you've lost your private key you have to deactivate your old key and - create a new one. -3. Get the K2 team private SSH key. (Ignore this when we have a Bastion Host). Ask someone on K2 for a copy of the private SSH key. Copy it to your ~/.ssh folder. - 1. `chmod 400 ~/.ssh/k2-keypair.pem` - 1. `ssh i ~/.ssh/k2-keypair.pem some-ec2-box-we-own.amazonaws.com` -4. Connect to Elastic Beanstalk instances: `eb init`. Select correct region. Select correct application. +## Build A Plugin -# Developing Locally: +Plugins lie at the heart of Nylas Mail and give it its powerful features. Building your own plugins allows you to integrate the app with other tools, experiment with new workflows, and more. Follow the [Getting Started guide](https://nylas.github.io/nylas-mail/) to write your first plugin in five minutes. To create your own theme, go to our [Theme Starter guide](https://github.com/nylas/N1-theme-starter). -``` -npm run start -npm run logs -npm run stop -``` +If you would like to run the N1 source and contribute, check out our [contributing +guide](https://github.com/nylas/nylas-mail/blob/master/CONTRIBUTING.md). -We use [pm2](http://pm2.keymetrics.io/) to launch a variety of processes -(sync, api, dashboard, processor, etc). +## Themes -You can see the scripts that are running and their arguments in -`/pm2-dev.yml` +The Nylas Mail user interface is styled using CSS, which means it's easy to modify and extend. Nylas Mail comes stock with a few beautiful themes, and there are many more which have been built by community developers -To test to see if the basic API is up go to: `http://lvh.me:5100/ping`. You -should see `pong`. +
-`lvh.me` is a DNS hack that redirects back to 127.0.0.1 with the added -benefit of letting us use subdomains. -# Deploying +#### Bundled Themes +- [Dark](https://github.com/nylas/nylas-mail/tree/master/internal_packages/ui-dark) +- [Darkside](https://github.com/nylas/nylas-mail/tree/master/internal_packages/ui-darkside) (designed by [Jamie Wilson](https://github.com/jamiewilson)) +- [Taiga](https://github.com/nylas/nylas-mail/tree/master/internal_packages/ui-taiga) (designed by [Noah Buscher](https://github.com/noahbuscher)) +- [Ubuntu](https://github.com/nylas/nylas-mail/tree/master/internal_packages/ui-ubuntu) (designed by [Ahmed Elhanafy](https://github.com/ahmedlhanafy)) +- [Less Is More](https://github.com/nylas/nylas-mail/tree/master/internal_packages/ui-less-is-more) (designed by [Alexander Adkins](https://github.com/P0WW0W)) + + + +#### Community Themes +- [Arc Dark](https://github.com/varlesh/Nylas-Arc-Dark-Theme) +- [Predawn](https://github.com/adambmedia/N1-Predawn) +- [ElementaryOS](https://github.com/edipox/elementary-nylas) +- [Ido](https://github.com/edipox/n1-ido)—Polymail-inspired theme +- [Solarized Dark](https://github.com/NSHenry/N1-Solarized-Dark) +- [Berend](https://github.com/Frique/N1-Berend) +- [LevelUp](https://github.com/stolinski/level-up-nylas-n1-theme) +- [Sunrise](https://github.com/jackiehluo/n1-sunrise) +- [ToogaBooga](https://github.com/brycedorn/N1-ToogaBooga) +- [Material](https://github.com/jackiehluo/n1-material) +- [Monokai](https://github.com/dcondrey/n1-monokai) +- [Agapanthus](https://github.com/taniadaniela/n1-agapanthus)—Inbox-inspired theme +- [Stripe](https://github.com/oeaeee/n1-stripe) +- [Kleinstein] (https://github.com/diklein/Kleinstein)—Hide the account list sidebar +- [BoraBora](https://github.com/arimai/N1-BoraBora) +- [Honeyduke](https://github.com/arimai/n1-honeyduke) +- [Snow](https://github.com/Wattenberger/N1-snow-theme) +- [Hull](https://github.com/unity/n1-hull) +- [Express](https://github.com/oeaeee/n1-express) +- [DarkSoda](https://github.com/adambullmer/N1-theme-DarkSoda) +- [Bemind](https://github.com/bemindinteractive/Bemind-N1-Theme) +- [Dracula](https://github.com/dracula/nylas-n1) +- [MouseEatsCat](https://github.com/MouseEatsCat/MouseEatsCat-N1) +- [Sublime Dark](https://github.com/rishabhkesarwani/Nylas-Sublime-Dark-Theme) +- [Firefox](https://github.com/darshandsoni/n1-firefox-theme) +- [Gmail](https://github.com/dregitsky/n1-gmail-theme) +- [Darkish](https://github.com/dyrnade/N1-Darkish) + +#### To install community themes: + +1. Download and unzip the repo +2. In Nylas Mail, select `Developer > Install a Package Manually... ` +3. Navigate to where you downloaded the theme and select the root folder. The theme is copied into the `~/.nylas-mail` folder for your convinence +5. Select `Change Theme...` from the top level menu, and you'll see the newly installed theme. That's it! + + +Want to dive in more? Try [creating your own theme](https://github.com/nylas/nylas-mail-theme-starter)! + + +## Plugin List +We're working on building a plugin index that makes it super easy to add them to Nylas Mail. For now, check out the list below! (Feel free to submit a PR if you build a plugin and want it featured here.) + + +#### Bundled Plugins +Great starting points for creating your own plugins! +- [Translate](https://github.com/nylas/nylas-mail/tree/master/internal_packages/composer-translate)—Works with 10 languages +- [Quick Replies](https://github.com/nylas/nylas-mail/tree/master/internal_packages/composer-templates)—Send emails faster with templates +- [Emoji Keyboard](https://github.com/nylas/nylas-mail/tree/master/internal_packages/composer-emoji)—Insert emoji by typing a colon (:) followed by the name of an emoji symbol +- [GitHub Sidebar Info](https://github.com/nylas/nylas-mail/tree/master/internal_packages/github-contact-card) +- [View on GitHub](https://github.com/nylas/nylas-mail/tree/master/internal_packages/message-view-on-github) +- [Personal Level Indicators](https://github.com/nylas/nylas-mail/tree/master/internal_packages/personal-level-indicators) +- [Phishing Detection](https://github.com/nylas/nylas-mail/tree/master/internal_packages/phishing-detection) + +#### Community Plugins + +Note these are not tested or officially supported by Nylas, but we still think they are really cool! If you find bugs with them, please open GitHub issues on their individual project pages, not the Nylas Mail (N1) repo page. Thanks! + +- [Jiffy](http://noahbuscher.github.io/N1-Jiffy/)—Insert animated GIFs +- [Weather](https://github.com/jackiehluo/n1-weather) +- [Todoist](https://github.com/alexfruehwirth/N1TodoistIntegration) +- [Unsubscribe](https://github.com/colinking/n1-unsubscribe) +- [Squirt Speed Reader](https://github.com/HarleyKwyn/squirt-reader-N1-plugin/) +- [Website Launcher](https://github.com/adriangrantdotorg/nylas-n1-background-webpage)—Opens a URL in separate window +- In Development: [Cypher](https://github.com/mbilker/cypher) (PGP Encryption) +- [Avatars](https://github.com/unity/n1-avatars) +- [Events Calendar (WIP)](https://github.com/nerdenough/n1-events-calendar) +- [Mail in Chat (WIP)](https://github.com/yjchen/mail_in_chat) +- [Evernote](https://github.com/grobgl/n1-evernote) +- [Wunderlist](https://github.com/miguelrs/n1-wunderlist) +- [Participants Display](https://github.com/kbruccoleri/nylas-participants-display) +- [GitHub](https://github.com/ForbesLindesay/N1-GitHub) + +When you install packages, they're moved to ~/.nylas-mail/packages, and Nylas Mail runs apm install on the command line to fetch dependencies listed in the package's package.json + +## Configuration +You can configure Nylas Mail in a few ways—for instance, pointing it to your self-hosted instance of the sync engine or changing the interface zoom level. [Learn more about how.](https://github.com/nylas/nylas-mail/blob/master/CONFIGURATION.md) + +## Feature Requests / Plugin Ideas +Have an idea for a package or a feature you'd love to see in Nylas Mail? Search for existing [GitHub issues](https://github.com/nylas/nylas-mail/issues) and join the conversation! diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000000..eb5a6b4dc5 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,47 @@ +version: '{build}' + +branches: + only: + - master + - /ci.*/ + - /stable.*/ + +# We need to only clone the main module because our submodule requires the +# encrypted ssh key to access submodules +install: +- ps: Install-Product node $env:NODE_VERSION +- ps: nuget install secure-file -ExcludeVersion +- ps: npm config set msvs_version 2013 + +# We need to extract the encrypted private ssh key to clone the submodule. +- ps: | + if ($env:DECRYPTION_PASSWORD) { + secure-file\tools\secure-file -decrypt packages\client-private-plugins\encrypted_certificates\appveyor\win-nylas-n1.p12.enc -secret $env:DECRYPTION_PASSWORD + secure-file\tools\secure-file -decrypt packages\client-private-plugins\encrypted_certificates\appveyor\set_win_env.ps1.enc -secret $env:DECRYPTION_PASSWORD + . packages\client-private-plugins\encrypted_certificates\appveyor\set_win_env.ps1 + } + +build_script: +- cmd: npm install + +deploy_script: +- ps: | + npm run build-client + node packages/client-app/build/create-signed-windows-installer.js + npm run upload-client + +environment: + matrix: + - NODE_VERSION: 6.9 + global: + DEBUG: "electron-windows-installer:*,electron-packager:*" + SIGN_BUILD: true + INSTALL_TARGET: "client" + CERTIFICATE_FILE: .\packages\client-private-plugins\encrypted_certificates\appveyor\win-nylas-n1.p12 + DECRYPTION_PASSWORD: + secure: 48VSzDtdBd52Xlo3TZ1NeR1yRRrZ3AU6Px5XjD5RDp44cFU5GYVspecGqX6DGCV7i0D7nldGMyEbXNrjM1t1Kw== + +cache: + - node_modules -> package.json + - packages\client-app\node_modules -> packages\client-app\package.json + - '%USERPROFILE%\.npm' diff --git a/lerna.json b/lerna.json index 7a61c20d8a..43177a933e 100644 --- a/lerna.json +++ b/lerna.json @@ -1,4 +1,4 @@ { - "lerna": "2.0.0-beta.23", + "lerna": "2.0.0-beta.38", "version": "0.0.1" } diff --git a/package.json b/package.json index 9638289e6e..bab7d49e6e 100644 --- a/package.json +++ b/package.json @@ -1,53 +1,73 @@ { - "name": "k2", + "name": "nylas-mail-all", "version": "0.0.1", - "description": "k2", - "main": "", - "dependencies": { - "bunyan": "1.8.0", - "bunyan-cloudwatch": "2.0.0", - "bunyan-loggly": "^1.0.0", - "bunyan-prettystream": "emorikawa/node-bunyan-prettystream", - "imap": "0.8.x", - "lerna": "2.0.0-beta.23", - "mysql": "^2.11.1", - "pm2": "^1.1.3", - "promise-props": "^1.0.0", - "promise.prototype.finally": "^1.0.1", - "redis": "2.x.x", - "request": "^2.73.0", - "rx": "4.x.x", - "sequelize": "3.x.x", - "underscore": "1.x.x", - "utf7": "https://github.com/truebit/utf7/archive/1f753bac59b99d93b17a5ef11681e232465e2558.tar.gz" - }, + "description": "All components required to run Nylas Mail", "devDependencies": { + "babel-cli": "6.x.x", + "babel-core": "6.x.x", "babel-eslint": "7.1.0", + "babel-preset-electron": "1.4.15", + "babel-preset-react": "6.x.x", + "chalk": "1.x.x", + "coffeelint-cjsx": "2.x.x", + "commander": "^2.9.0", + "electron-installer-dmg": "0.2.x", + "electron-packager": "8.4.x", + "electron-winstaller": "2.x.x", "eslint": "3.10.1", - "eslint_d": "4.2.0", "eslint-config-airbnb": "13.0.0", "eslint-plugin-import": "2.2.0", "eslint-plugin-jsx-a11y": "2.2.3", "eslint-plugin-react": "6.7.1", - "sqlite3": "https://github.com/bengotow/node-sqlite3/archive/bengotow/usleep-v3.1.4.tar.gz" + "eslint_d": "4.2.0", + "fs-extra": "2.x.x", + "fs-plus": "2.x.x", + "glob": "7.x.x", + "grunt": "0.4.x", + "grunt-cli": "0.1.x", + "grunt-coffeelint": "git+https://github.com/atom/grunt-coffeelint.git#cfb99aa99811d52687969532bd5a98011ed95bfe", + "grunt-coffeelint-cjsx": "0.1.x", + "grunt-contrib-coffee": "0.12.x", + "grunt-contrib-csslint": "0.5.x", + "grunt-contrib-less": "0.8.x", + "grunt-lesslint": "0.13.x", + "jasmine": "2.x.x", + "lerna": "emorikawa/lerna#v2.0.0-beta.38.forked", + "load-grunt-parent-tasks": "0.1.1", + "mkdirp": "^0.5.1", + "pm2": "2.4.0", + "request": "2.x.x", + "s3": "4.x.x", + "temp": "0.8.x", + "underscore": "1.8.x" }, "scripts": { - "start": "pm2 start ./pm2-dev.yml --no-daemon", - "stop": "pm2 kill", - "postinstall": "lerna bootstrap" + "start": "npm run client", + "test": "npm run test-client && npm run test-cloud", + "client": "packages/client-app/node_modules/.bin/electron packages/client-app --enable-logging --dev", + "benchmark": "packages/client-app/node_modules/.bin/electron packages/client-app --enable-logging --dev --benchmark", + "test-client": "packages/client-app/node_modules/.bin/electron packages/client-app --enable-logging --test", + "test-client-window": "packages/client-app/node_modules/.bin/electron packages/client-app --enable-logging --test=window", + "test-client-junit": "", + "build-client": "grunt build-client --gruntfile=packages/client-app/build/Gruntfile.js --base=./", + "upload-client": "grunt upload-client --gruntfile=packages/client-app/build/Gruntfile.js --base=./", + "cloud": "pm2 stop all; pm2 delete all; pm2 start packages/cloud-core/pm2-dev.yml --no-daemon", + "cloud-debug": "pm2 stop all; pm2 delete all; pm2 start packages/cloud-core/pm2-debug-cloud-api.yml --no-daemon", + "test-cloud": "cd packages/cloud-api && npm test && cd ../cloud-core && npm test && cd ../cloud-workers && npm test && cd ../isomorphic-core && npm test", + "stop": "npm run stop-cloud", + "stop-cloud": "pm2 stop all; pm2 delete all;", + "build-cloud": "docker build .", + "postinstall": "babel-node scripts/postinstall.es6", + "daily": "babel-node scripts/daily.js" }, "repository": { "type": "git", - "url": "git+https://github.com/nylas/K2.git" + "url": "git+https://github.com/nylas/nylas-mail-all.git" }, "author": "Nylas", - "license": "ISC", - "bugs": { - "url": "https://github.com/nylas/K2/issues" - }, - "homepage": "https://github.com/nylas/K2#readme", + "license": "GPL-3.0", "engines": { - "node": "6.2.2", - "npm": "3.9.5" + "node": "6.9.1", + "npm": "3.10.8" } } diff --git a/packages/README.md b/packages/README.md new file mode 100644 index 0000000000..9734203d91 --- /dev/null +++ b/packages/README.md @@ -0,0 +1,10 @@ +# Monorepo Packages + +Each folder here is designed to act as its own repository. For development +convenience, they are all included here in one monorepo. This allows us to grep +across multiple codebases, not use submodules, and keep a unified commit +history. + +We use [Lerna](https://github.com/lerna/lerna) to manage the monorepo and tie +them all together with the main `nylas-mail-all/scripts/postinstall.es6` script, +which in turn, calls `lerna bootstrap` diff --git a/packages/client-app/.babelrc b/packages/client-app/.babelrc new file mode 100644 index 0000000000..298149bb09 --- /dev/null +++ b/packages/client-app/.babelrc @@ -0,0 +1,7 @@ +{ + "presets": [ + "electron", + "react" + ], + "sourceMaps": "inline" +} diff --git a/packages/client-app/.travis.yml b/packages/client-app/.travis.yml new file mode 100644 index 0000000000..de54a80013 --- /dev/null +++ b/packages/client-app/.travis.yml @@ -0,0 +1,53 @@ +# The open source Nylas Mail Client for Linux and Mac. See AppVeyor for +# Windows +sudo: false + +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - build-essential + - clang + - fakeroot + - g++-4.8 + - git + - libgnome-keyring-dev + - xvfb + - rpm + - libxext-dev + - libxtst-dev + - libxkbfile-dev + +branches: + only: + - master + - /ci-.*/ + - /stable.*/ + +matrix: + include: + - os: linux + env: NODE_VERSION=6.9 CC=gcc-4.8 CXX=g++-4.8 + - os: osx + env: NODE_VERSION=6.9 CC=clang CXX=clang++ + +install: +- git clone https://github.com/creationix/nvm.git /tmp/.nvm +- source /tmp/.nvm/nvm.sh +- nvm install $NODE_VERSION +- nvm use --delete-prefix $NODE_VERSION + +before_script: +- if [ "${TRAVIS_OS_NAME}" == "linux" ]; then + export DISPLAY=:99.0; + sh -e /etc/init.d/xvfb start; + fi + +script: +- npm install && npm test + +cache: + directories: + - node_modules + - apm/node_modules diff --git a/packages/client-app/.watchmanconfig b/packages/client-app/.watchmanconfig new file mode 100644 index 0000000000..8da48ca18f --- /dev/null +++ b/packages/client-app/.watchmanconfig @@ -0,0 +1,13 @@ +{ + "ignore_dirs": [ + "build/node_modules", + "apm/node_modules", + "node_modules", + "src/K2/node_modules", + "src/K2/packages/local-sync/node_modules", + "src/K2/packages/isomorphic-core/node_modules", + "src/K2/packages/cloud-api/node_modules", + "src/K2/packages/cloud-workers/node_modules", + "src/K2/packages/cloud-core/node_modules" + ] +} diff --git a/packages/client-app/CHANGELOG.md b/packages/client-app/CHANGELOG.md new file mode 100644 index 0000000000..f671682104 --- /dev/null +++ b/packages/client-app/CHANGELOG.md @@ -0,0 +1,760 @@ +# Nylas Mail Changelog + +### 2.0.15 (4/17/2017) + + + Correctly handle and inform users about database malformed errors that can + occur both in main process and/or window processes + +### 2.0.14 (4/14/2017) + +- Fixes: + + Prevent from adding duplicate accounts and sync workers due to account id changes + + Correctly remove sync worker reference when destroying it + + Correctly initialize SyncProcessManager with Identity + + Fix contact ranking runtime error + +### 2.0.13 (4/13/2017) + +- Fixes: + + Upload nupkg with correct name for win32 autoupdater to work + + Correctly handle window.unhandledrejection events + +### 2.0.12 (4/13/2017) + +- Fixes: + + Prevent NM from overwriting N1 binary on windows + + Fix runtime error in sync process + + Prevent old N1 config from getting wiped when installing Nylas Mail + +- Development: + + Remove useless docs + +### 2.0.11 (4/12/2017) + +- Fixes: + + Dispose of mail listener connection before getting new one. This will + prevent sync process from leaking Imap connections and getting stuck. + + Fix performance regression when polling for gmail attribute changes + + Don't double report unhandled rejections + + Fix unhandled rejection handling (fix ipc parse error) + + Fix regression when processing messages under a transaction + + Rate limit database malformed error reports to sentry + +### 2.0.10 (4/11/2017) + +- Fixes: + + Fix missing UID error when archiving threads after sending + + Ensure all mail folder exists before trying to access it + + Fix SyncbackMetadataTask dependency + +- Development: + + Don't report stuck sync processes to Sentry + + MessageFactory -> MessageUtils, SendUtils -> ModelUtils + +### 2.0.9 (4/11/2017) + +- Features: + + Re-add imap to the onboarding accounts page + +- Fixes: + + Correctly detect changes in labels, starred and unread for Gmail accounts + + Fix delta streaming connection retries + + Handle weird MIME edge case with @ symbol + +- Performance: + + Wrap message processing in transaction for better performance + + Increase sqlite `page_size` and `cache_size` + +- Cloud: + + Improve performance of reminders worker + + Add DataDog StatsD for heartbeats + + Restart automatically on unhandeld rejections + +- Development: + + Add benchmark mode + +### 2.0.8 (4/7/2017) + +- Fixes: + + Revamp SSL options during authentication to be able to properly auth against + SMTP and prevent sending failures + + Ensure IMAPConnnectionPool uses updated account credentials + + Always fetch and update identity regardless of environment + + Properly handle serialization errors for JSON columns in database + +- Cloud: + + Switch MySQL charset to utf8mb4 + + Add exponential backoff for cloud worker jobs when encountering errors + + Use IMAP connection pool in cloud workers to limit number of connections + + Properly generate metadata deltas when clearing expiration field + + Increment default imap connection socket timeout in cloud workers + +- Plugins: + + Correctly syncback metadata for send later + + Delete drafts after they are sent later + + Correctly ensure messages in sent folder for send later in gmail + + Fix send reminders version conflict error + + Correctly set metadata values for send reminders + + Fix imap folder names in send-reminders + + Fix send later access token refresh + +- Development: + + Add view of CloudJobs in n1.nylas.com/admin + + Ensure daily script grabs current version after pulling latest changes + +### 2.0.1 (4/5/2017) + +- Features: + + Limit search to focused perspective + +- Fixes: + + IMAPConnectionPool now correctly disposes connections + + Ensure we use refreshed access token for all imap connections during sync + + Prevent IMAP connection leaking in sync worker + + Fix send later button saving state and sending action + + Fix inline images for send later + + Correctly enable plugins on 2.0.1 + + Make sure app can update even after signing out of NylasID + + Don't make any requests when NylasID isn't present + +- Cloud: + + Make cloud workers more robust + + Remove old SignalFX reporter & add docs + + Log errors according to bunyan specs + +- Development: + + Add script to run benchmarks once per day at specified time + + Add script to upload benchmark data to Google Sheets + + Add better logging when restarting stuck sync worker + +### 2.0.0 (4/4/2017) + +Introducing Nylas Mail Pro + +- Features: + + Enable snooze, send later, and send reminders + + Add feature limits to reminders and send later + +- Fixes + + Don't assign duplicate folder roles + + Re-setup IdentityStore in new window + +- Development: + + Fix sqlite build for older versions of clang + + Remove rogue scripts-tmp folder + + Remove unecessary db setup for mail rules + +### 1.0.55 (3/31/2017) + +- Fixes + + Ensure open/link tracking work when sending multiple consecutive emails + + Fix performance of contact rankings database query + + Fix performance of thread search index database queries + + Fix performance of ANALYZE queries + +### 1.0.54 (3/31/2017) + +- Features: + + Add search support for `has:attachment` + +- Fixes: + + Reduce database thrashing caused by thread search indexing + + Interrupt long-running syncback tasks + + Fix performance of contact rankings db query + + Don't hit contact rankings endpoint until account is ready + + Ensure sync worker is stopped correctly when removing accounts or when + restarting it + +- Metrics: + + Report metrics about SyncbackTask runs + +- Perf: + + Delay building new hot window to improve win perf + +- Development: + + Add script to benchmarks new commits + + Add DEBUG flag to be able to log all query activity for both databases + + Add `DatabaseStore.write` which doesn't use Transactions + + Metadata test fixes + +### 1.0.52 (3/29/2017) + +- Fixes: + + Fix open and link tracking: + + No longer triggers your own opens & link clicks + + Link tracking indicator is now always present in sent messages + + Fix regression in DB query execution which would delay all queries in the + system. + + Reduce max retry backoff for DB queries, which could hold a query open for + too long + + Fix thread reindexing issues, which should help performance and correctly + index threads for search + + Fix `in:` search syntax for non-gmail search + + Fix references to RetryableError imports + +- Development: + + Add initial sync benchmarking script + + Clean up logging in DatabaseStore: differentiate background queries from + regular queries in the logs, only log queries that actually take more than + 100ms. + + Point the billing server URL to staging by default for easier development, + and allow it to be overriden + + Add index to expiration field on Metadata + +### 1.0.51 (3/28/2017) + +- Features: + + Restore contact rankings feature for better contact predictions in composer + recipient fields + +- Fixes: + + Correctly listen for new mail in between sync loops + + Verify SMTP credentials in /auth endpoint + + Also prioritize sent label for initial Gmail sync + + Properly relaunch windows on autoupdate + + Properly set up local /health endpoint by making sure to attach route files + ending in .es6 to local-api + +- Perf: + + Don't throttle while syncing first 500 threads + +- Metrics: + + Report battery state changes to Mixpanel + +- Development: + + Make deploy-it say what it's doing instead of hanging silently + + Make deploy-it print link to the EB console + + Make help message better on deploy-it + + Add `SHOW_HOT_WINDOW` env for prod debugging of window launches + + Correctly ignore `node_modules` in .ebignore for faster deploys + + Only bootstrap specific pkgs in postinstall for faster npm installs + +### 1.0.50 (3/28/2017) + +- Fixes: + + Fix SyncActivity errors introduced in 1.0.49 + +### 1.0.49 (3/27/2017) + +- Fixes: + + Ensure sync process does not get stuck + + Ensure the worker window is always available + + Retry database operations when encountering locking issues + +- Metrics: + + Detect and report when the worker window is unavailable + + Detect and report when a sync process is stuck + +- Development: + + Windows autoupdater fixes + + Add better documentation for windows autoupdater + + Remap windows dev shortcuts to match the ones used on darwin and linux + + When building app, only re-install for optional dependencies on darwin + +- Cloud: + + Timeout streaming API connections every 15 minutes + + Add missing database indexes from SQL review + +### 1.0.48 (3/27/2017) + +- Fixes: + + Reindex threads when they're updated + + Don't try to restart sync on every IdentityStore change + + Correctly remove inline images with x button + +### 1.0.47 (3/23/2017) + +- Fixes: + + Report hard crashes using Electron's built-in crash reporter + +- Development: + + Don't handle IMAP timeouts in the connection pool + + Record file download times + +### 1.0.46 (3/22/2017) + +- Fixes: + + Ensure files get transferred in forwarded messages + + Correctly sign out of NylasID + + Don't report non-reportable errors in delta connection + + Fix S3 attachment upload for send later + +- Development: + + Rename downloadDataForFile(s) -> getDownloadDataForFile(s) + + Switch type of Metadata value column + + Fix build condition + + Fix DraftFactory specs + + Refactor sync worker IMAPConnectionPool callbacks + +### 1.0.45 (3/21/2017) + +- Fixes: + + Correctly report unhandled errors caught in window. + + Fix passing cursor to delta streams + +### 1.0.44 (3/20/2017) + +- Fixes: + + Add error handling when creating syncback requests + + Fix path for tmp dir in daily script + +### 1.0.43 (3/17/2017) + +- Fixes: + + + Revert nodemailer to previous version + + Creating a folder no longer creates a non-existent duplicate subfolder + + Don't bump threads to the top of list when a message is sent: only update lastReceivedDate if the message was actually received + +### 1.0.42 (3/16/2017) + +- Fixes: + + Fix spellchecker regression (Don't exclude source maps in build) + +### 1.0.41 (3/16/2017) + +- Development: + + Upgrade nodemailer to latest version + +### 1.0.40 (3/15/2017) + +- Features: + + Add support for attachments in send later + +- Development: + + Improve build time + + Windows Autoupdater fixes + +### 1.0.39 (3/14/2017) + +- Fixes: + + Fix missing depedency for imap-provider-settings + +- Development: + + Only upload 7 characters of the commit hash for Windows build + +### 1.0.38 (3/13/2017) + +- Fixes: + + Restart sync when computer awakes from sleep + + Fix issue that made users log out of NylasID, restart, and then force them to log out and restart again in a loop (#3325) + + Don't start sync or delta connections without an identity + +- Development: + + Restore windows build + + Remove specs from production build + + Fix arc lint + + Specify Content-Type in developer bar curl commands + +### 1.0.37 (3/10/2017) + +- Fixes: + + Fix regression introduced in 1.0.36 in the message processor + + Correctly show auth error when we can't connect to n1cloud + + Fix error thrown sometimes when handling send errors + +### 1.0.36 (3/10/2017) + +- Fixes: + + Increase the IMAP connection pool size + + Shim sequelize to timeout after 1 minute on every database operation. This + is a safeguard to prevent unresolved db promises from halting the sync loop. + + Better error handling to prevent the message processor from halting sync + +- Development: + + Measure and report inline composer open times + + Refactor MessageProcessor to be more robust to errors + +### 1.0.35 (3/9/2017) + +- Fixes: + + Make sure delta connection is restarted when an account is re-authed + + More defensive error handling to prevent sync from halting + + Prevent delta streaming connection from retrying too much + + Fix error when attempting to report a fetch id error + + Prevent error restart loop when database is malformed + + Correctly cancel search when the search perspective is cleared + + When many search results are returned from the server, don't try to sync them all at once, otherwise would slow down the main sync process. + + When restarting the app, don't try to continue syncing search results from an old search + +- Development: + + Consolidate delta connection stores, remove `internal_package/deltas` + + Rename NylasSyncStatusStore to FolderSyncProgressStore + + Consolidate APIError status code that we should not report + + Don't report incorrect username or password to Sentry + + Rate limit error reporting for message processing errors + + Fix circular reference error when reporting errors + + Refactor file download IMAPConnectionPool usage + + Don't focus the Console tab in dev tools every time an error is logged + + Correctly set process title + +### 1.0.34 (3/8/2017) + +- Fixes: + + Sync should not get stuck anymore due to sequelize + + Delta Streaming connections now correctly retry after they are closed or an error occurs + + Handle errors when opening imap box correctly + +- Development: + + Add script/daily + + Provide better info to Sentry on sending errors + + Refactor and clean up delta streaming code + + Refactor message processing throttling + +### 1.0.33 (3/8/2017) + +- Features: + + + Add intitial support for send later + +- Fixes: + + + Fetch unknown message uids returned in search results + + Don't throttle message processing when syncing specific UIDs + +- Development: + + + Better grouping for APIError by URL also + + Don't generate sourceMapCache in prod mode + + Upload a next-version to S3 for autoupdate testing + + Windows build fixes + +### 1.0.32 (3/7/2017) + +- Development: + + + Report provider when reporting remove-from-threads-from-list + + Report provider when reporting send perf metrics + +### 1.0.31 (3/6/2017) + +- Fixes: + + + Improve initial sync speed by scaling number of messages synced based on + folder SELECT duration + + Immediately restore sync process when app comes back online after being + disconnected from the internet. + + Can now reply from within notifications again + +- Development: + + + Add basic rate limiting to Sentry + + Report all search performance metrics + + Prevent noisy uncaught errors when closing long connection + + Improve reporting of refresh access token errors + + Don't double report refresh access token API errors + + Replace `setImmediate` with `setTimeout` as Promise scheduler + + Use new Bluebird preferred `longStackTraces` syntax + + NylasAPIRequest refactored and cleaned up + + Search refactors and improvements + + Protect from operating on IMAP connection while opening a box + + Enable logging in prod builds + + Make deploy-it support -h/--help + + Restore cloud testing environments + +### 1.0.30 (2/28/2017) + +- Fixes: + + + Can properly add signatures and select them as default for different + accounts. + + Can now correctly reply to a thread and immediately archive it or move it to + another folder without throwing an error (#3290) + + Correctly fix IMAP connection timeout issues (#3232) + + Nylas Mail no longer opens an increasing number of IMAP connections which + caused some users to reach IMAP server connection limits (#3228) + + Fix memory leak while syncing which caused sync process to restart + sometimes. + + Correctly handle IMAP connections ending unexpectedly + + Correctly detect retryable IMAP errors during sync + detect more + retryable errors + + Correctly catch more authentication errors when sending + + Improve speed of processing messages during sync + + Prevent unnecessary re-renders of the thread list + +- Development: + + + Report performance metrics + + More Coffeescript to Javascript conversions + +### 1.0.29 (2/21/2017) + +- Fixes: + + + You can now click inline images in messages to open them + + More IMAP errors have been identified as retryable, which means users will + see less errors when syncing an account + + Improve performance of thread search indexing queries + + Correctly catch Invalid Login errors when sending + +- Development: + + + Developer bar in Worker window now shows single delta connection + + More code converted to Javascript + +### 1.0.28 (2/16/2017) + +- Fixes: + + + Fix offline notification bug that caused api outage + + We now properly handle gmail auth token errors in the middle of the sync loop. This means less red boxes for users! + + Less battery usage when initial sync has completed! + + No more errors when saving sent messages to sent folders (`auth or accountId` errors) + + No more `Lingering tasks in progress marked as failed errors` + + Syncback tasks will continue retrying even after closing app + + Syncback tasks retry more aggressively + + Detect more offline errors when sending, sending is more reliable + + Imap connection pooling (yet to land) + + More retryable IMAP errors, means less red boxes for users + + Offline notification now shows itself when we’re actually offline, shows countdown for next reconnect attempt + +- Development: + + + More tests + + Don't use breadcrumbs in dev mode + + Add a better reason when waking sync for syncback in the logs + + BackoffScheduler, BatteryManager added for reusability + +### 1.0.27 (2/14/17) + +- Fixes: + + + Offline notification fixes + +### 1.0.26 (2/10/17) + +- Fixes: + + + Downloads retry if they fail + + NylasID doesn't intermittently log out or throw errors + + Fix initial sync for Inbox Zero Gmail accounts + +### 1.0.25 (2/10/17) + +- Fixes: + + + When replying to a thread, properly add it to the sent folder + +- Development: + + + Can now once again run Nylas Mail test suite + +### 1.0.24 (2/9/17) + +- Fixes: + + + Fix error reporter when reporting an error without an identity (this would + crash the app) + +- Development: + + + Fix logging inside local-sync api requests + + Stop reporting handled API errors to Sentry + + Report thread-list perf metrics + +### 1.0.23 (2/8/17) + +- Fixes: + + + Fix emails occasionally being sent with an incomplete body (#3269) + + Correctly thread messages together when open/link tracking is enabled + + Fix `Mailbox does not exist` error for iCloud users (#3253) + + When adding account, correctly remove whitespace from emails + + Fix link in update notification to point to latest changelog + +- Performance: + + + Thread list actions no longer sporadically lag for ~1sec (this is especially + noticeable when many accounts have been added) + + No longer slow down sync process when more than 100,000 threads have been synced + +- Development: + + + Better logging in worker window + + You can now run a development build of Nylas Mail alongside a production + build + +### 1.0.22 (2/7/17) + +- Fixes: + + + New mail notification sounds on startup are combined when multiple new messages have arrived + + You can now correctly select threads using `cmd` and `shift` + + Improve message fetching by making sure we always fetch the most recent + messages first. + + Improve IMAP connection timeouts by incrementing the socket timeout (#3232) + + When adding a Google account, make sure to show the Account Chooser + +- Development: + + + Nylas Identity is no longer stored in config.json + +### 1.0.21 (2/3/17) + +- Fixes: + + + Fixed an issue where Nylas Mail could delete all accounts (addresses #3231) + + Correctly delete and archive threads when they contain sent messages (addresses #2706) + + Improve performance and prevent crashes when running several sync actions + + Improve error handling when sync actions fail + + Fix JSON serialization issue which could cause sync process to error. + +### 1.0.20 (2/1/17) + +- Fixes: + + + Properly clean up broken replies + +### 1.0.19 (1/31/17) + +- Fixes: + + + Replies on threads won't create duplicate-looking emails. This began + to happen on midnight February 1 UTC due to a date parsing bug + + Improve error handling in sync + + Better retrying of certain syncback actions + +- Development: + + + Now using Electron 1.4.15 + +### 1.0.18 (1/30/17) + +- Performance: + + + 60% reduction of CPU usage during initial sync due to optimizing + unnecessary rendering + +- Fixes: + + + New composer stays in "to" field when initially typing + +- Development: + + + Better documentation for Nylas Mail SDKs + + GitHub repository renamed from nylas/N1 to nylas/nylas-mail + + `master` branch now has Nylas Mail (1.0.x) + + `n1-pro` branch now has Nylas Pro (1.5.x) + +### 1.0.17 (1/27/17) + +- Fixes: + + + Fix send and archive: Can now archive after sending without errors + + Local search now includes more thread results + + Contact autocomplete in composer participant fields now includes more results + +### 1.0.16 (1/27/17) + +- Performance: + + + Improved typing performance in the composer, especially with + misspelled words + +- Fixes: + + + Nylas Mail plugins install properly + + Fix undo and occasional archive & move tasks failing due to not having uids + + Fix logging for auth + + Properly clean up after file downloads + + Properly recover from IMAP uid invalidity + +### 1.0.15 (1/25/17) + +- Features: + + + Improve CPU performance of idle windows + +- Fixes: + + + Correctly detect initial battery status for throttling. + + Correctly allow auth for Custom IMAP accounts only #3185 + +### 1.0.14 (1/25/17) + +- Features: + + + Improved spellchecker + +- Fixes: + + + Correctly update attributes like starred and unread when syncing folders. + Marking as read or starred will no longer bounce back. + + Correctly detect new mail while syncing Gmail inbox. + +### 1.0.13 (1/25/17) + +- Fixes: + + + Messages immediately appear in sent folder. No bouncing back. + + Login more likely to succeed. Waits longer for IMAP + + Doesn't allow invalid form submission + + Correctly handles token refresh failing + + Auto updater says "Nylas Mail" properly + + Sync drafts correctly on Gmail + +- Development: + + + Local sync account API deprecated + + Silence noisy queries in the logs + +### 1.0.12 (1/24/17) + +- Features: + + + New 'Debug' sync button that opens up the console + + Faster search + + Message processing now throttles when on battery + + Analytics for change mail tasks + +- Fixes: + + + Archive, Mark as Unread, and Move to trash don't "bounce back" + + Adding a new account is now smoother + + Improved threading + + Drafts are no longer in the inbox + +### 1.0.11 (1/19/17) + +- Features: + + + Nylas Mail's installer on Mac uses a DMG + +- Fixes: + + + Fixed app being occasionally unresponsive + + Decreased odds of failed logins (by bumping connection timeout value) + + Sync erroring notification no longer tripped by timeouts + +### 1.0.10 (1/19/17) + +- Features: + + + "Contact Support" button now auto-fills information + + Actions reach providers faster + +- Fixes: + + + Show errors on the GMail auth screen + + Show draft sending errors + + Can now correctly search threads via `from:` and `to:` + + Other error management improvements + + The database will now be reset if malformed + + Improve the offline notification + +- Development: + + + Update Thread indexing + + Add loadFromColumm option to Attribute + +### 1.0.9 (1/17/17) + +- Fixes: + + + All Fastmail domains now use the correct credentials + + Offline notification more reliable + + Fix error logging + +### 1.0.8 (1/17/17) + +- Introducing Nylas Mail Basic! Read more about it [here](https://blog.nylas.com/nylas-mail-is-now-free-8350d6a1044d) diff --git a/packages/client-app/CONFIGURATION.md b/packages/client-app/CONFIGURATION.md new file mode 100644 index 0000000000..4d7e99009c --- /dev/null +++ b/packages/client-app/CONFIGURATION.md @@ -0,0 +1,13 @@ +# Configuration + +This document outlines configuration options which aren't exposed via N1's +preferences interface but may be useful. + +## Running Against Open Source Sync Engine + +If you want to point N1 to your self-hosted sync engine, select "Hosting your own sync engine?" under the "Get Started" button on the welcome screen. There, follow the instructions for creating your own instance of the sync engine and enter the URL and port number where you have it running. + + +## Other Config Options + +- `core.workspace.interfaceZoom`: If you'd like the N1 interface to be smaller or larger, this option allows you to scale the UI globally. (Default: 1) diff --git a/packages/client-app/CONTRIBUTING.md b/packages/client-app/CONTRIBUTING.md new file mode 100644 index 0000000000..ffa2a56038 --- /dev/null +++ b/packages/client-app/CONTRIBUTING.md @@ -0,0 +1,101 @@ +# Filing an Issue + +Thanks for checking out N1! If you have a feature request, be sure to check out the [open source roadmap](http://trello.com/b/hxsqB6vx/n1-open-source-roadmap). If someone has already requested +the feature you have in mind, you can upvote the card on Trello—to keep things organized, we +often close feature requests on GitHub after creating Trello cards. + +If you've found a bug, try searching for similars issue before filing a new one. Please include +the version of N1 you're using, the platform you're using (Mac / Windows / Linux), and the +type of email account. (Gmail, Outlook 365, etc.) + +# Contributing to N1 + +The hosted sync engine allows us to control adoption of N1 and maintain a great +experience for our users. However, the sync engine is +[open source](https://github.com/nylas/sync-engine) and you can set it +up yourself to begin using N1 immediately. Follow instructions on the [sync +engine](https://github.com/nylas/sync-engine) repository. + +### Getting Started + +Before you get started, make sure you've installed the following dependencies. +N1's build scripts and tooling use modern JavaScript features and require: + + - Node 6.0 or above with npm3 + - python 2.7 + +Linux users should make sure they've installed all the packages listed at +https://github.com/nylas/nylas-mail/blob/master/.travis.yml#L10. Linux users on +Debian 8 and Ubuntu 15.04 onward must also install libgcrypt11 and gnome-keyring. + +Next, clone and build N1 from source: + + git clone https://github.com/nylas/nylas-mail.git + cd nylas-mail + script/bootstrap + +Read the [getting started guides](https://nylas.github.io/N1/getting-started/). + +**Building Nylas on Windows? See the [Windows instructions.](https://github.com/nylas/nylas-mail/blob/master/docs/Windows.md)** + +### Running N1 + + npm start + +### Testing N1 + + npm test + +This will run the full suite of automated unit tests. We use [Jasmine 1.3](http://jasmine.github.io/1.3/introduction.html). + +It runs all tests inside of the `/spec` folder and all tests inside of +`/internal_packages/**/spec` + +You may skip certain tests (temporarily) with `xit` and `xdescribe`, or focus on only certain tests with `fit` and `fdescribe`. + +### Linting N1 + +N1 lints clean against eslint, coffeelint, csslint, lesslint, and our own internal +tool, nylaslint. To run the linters, just run `npm run lint`. + +### Creating binaries + +Once you've checked out N1 and run `script/bootstrap`, you can create a packaged +version of the application by running `script/build`. Note that the builds +available at [https://nylas.com/N1](https://nylas.com/N1) include licensed +fonts, sounds, and other improvements. If you're just looking to run N1, you +should download it there! + + +# Pull requests + +We require all authors sign our [Contributor License +Agreement](https://www.nylas.com/cla.html) before pull requests (even +minor ones) can be accepted. (It's similar to other projects, like NodeJS +Meteor, or React). I'm really sorry, but Legal made us do it. + +### Commit Format + +We decided to not impose super strict commit guidelines on the community. + +We're trusting you to be thoughtful, responsible, committers. + +We do have a few heuristics: + +- Keep commits fairly isolated. Don't jam lots of different functionality + in 1 squashed commit. `git bisect` and `git cherry-pick` should still be + reasonable things to do. +- Keep commits fairly significant. DO `squash` all those little file + changes and "fixmes". Don't make it difficult to browse our history. + Play the balance between this idea and the last point. If a commit + doesn't deserve your time to write a long thoughtful message about, then + squash it. +- Be hyper-descriptive in your commit messages. I care less about what + you did (I can read the code), **I want to know WHY you did it**. Put + that in the commit body (not the subject). Itemize the major semantic + changes that happened. +- Read "[How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/)" if you haven't already (but don't be too prescriptivist about it!) + +# Running Against Open Source Sync Engine + +See [Configuration](https://github.com/nylas/nylas-mail/blob/master/CONFIGURATION.md) diff --git a/packages/client-app/ISSUE_TEMPLATE.md b/packages/client-app/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..91cede0765 --- /dev/null +++ b/packages/client-app/ISSUE_TEMPLATE.md @@ -0,0 +1,37 @@ + + +##### Are there any related issues? + +... + +##### What operating system are you using? +... + +##### What version of Nylas Mail are you using? +... + +-- + +**Bug?** +##### Do you have any third-party plugins installed? If so, which ones? +... + +##### Is the issue related to a specific email provider (Gmail, Exchange, etc.)? +... + +##### Is the issue reproducible with a particular attachment, message, signature, etc? + +... + +-- + +**Feature Request?** +##### Does this feature exist in another mail client or tool you use? +... diff --git a/packages/client-app/LICENSE.md b/packages/client-app/LICENSE.md new file mode 100644 index 0000000000..5f8b06fe3c --- /dev/null +++ b/packages/client-app/LICENSE.md @@ -0,0 +1,675 @@ +### GNU GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +### Preamble + +The GNU General Public License is a free, copyleft license for +software and other kinds of works. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom +to share and change all versions of a program--to make sure it remains +free software for all its users. We, the Free Software Foundation, use +the GNU General Public License for most of our software; it applies +also to any other work released this way by its authors. You can apply +it to your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you +have certain responsibilities if you distribute copies of the +software, or if you modify it: responsibilities to respect the freedom +of others. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + +Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + +Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the +manufacturer can do so. This is fundamentally incompatible with the +aim of protecting users' freedom to change the software. The +systematic pattern of such abuse occurs in the area of products for +individuals to use, which is precisely where it is most unacceptable. +Therefore, we have designed this version of the GPL to prohibit the +practice for those products. If such problems arise substantially in +other domains, we stand ready to extend this provision to those +domains in future versions of the GPL, as needed to protect the +freedom of users. + +Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish +to avoid the special danger that patents applied to a free program +could make it effectively proprietary. To prevent this, the GPL +assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and +modification follow. + +### TERMS AND CONDITIONS + +#### 0. Definitions. + +"This License" refers to version 3 of the GNU General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of +an exact copy. The resulting work is called a "modified version" of +the earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user +through a computer network, with no transfer of a copy, is not +conveying. + +An interactive user interface displays "Appropriate Legal Notices" to +the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +#### 1. Source Code. + +The "source code" for a work means the preferred form of the work for +making modifications to it. "Object code" means any non-source form of +a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can +regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same +work. + +#### 2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, +without conditions so long as your license otherwise remains in force. +You may convey covered works to others for the sole purpose of having +them make modifications exclusively for you, or provide you with +facilities for running those works, provided that you comply with the +terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for +you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your +copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes +it unnecessary. + +#### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such +circumvention is effected by exercising rights under this License with +respect to the covered work, and you disclaim any intention to limit +operation or modification of the work as a means of enforcing, against +the work's users, your or third parties' legal rights to forbid +circumvention of technological measures. + +#### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +#### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these +conditions: + +- a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. +- b) The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in section 4 + to "keep intact all notices". +- c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +#### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of +sections 4 and 5, provided that you also convey the machine-readable +Corresponding Source under the terms of this License, in one of these +ways: + +- a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. +- b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the Corresponding + Source from a network server at no charge. +- c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. +- d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. +- e) Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the general + public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of +coverage. For a particular product received by a particular user, +"normally used" refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected +to use, the product. A product is a consumer product regardless of +whether the product has substantial commercial, industrial or +non-consumer uses, unless such uses represent the only significant +mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to +install and execute modified versions of a covered work in that User +Product from a modified version of its Corresponding Source. The +information must suffice to ensure that the continued functioning of +the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or +updates for a work that has been modified or installed by the +recipient, or for the User Product in which it has been modified or +installed. Access to a network may be denied when the modification +itself materially and adversely affects the operation of the network +or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +#### 7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + +- a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or +- b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or +- c) Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or +- d) Limiting the use for publicity purposes of names of licensors + or authors of the material; or +- e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or +- f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions + of it) with contractual assumptions of liability to the recipient, + for any liability that these contractual assumptions directly + impose on those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; the +above requirements apply either way. + +#### 8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +#### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run +a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +#### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +#### 11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned +or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on +the non-exercise of one or more of the rights that are specifically +granted under this License. You may not convey a covered work if you +are a party to an arrangement with a third party that is in the +business of distributing software, under which you make payment to the +third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties +who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by +you (or copies made from those copies), or (b) primarily for and in +connection with specific products or compilations that contain the +covered work, unless you entered into that arrangement, or that patent +license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +#### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not convey it at all. For example, if you agree to +terms that obligate you to collect a royalty for further conveying +from those to whom you convey the Program, the only way you could +satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +#### 13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + +#### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in +detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies that a certain numbered version of the GNU General Public +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that numbered version or +of any later version published by the Free Software Foundation. If the +Program does not specify a version number of the GNU General Public +License, you may choose any version ever published by the Free +Software Foundation. + +If the Program specifies that a proxy can decide which future versions +of the GNU General Public License can be used, that proxy's public +statement of acceptance of a version permanently authorizes you to +choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +#### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +#### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +#### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively state +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper +mail. + +If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands \`show w' and \`show c' should show the +appropriate parts of the General Public License. Of course, your +program's commands might be different; for a GUI interface, you would +use an "about box". + +You should also get your employer (if you work as a programmer) or +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. For more information on this, and how to apply and follow +the GNU GPL, see . + +The GNU General Public License does not permit incorporating your +program into proprietary programs. If your program is a subroutine +library, you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +GNU Lesser General Public License instead of this License. But first, +please read . diff --git a/packages/client-app/README.md b/packages/client-app/README.md new file mode 100644 index 0000000000..0192fe7f77 --- /dev/null +++ b/packages/client-app/README.md @@ -0,0 +1,148 @@ +# Nylas Mail - the open-source, extensible mail client +![N1 Screenshot](https://nylas.com/static/img/home/screenshot-hero-mac@2x.png) + + **Nylas Mail is an open-source mail client built on the modern web with [Electron](https://github.com/atom/electron), [React](https://facebook.github.io/react/), and [Flux](https://facebook.github.io/flux/).** It is designed to be extensible, so it's easy to create new experiences and workflows around email. Want to learn more? Check out the [full documentation](https://nylas.github.io/nylas-mail/). + +[![Build Status](https://travis-ci.org/nylas/nylas-mail.svg?branch=master)](https://travis-ci.org/nylas/nylas-mail) +[![Slack Invite Button](http://slack-invite.nylas.com/badge.svg)](http://slack-invite.nylas.com) + +#### Want to help build the future of email? [Nylas is hiring](https://jobs.lever.co/nylas)! + +## Download Nylas Mail + +You can download compiled versions of Nylas Mail for Windows, Mac OS X, and Linux (.deb) from [https://nylas.com/download](https://nylas.com/download). You can also build and run Nylas Mail (Previously N1) on Fedora. On Arch Linux, you can install **[n1](https://aur.archlinux.org/packages/n1/)** or **[n1-git](https://aur.archlinux.org/packages/n1-git/)** from the aur. + +## Build A Plugin + +Plugins lie at the heart of Nylas Mail and give it its powerful features. Building your own plugins allows you to integrate the app with other tools, experiment with new workflows, and more. Follow the [Getting Started guide](https://nylas.github.io/nylas-mail/) to write your first plugin in five minutes. To create your own theme, go to our [Theme Starter guide](https://github.com/nylas/N1-theme-starter). + +If you would like to run the N1 source and contribute, check out our [contributing +guide](https://github.com/nylas/nylas-mail/blob/master/CONTRIBUTING.md). + +## Themes + +The Nylas Mail user interface is styled using CSS, which means it's easy to modify and extend. Nylas Mail comes stock with a few beautiful themes, and there are many more which have been built by community developers + +
+ + +#### Bundled Themes +- [Dark](https://github.com/nylas/nylas-mail/tree/master/internal_packages/ui-dark) +- [Darkside](https://github.com/nylas/nylas-mail/tree/master/internal_packages/ui-darkside) (designed by [Jamie Wilson](https://github.com/jamiewilson)) +- [Taiga](https://github.com/nylas/nylas-mail/tree/master/internal_packages/ui-taiga) (designed by [Noah Buscher](https://github.com/noahbuscher)) +- [Ubuntu](https://github.com/nylas/nylas-mail/tree/master/internal_packages/ui-ubuntu) (designed by [Ahmed Elhanafy](https://github.com/ahmedlhanafy)) +- [Less Is More](https://github.com/nylas/nylas-mail/tree/master/internal_packages/ui-less-is-more) (designed by [Alexander Adkins](https://github.com/P0WW0W)) + + + +#### Community Themes +- [Arc Dark](https://github.com/varlesh/Nylas-Arc-Dark-Theme) +- [Predawn](https://github.com/adambmedia/N1-Predawn) +- [ElementaryOS](https://github.com/edipox/elementary-nylas) +- [Ido](https://github.com/edipox/n1-ido)—Polymail-inspired theme +- [Solarized Dark](https://github.com/NSHenry/N1-Solarized-Dark) +- [Berend](https://github.com/Frique/N1-Berend) +- [LevelUp](https://github.com/stolinski/level-up-nylas-n1-theme) +- [Sunrise](https://github.com/jackiehluo/n1-sunrise) +- [ToogaBooga](https://github.com/brycedorn/N1-ToogaBooga) +- [Material](https://github.com/jackiehluo/n1-material) +- [Monokai](https://github.com/dcondrey/n1-monokai) +- [Agapanthus](https://github.com/taniadaniela/n1-agapanthus)—Inbox-inspired theme +- [Stripe](https://github.com/oeaeee/n1-stripe) +- [Kleinstein] (https://github.com/diklein/Kleinstein)—Hide the account list sidebar +- [BoraBora](https://github.com/arimai/N1-BoraBora) +- [Honeyduke](https://github.com/arimai/n1-honeyduke) +- [Snow](https://github.com/Wattenberger/N1-snow-theme) +- [Hull](https://github.com/unity/n1-hull) +- [Express](https://github.com/oeaeee/n1-express) +- [DarkSoda](https://github.com/adambullmer/N1-theme-DarkSoda) +- [Bemind](https://github.com/bemindinteractive/Bemind-N1-Theme) +- [Dracula](https://github.com/dracula/nylas-n1) +- [MouseEatsCat](https://github.com/MouseEatsCat/MouseEatsCat-N1) +- [Sublime Dark](https://github.com/rishabhkesarwani/Nylas-Sublime-Dark-Theme) +- [Firefox](https://github.com/darshandsoni/n1-firefox-theme) +- [Gmail](https://github.com/dregitsky/n1-gmail-theme) +- [Darkish](https://github.com/dyrnade/N1-Darkish) + +#### To install community themes: + +1. Download and unzip the repo +2. In Nylas Mail, select `Developer > Install a Package Manually... ` +3. Navigate to where you downloaded the theme and select the root folder. The theme is copied into the `~/.nylas-mail` folder for your convinence +5. Select `Change Theme...` from the top level menu, and you'll see the newly installed theme. That's it! + + +Want to dive in more? Try [creating your own theme](https://github.com/nylas/nylas-mail-theme-starter)! + + +## Plugin List +We're working on building a plugin index that makes it super easy to add them to Nylas Mail. For now, check out the list below! (Feel free to submit a PR if you build a plugin and want it featured here.) + + +#### Bundled Plugins +Great starting points for creating your own plugins! +- [Translate](https://github.com/nylas/nylas-mail/tree/master/internal_packages/composer-translate)—Works with 10 languages +- [Quick Replies](https://github.com/nylas/nylas-mail/tree/master/internal_packages/composer-templates)—Send emails faster with templates +- [Emoji Keyboard](https://github.com/nylas/nylas-mail/tree/master/internal_packages/composer-emoji)—Insert emoji by typing a colon (:) followed by the name of an emoji symbol +- [GitHub Sidebar Info](https://github.com/nylas/nylas-mail/tree/master/internal_packages/github-contact-card) +- [View on GitHub](https://github.com/nylas/nylas-mail/tree/master/internal_packages/message-view-on-github) +- [Personal Level Indicators](https://github.com/nylas/nylas-mail/tree/master/internal_packages/personal-level-indicators) +- [Phishing Detection](https://github.com/nylas/nylas-mail/tree/master/internal_packages/phishing-detection) + +#### Community Plugins + +Note these are not tested or officially supported by Nylas, but we still think they are really cool! If you find bugs with them, please open GitHub issues on their individual project pages, not the Nylas Mail (N1) repo page. Thanks! + +- [Jiffy](http://noahbuscher.github.io/N1-Jiffy/)—Insert animated GIFs +- [Weather](https://github.com/jackiehluo/n1-weather) +- [Todoist](https://github.com/alexfruehwirth/N1TodoistIntegration) +- [Unsubscribe](https://github.com/colinking/n1-unsubscribe) +- [Squirt Speed Reader](https://github.com/HarleyKwyn/squirt-reader-N1-plugin/) +- [Website Launcher](https://github.com/adriangrantdotorg/nylas-n1-background-webpage)—Opens a URL in separate window +- In Development: [Cypher](https://github.com/mbilker/cypher) (PGP Encryption) +- [Avatars](https://github.com/unity/n1-avatars) +- [Events Calendar (WIP)](https://github.com/nerdenough/n1-events-calendar) +- [Mail in Chat (WIP)](https://github.com/yjchen/mail_in_chat) +- [Evernote](https://github.com/grobgl/n1-evernote) +- [Wunderlist](https://github.com/miguelrs/n1-wunderlist) +- [Participants Display](https://github.com/kbruccoleri/nylas-participants-display) +- [GitHub](https://github.com/ForbesLindesay/N1-GitHub) + +When you install packages, they're moved to ~/.nylas-mail/packages, and Nylas Mail runs apm install on the command line to fetch dependencies listed in the package's package.json + + +## Building the docs + +Plugin SDK docs are available at [https://nylas.github.io/nylas-mail/](https://nylas.github.io/nylas-mail/). Here's how you build them: + +Until my patch gets merged, docs need to be built manually using mg's fork. + + git clone git@github.com:grinich/gitbook.git + + cd nylas-mail + + ./node_modules/.bin/gitbook alias ../gitbook latest + +Then to actually build the docs: + + script/grunt docs + + ./node_modules/.bin/gitbook --gitbook=latest build . ./_docs_output --log=debug --debug + + rm -r docs_src/classes + +If you want to preview the docs: + + pushd ./_docs_output; python -m SimpleHTTPServer; popd + +Just want to publish everything? There's a helper script that does it for you: + + script/publish-docs + + +## Configuration +You can configure Nylas Mail in a few ways—for instance, pointing it to your self-hosted instance of the sync engine or changing the interface zoom level. [Learn more about how.](https://github.com/nylas/nylas-mail/blob/master/CONFIGURATION.md) + +## Feature Requests / Plugin Ideas + +Have an idea for a package or a feature you'd love to see in Nylas Mail? Search for existing [GitHub issues](https://github.com/nylas/nylas-mail/issues) and join the conversation! diff --git a/packages/client-app/apm/README.md b/packages/client-app/apm/README.md new file mode 100644 index 0000000000..1b22ac5fd1 --- /dev/null +++ b/packages/client-app/apm/README.md @@ -0,0 +1,2 @@ +Nylas Mail ships a copy of [apm](https://github.com/atom/apm) to build packages +when users choose to install them. This won't be true much longer. diff --git a/packages/client-app/apm/package.json b/packages/client-app/apm/package.json new file mode 100644 index 0000000000..f6c13d7005 --- /dev/null +++ b/packages/client-app/apm/package.json @@ -0,0 +1,11 @@ +{ + "name": "n1-bundled-apm", + "description": "N1's bundled apm", + "repository": { + "type": "git", + "url": "https://github.com/nylas/nylas-mail" + }, + "dependencies": { + "atom-package-manager": "1.1.1" + } +} diff --git a/packages/client-app/build/Gruntfile.js b/packages/client-app/build/Gruntfile.js new file mode 100644 index 0000000000..00808d5cb6 --- /dev/null +++ b/packages/client-app/build/Gruntfile.js @@ -0,0 +1,92 @@ +/* eslint global-require: 0 */ +/* eslint import/no-dynamic-require: 0 */ +const path = require('path'); + +module.exports = (grunt) => { + if (!grunt.option('platform')) { + grunt.option('platform', process.platform); + } + + /** + * The main appDir is that of the root nylas-mail-all repo. This Gruntfile + * is designed to be run from the npm-build-client task whose repo root is + * the main nylas-mail-all package. + */ + const appDir = path.resolve(path.join('packages', 'client-app')); + const buildDir = path.join(appDir, 'build'); + const tasksDir = path.join(buildDir, 'tasks'); + const taskHelpers = require(path.join(tasksDir, 'task-helpers'))(grunt) + + // This allows all subsequent paths to the relative to the root of the repo + grunt.config.init({ + 'taskHelpers': taskHelpers, + 'rootDir': path.resolve('./'), + 'buildDir': buildDir, + 'appDir': appDir, + 'classDocsOutputDir': './docs_src/classes', + 'outputDir': path.join(appDir, 'dist'), + 'appJSON': grunt.file.readJSON(path.join(appDir, 'package.json')), + 'source:coffeescript': [ + 'internal_packages/**/*.cjsx', + 'internal_packages/**/*.coffee', + 'dot-nylas/**/*.coffee', + 'src/**/*.coffee', + 'src/**/*.cjsx', + '!src/**/node_modules/**/*.coffee', + '!internal_packages/**/node_modules/**/*.coffee', + ], + 'source:es6': [ + 'internal_packages/**/*.jsx', + 'internal_packages/**/*.es6', + 'internal_packages/**/*.es', + 'dot-nylas/**/*.es6', + 'dot-nylas/**/*.es', + 'src/**/*.es6', + 'src/**/*.es', + 'src/**/*.jsx', + 'src/K2/**/*.js', // K2 doesn't use ES6 extension, lint it anyway! + '!src/K2/packages/local-private/src/error-logger-extensions/*.js', + '!src/**/node_modules/**/*.es6', + '!src/**/node_modules/**/*.es', + '!src/**/node_modules/**/*.jsx', + '!src/K2/**/node_modules/**/*.js', + '!internal_packages/**/node_modules/**/*.es6', + '!internal_packages/**/node_modules/**/*.es', + '!internal_packages/**/node_modules/**/*.jsx', + ], + }); + + grunt.loadTasks(tasksDir); + grunt.file.setBase(appDir); + + grunt.registerTask('docs', ['docs-build', 'docs-render']); + grunt.registerTask('lint', [ + 'eslint', + 'lesslint', + 'nylaslint', + 'coffeelint', + 'csslint', + ]); + + if (grunt.option('platform') === 'win32') { + grunt.registerTask("build-client", [ + "package", + // The Windows electron-winstaller task must be run outside of grunt + ]); + } else if (grunt.option('platform') === 'darwin') { + const subTasks = process.env.SIGN_BUILD ? ["setup-mac-keychain"] : [] + grunt.registerTask("build-client", subTasks.concat([ + "package", + "create-mac-zip", + "create-mac-dmg", + ])); + } else if (grunt.option('platform') === 'linux') { + grunt.registerTask("build-client", [ + "package", + "create-deb-installer", + "create-rpm-installer", + ]); + } + + grunt.registerTask("upload-client", ["upload"]) +} diff --git a/packages/client-app/build/README.md b/packages/client-app/build/README.md new file mode 100644 index 0000000000..0623125f41 --- /dev/null +++ b/packages/client-app/build/README.md @@ -0,0 +1,27 @@ +# N1 Build Environment +Node version 0.10.x (Due to the version of electron currently used.) + +# N1 Building and Tasks + +This folder contains tasks to create production builds of N1 + +Tasks should not be executed from this folder, but rather from `/scripts`. The +`/scripts` folder has convenient methods that fix paths and do environment +checks. + +Note that most of the task definitions are stored in `/build/tasks` + +## Some useful tasks + +NOTE: Run all of these from the N1 root folder. + +**Linting:** + + `script/grunt lint` + +**Building:** + + `script/grunt build` + +The build folder has its own package.json and is isolated so we can use `npm` +to compile against v8's headers instead of `apm` diff --git a/packages/client-app/build/config/coffeelint.json b/packages/client-app/build/config/coffeelint.json new file mode 100644 index 0000000000..38ed9d3cbb --- /dev/null +++ b/packages/client-app/build/config/coffeelint.json @@ -0,0 +1,20 @@ +{ + "max_line_length": { + "level": "ignore" + }, + "no_empty_param_list": { + "level": "error" + }, + "arrow_spacing": { + "level": "error" + }, + "no_unnecessary_fat_arrows": { + "level": "ignore" + }, + "no_interpolation_in_single_quotes": { + "level": "error" + }, + "no_debugger": { + "level": "error" + } +} diff --git a/packages/client-app/build/create-signed-windows-installer.js b/packages/client-app/build/create-signed-windows-installer.js new file mode 100644 index 0000000000..7ffa150ebb --- /dev/null +++ b/packages/client-app/build/create-signed-windows-installer.js @@ -0,0 +1,31 @@ +/* eslint import/no-dynamic-require:0 */ +/** + * NOTE: Due to path issues, this script must be run outside of grunt + * directly from a powershell command. + */ +const path = require('path') +const createWindowsInstaller = require('electron-winstaller').createWindowsInstaller + +const appDir = path.join(__dirname, ".."); +const version = require(path.join(appDir, 'package.json')).version; + +const config = { + usePackageJson: false, + outputDirectory: path.join(appDir, 'dist'), + appDirectory: path.join(appDir, 'dist', 'nylas-win32-ia32'), + loadingGif: path.join(appDir, 'build', 'resources', 'win', 'loading.gif'), + iconUrl: 'http://edgehill.s3.amazonaws.com/static/nylas.ico', + certificateFile: process.env.CERTIFICATE_FILE, + certificatePassword: process.env.WINDOWS_CODESIGN_KEY_PASSWORD, + description: "Nylas Mail", + version: version, + title: "nylas", + authors: 'Nylas Inc.', + setupIcon: path.join(appDir, 'build', 'resources', 'win', 'nylas.ico'), + setupExe: 'NylasMailSetup.exe', + exe: 'nylas.exe', + name: 'NylasMail', +} +console.log(config); +console.log("---> Starting") +createWindowsInstaller(config, console.log, console.error) diff --git a/packages/client-app/build/docs_templates/_function.html b/packages/client-app/build/docs_templates/_function.html new file mode 100644 index 0000000000..5ef834d927 --- /dev/null +++ b/packages/client-app/build/docs_templates/_function.html @@ -0,0 +1,40 @@ +

+ {{name}}({{#each arguments}}{{#if isOptional}}[{{/if}}{{name}}{{#if isOptional}}]{{/if}}{{/each}}) +

+ +
+

{{{description}}}

+
+ +{{#if arguments.length}} +Parameters + + + + + + {{#each arguments}} + + + + + {{/each}} +
ArgumentDescription
+ {{name}} + + {{#if isOptional}}Optional{{/if}} + {{{description}}} +
+{{/if}} + +{{#if returnValues.length}} +Returns + + + + + {{#each returnValues}} + + {{/each}} +
Return Values
{{{description}}}
+{{/if}} diff --git a/packages/client-app/build/docs_templates/_property.html b/packages/client-app/build/docs_templates/_property.html new file mode 100644 index 0000000000..ee21ae0a68 --- /dev/null +++ b/packages/client-app/build/docs_templates/_property.html @@ -0,0 +1,17 @@ +

{{name}}

+

{{{description}}}

+{{#if arguments.length}} + + {{#each arguments}} + + + + + {{/each}} +
+ {{name}} + + {{#if isOptional}}Optional{{/if}} + {{{description}}} +
+{{/if}} diff --git a/packages/client-app/build/docs_templates/class.md b/packages/client-app/build/docs_templates/class.md new file mode 100644 index 0000000000..9b75d411c8 --- /dev/null +++ b/packages/client-app/build/docs_templates/class.md @@ -0,0 +1,44 @@ +# {{ name }} + +## Summary + +{{{documentation.description}}} + +
    + {{#each documentation.sections}} +
  • {{name}}
  • + {{/each}} +
+ + +{{#if documentation.classProperties.length}} + +### Class Properties + +{{#each documentation.classProperties}} +{{> _property.html}} +{{/each}} + +{{/if}} + + +{{#if documentation.classMethods.length}} + +### Class Methods + +{{#each documentation.classMethods}} +{{> _function.html}} +{{/each}} + +{{/if}} + + +{{#if documentation.instanceMethods.length}} + +### Instance Methods + +{{#each documentation.instanceMethods}} +{{> _function.html}} +{{/each}} + +{{/if}} diff --git a/packages/client-app/build/docs_templates/sidebar.md b/packages/client-app/build/docs_templates/sidebar.md new file mode 100644 index 0000000000..a715353cc7 --- /dev/null +++ b/packages/client-app/build/docs_templates/sidebar.md @@ -0,0 +1,45 @@ +{{!-- This is our preferred ordering, so do it manually! --}} +## General +{{#each sidebar.General}} +* [{{this}}](/classes/{{this}}.md) +{{/each}} + +## Component Kit +{{#each sidebar.[Component Kit]}} +* [{{this}}](/classes/{{this}}.md) +{{/each}} + +## Extensions +{{#each sidebar.[Extensions]}} +* [{{this}}](/classes/{{this}}.md) +{{/each}} + +## Models +{{#each sidebar.[Models]}} +* [{{this}}](/classes/{{this}}.md) +{{/each}} + +## Stores +{{#each sidebar.[Stores]}} +* [{{this}}](/classes/{{this}}.md) +{{/each}} + +## Database +{{#each sidebar.[Database]}} +* [{{this}}](/classes/{{this}}.md) +{{/each}} + +## Drafts +{{#each sidebar.[Drafts]}} +* [{{this}}](/classes/{{this}}.md) +{{/each}} + +## NylasEnv +{{#each sidebar.[NylasEnv]}} +* [{{this}}](/classes/{{this}}.md) +{{/each}} + +## Atom +{{#each sidebar.[Atom]}} +* [{{this}}](/classes/{{this}}.md) +{{/each}} diff --git a/packages/client-app/build/resources/asar-ordering-hint.txt b/packages/client-app/build/resources/asar-ordering-hint.txt new file mode 100644 index 0000000000..980230dd1c --- /dev/null +++ b/packages/client-app/build/resources/asar-ordering-hint.txt @@ -0,0 +1,3818 @@ +956764: package.json +1760546: src/browser/main.js +1766806: node_modules/fs-plus/package.json +1768751: node_modules/fs-plus/lib/fs-plus.js +1789356: node_modules/fs-plus/node_modules/underscore-plus/package.json +1791321: node_modules/fs-plus/node_modules/underscore-plus/lib/underscore-plus.js +1806109: node_modules/fs-plus/node_modules/underscore-plus/node_modules/underscore/package.json +1807823: node_modules/fs-plus/node_modules/underscore-plus/node_modules/underscore/underscore.js +1853312: node_modules/fs-plus/node_modules/async/package.json +1854649: node_modules/fs-plus/node_modules/async/lib/async.js +1884050: node_modules/fs-plus/node_modules/mkdirp/package.json +1885107: node_modules/fs-plus/node_modules/mkdirp/index.js +1887478: node_modules/fs-plus/node_modules/rimraf/package.json +1889121: node_modules/fs-plus/node_modules/rimraf/rimraf.js +1894875: node_modules/mkdirp/package.json +1896279: node_modules/mkdirp/index.js +1898909: node_modules/optimist/package.json +1900199: node_modules/optimist/index.js +1914385: node_modules/optimist/node_modules/wordwrap/package.json +1915858: node_modules/optimist/node_modules/wordwrap/index.js +1918089: src/error-logger.js +1926560: node_modules/nslog/package.json +1928316: node_modules/nslog/lib/nslog.js +1928640: src/error-logger-extensions/nylas-private-error-reporter.js +1933337: node_modules/raven/package.json +1935222: node_modules/raven/index.js +1935631: node_modules/raven/lib/client.js +1941308: node_modules/raven/lib/parsers.js +1945141: node_modules/raven/node_modules/cookie/package.json +1946217: node_modules/raven/node_modules/cookie/index.js +1948116: node_modules/raven/lib/utils.js +1953762: node_modules/raven/lib/transports.js +1956068: node_modules/raven/node_modules/lsmod/package.json +1957254: node_modules/raven/node_modules/lsmod/index.js +1958729: node_modules/raven/node_modules/stack-trace/package.json +1960042: node_modules/raven/node_modules/stack-trace/lib/stack-trace.js +1962761: node_modules/node-uuid/package.json +1964629: node_modules/node-uuid/uuid.js +1933337: node_modules/raven/package.json +1972642: node_modules/raven/lib/middleware/connect.js +1973294: src/compile-cache.js +1978902: src/compile-support/babel.js +1980527: static/babelrc.json +1980671: src/compile-support/coffee-script.js +1981853: src/compile-support/typescript.js +1983139: node_modules/underscore/package.json +1985095: node_modules/underscore/underscore.js +2038014: node_modules/source-map-support/package.json +2039642: node_modules/source-map-support/source-map-support.js +2054178: node_modules/source-map-support/node_modules/source-map/package.json +2057217: node_modules/source-map-support/node_modules/source-map/lib/source-map.js +2057643: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-map-generator.js +2070902: node_modules/source-map-support/node_modules/source-map/node_modules/amdefine/package.json +2072148: node_modules/source-map-support/node_modules/source-map/node_modules/amdefine/amdefine.js +2082064: node_modules/source-map-support/node_modules/source-map/lib/source-map/base64-vlq.js +2086956: node_modules/source-map-support/node_modules/source-map/lib/source-map/base64.js +2088093: node_modules/source-map-support/node_modules/source-map/lib/source-map/util.js +2093422: node_modules/source-map-support/node_modules/source-map/lib/source-map/array-set.js +2096140: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-map-consumer.js +2113947: node_modules/source-map-support/node_modules/source-map/lib/source-map/binary-search.js +2117157: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-node.js +2130092: src/browser/application.js +2161879: src/browser/system-tray-manager.js +2173675: src/browser/nylas-window.js +2187662: src/browser/window-manager.js +91213297: src/browser/window-launcher.js +91200495: src/browser/file-list-cache.js +2195924: src/browser/application-menu.js +2207382: src/flux/models/utils.js +8174990: node_modules/moment-timezone/package.json +8177056: node_modules/moment-timezone/index.js +8177170: node_modules/moment-timezone/moment-timezone.js +3971136: node_modules/moment/package.json +3974612: node_modules/moment/moment.js +8190929: node_modules/moment-timezone/data/packed/latest.json +3261374: src/task-registry.js +3264167: src/serializable-registry.js +3271782: src/database-object-registry.js +2228693: src/browser/auto-update-manager.js +2235663: src/browser/nylas-protocol-handler.js +2396007: node_modules/season/package.json +2397840: node_modules/season/lib/cson.js +2238228: src/config.js +2261295: src/config-utils.js +2264399: node_modules/emissary/package.json +2266273: node_modules/emissary/lib/emissary.js +2266555: node_modules/emissary/lib/helpers.js +2268129: node_modules/emissary/lib/behavior.js +2272107: node_modules/emissary/node_modules/underscore-plus/package.json +2274072: node_modules/emissary/node_modules/underscore-plus/lib/underscore-plus.js +2288860: node_modules/emissary/node_modules/underscore-plus/node_modules/underscore/package.json +2290574: node_modules/emissary/node_modules/underscore-plus/node_modules/underscore/underscore.js +2336063: node_modules/property-accessors/package.json +2337929: node_modules/property-accessors/lib/property-accessors.js +2340201: node_modules/property-accessors/node_modules/mixto/package.json +2341779: node_modules/property-accessors/node_modules/mixto/lib/mixin.js +2343179: node_modules/emissary/lib/signal.js +2352520: node_modules/emissary/lib/emitter.js +2367779: node_modules/emissary/node_modules/mixto/package.json +2369463: node_modules/emissary/node_modules/mixto/lib/mixin.js +2370863: node_modules/emissary/lib/subscriber.js +2374948: node_modules/emissary/lib/subscription.js +2376215: node_modules/event-kit/package.json +2378208: node_modules/event-kit/lib/event-kit.js +2378397: node_modules/event-kit/lib/emitter.js +2382101: node_modules/event-kit/lib/disposable.js +2394595: node_modules/event-kit/lib/composite-disposable.js +2441735: node_modules/pathwatcher/package.json +2444020: node_modules/pathwatcher/lib/main.js +2450494: node_modules/pathwatcher/lib/file.js +2466571: node_modules/pathwatcher/node_modules/underscore-plus/package.json +2468584: node_modules/pathwatcher/node_modules/underscore-plus/lib/underscore-plus.js +2483372: node_modules/pathwatcher/node_modules/underscore-plus/node_modules/underscore/package.json +2485130: node_modules/pathwatcher/node_modules/underscore-plus/node_modules/underscore/underscore.js +2530619: node_modules/pathwatcher/lib/directory.js +2542040: node_modules/pathwatcher/node_modules/async/package.json +2543482: node_modules/pathwatcher/node_modules/async/lib/async.js +2572883: src/color.js +1973294: src/compile-cache.js +1766806: node_modules/fs-plus/package.json +1768751: node_modules/fs-plus/lib/fs-plus.js +1789356: node_modules/fs-plus/node_modules/underscore-plus/package.json +1791321: node_modules/fs-plus/node_modules/underscore-plus/lib/underscore-plus.js +1806109: node_modules/fs-plus/node_modules/underscore-plus/node_modules/underscore/package.json +1807823: node_modules/fs-plus/node_modules/underscore-plus/node_modules/underscore/underscore.js +1973294: src/compile-cache.js +1766806: node_modules/fs-plus/package.json +1768751: node_modules/fs-plus/lib/fs-plus.js +1789356: node_modules/fs-plus/node_modules/underscore-plus/package.json +1791321: node_modules/fs-plus/node_modules/underscore-plus/lib/underscore-plus.js +1806109: node_modules/fs-plus/node_modules/underscore-plus/node_modules/underscore/package.json +1973294: src/compile-cache.js +1807823: node_modules/fs-plus/node_modules/underscore-plus/node_modules/underscore/underscore.js +1766806: node_modules/fs-plus/package.json +1768751: node_modules/fs-plus/lib/fs-plus.js +1789356: node_modules/fs-plus/node_modules/underscore-plus/package.json +1791321: node_modules/fs-plus/node_modules/underscore-plus/lib/underscore-plus.js +1853312: node_modules/fs-plus/node_modules/async/package.json +1854649: node_modules/fs-plus/node_modules/async/lib/async.js +1806109: node_modules/fs-plus/node_modules/underscore-plus/node_modules/underscore/package.json +1807823: node_modules/fs-plus/node_modules/underscore-plus/node_modules/underscore/underscore.js +1884050: node_modules/fs-plus/node_modules/mkdirp/package.json +1885107: node_modules/fs-plus/node_modules/mkdirp/index.js +1887478: node_modules/fs-plus/node_modules/rimraf/package.json +1889121: node_modules/fs-plus/node_modules/rimraf/rimraf.js +1978902: src/compile-support/babel.js +1853312: node_modules/fs-plus/node_modules/async/package.json +1854649: node_modules/fs-plus/node_modules/async/lib/async.js +1884050: node_modules/fs-plus/node_modules/mkdirp/package.json +1885107: node_modules/fs-plus/node_modules/mkdirp/index.js +1887478: node_modules/fs-plus/node_modules/rimraf/package.json +1889121: node_modules/fs-plus/node_modules/rimraf/rimraf.js +1853312: node_modules/fs-plus/node_modules/async/package.json +1980527: static/babelrc.json +1854649: node_modules/fs-plus/node_modules/async/lib/async.js +1980671: src/compile-support/coffee-script.js +1981853: src/compile-support/typescript.js +1978902: src/compile-support/babel.js +1983139: node_modules/underscore/package.json +1985095: node_modules/underscore/underscore.js +1884050: node_modules/fs-plus/node_modules/mkdirp/package.json +1885107: node_modules/fs-plus/node_modules/mkdirp/index.js +1887478: node_modules/fs-plus/node_modules/rimraf/package.json +1889121: node_modules/fs-plus/node_modules/rimraf/rimraf.js +1978902: src/compile-support/babel.js +1980527: static/babelrc.json +1980671: src/compile-support/coffee-script.js +1981853: src/compile-support/typescript.js +2038014: node_modules/source-map-support/package.json +1983139: node_modules/underscore/package.json +2039642: node_modules/source-map-support/source-map-support.js +1985095: node_modules/underscore/underscore.js +2054178: node_modules/source-map-support/node_modules/source-map/package.json +2057217: node_modules/source-map-support/node_modules/source-map/lib/source-map.js +1980527: static/babelrc.json +2057643: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-map-generator.js +1980671: src/compile-support/coffee-script.js +2070902: node_modules/source-map-support/node_modules/source-map/node_modules/amdefine/package.json +1981853: src/compile-support/typescript.js +2072148: node_modules/source-map-support/node_modules/source-map/node_modules/amdefine/amdefine.js +1983139: node_modules/underscore/package.json +1985095: node_modules/underscore/underscore.js +2082064: node_modules/source-map-support/node_modules/source-map/lib/source-map/base64-vlq.js +2038014: node_modules/source-map-support/package.json +2039642: node_modules/source-map-support/source-map-support.js +2086956: node_modules/source-map-support/node_modules/source-map/lib/source-map/base64.js +2054178: node_modules/source-map-support/node_modules/source-map/package.json +2057217: node_modules/source-map-support/node_modules/source-map/lib/source-map.js +2057643: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-map-generator.js +2088093: node_modules/source-map-support/node_modules/source-map/lib/source-map/util.js +2070902: node_modules/source-map-support/node_modules/source-map/node_modules/amdefine/package.json +2072148: node_modules/source-map-support/node_modules/source-map/node_modules/amdefine/amdefine.js +2093422: node_modules/source-map-support/node_modules/source-map/lib/source-map/array-set.js +2096140: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-map-consumer.js +2038014: node_modules/source-map-support/package.json +2039642: node_modules/source-map-support/source-map-support.js +2113947: node_modules/source-map-support/node_modules/source-map/lib/source-map/binary-search.js +2054178: node_modules/source-map-support/node_modules/source-map/package.json +2117157: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-node.js +2082064: node_modules/source-map-support/node_modules/source-map/lib/source-map/base64-vlq.js +2057217: node_modules/source-map-support/node_modules/source-map/lib/source-map.js +2057643: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-map-generator.js +2086956: node_modules/source-map-support/node_modules/source-map/lib/source-map/base64.js +2070902: node_modules/source-map-support/node_modules/source-map/node_modules/amdefine/package.json +2072148: node_modules/source-map-support/node_modules/source-map/node_modules/amdefine/amdefine.js +2851648: src/module-cache.js +2088093: node_modules/source-map-support/node_modules/source-map/lib/source-map/util.js +2093422: node_modules/source-map-support/node_modules/source-map/lib/source-map/array-set.js +2866883: node_modules/semver/package.json +2868219: node_modules/semver/semver.js +2096140: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-map-consumer.js +2082064: node_modules/source-map-support/node_modules/source-map/lib/source-map/base64-vlq.js +2113947: node_modules/source-map-support/node_modules/source-map/lib/source-map/binary-search.js +2086956: node_modules/source-map-support/node_modules/source-map/lib/source-map/base64.js +2117157: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-node.js +2088093: node_modules/source-map-support/node_modules/source-map/lib/source-map/util.js +956764: package.json +2851648: src/module-cache.js +2093422: node_modules/source-map-support/node_modules/source-map/lib/source-map/array-set.js +2096140: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-map-consumer.js +2866883: node_modules/semver/package.json +2868219: node_modules/semver/semver.js +2113947: node_modules/source-map-support/node_modules/source-map/lib/source-map/binary-search.js +2117157: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-node.js +2851648: src/module-cache.js +956764: package.json +2866883: node_modules/semver/package.json +2868219: node_modules/semver/semver.js +956764: package.json +2397840: node_modules/season/lib/cson.js +94886363: src/secondary-window-bootstrap.js +2901212: node_modules/bluebird/js/main/bluebird.js +2901506: node_modules/bluebird/js/main/promise.js +2926597: node_modules/bluebird/js/main/util.js +2935230: node_modules/bluebird/js/main/es5.js +2397840: node_modules/season/lib/cson.js +2937208: node_modules/bluebird/js/main/async.js +2925857: src/window-bootstrap.js +2941161: node_modules/bluebird/js/main/schedule.js +2901212: node_modules/bluebird/js/main/bluebird.js +2901506: node_modules/bluebird/js/main/promise.js +2942440: node_modules/bluebird/js/main/queue.js +2944794: node_modules/bluebird/js/main/errors.js +2948412: node_modules/bluebird/js/main/thenables.js +2950756: node_modules/bluebird/js/main/promise_array.js +2926597: node_modules/bluebird/js/main/util.js +2954903: node_modules/bluebird/js/main/captured_trace.js +2935230: node_modules/bluebird/js/main/es5.js +2397840: node_modules/season/lib/cson.js +2937208: node_modules/bluebird/js/main/async.js +94886363: src/secondary-window-bootstrap.js +2941161: node_modules/bluebird/js/main/schedule.js +2901212: node_modules/bluebird/js/main/bluebird.js +2942440: node_modules/bluebird/js/main/queue.js +2901506: node_modules/bluebird/js/main/promise.js +2969949: node_modules/bluebird/js/main/debuggability.js +2944794: node_modules/bluebird/js/main/errors.js +2975097: node_modules/bluebird/js/main/context.js +2948412: node_modules/bluebird/js/main/thenables.js +2976035: node_modules/bluebird/js/main/catch_filter.js +2950756: node_modules/bluebird/js/main/promise_array.js +2978109: node_modules/bluebird/js/main/promise_resolver.js +2926597: node_modules/bluebird/js/main/util.js +2954903: node_modules/bluebird/js/main/captured_trace.js +2981972: node_modules/bluebird/js/main/progress.js +2935230: node_modules/bluebird/js/main/es5.js +2984464: node_modules/bluebird/js/main/method.js +2985800: node_modules/bluebird/js/main/bind.js +2987812: node_modules/bluebird/js/main/finally.js +2937208: node_modules/bluebird/js/main/async.js +2990436: node_modules/bluebird/js/main/direct_resolve.js +2991902: node_modules/bluebird/js/main/synchronous_inspection.js +2941161: node_modules/bluebird/js/main/schedule.js +2942440: node_modules/bluebird/js/main/queue.js +2994543: node_modules/bluebird/js/main/join.js +2969949: node_modules/bluebird/js/main/debuggability.js +2998433: node_modules/bluebird/js/main/map.js +2944794: node_modules/bluebird/js/main/errors.js +2975097: node_modules/bluebird/js/main/context.js +3002811: node_modules/bluebird/js/main/cancel.js +2948412: node_modules/bluebird/js/main/thenables.js +2976035: node_modules/bluebird/js/main/catch_filter.js +3004209: node_modules/bluebird/js/main/using.js +2950756: node_modules/bluebird/js/main/promise_array.js +2978109: node_modules/bluebird/js/main/promise_resolver.js +2954903: node_modules/bluebird/js/main/captured_trace.js +3011204: node_modules/bluebird/js/main/generators.js +2981972: node_modules/bluebird/js/main/progress.js +2984464: node_modules/bluebird/js/main/method.js +3015905: node_modules/bluebird/js/main/nodeify.js +2985800: node_modules/bluebird/js/main/bind.js +3017541: node_modules/bluebird/js/main/call_get.js +2987812: node_modules/bluebird/js/main/finally.js +3021885: node_modules/bluebird/js/main/props.js +2990436: node_modules/bluebird/js/main/direct_resolve.js +2991902: node_modules/bluebird/js/main/synchronous_inspection.js +3024057: node_modules/bluebird/js/main/race.js +2994543: node_modules/bluebird/js/main/join.js +3025282: node_modules/bluebird/js/main/reduce.js +2969949: node_modules/bluebird/js/main/debuggability.js +2998433: node_modules/bluebird/js/main/map.js +3030305: node_modules/bluebird/js/main/settle.js +2975097: node_modules/bluebird/js/main/context.js +3031473: node_modules/bluebird/js/main/some.js +2976035: node_modules/bluebird/js/main/catch_filter.js +3002811: node_modules/bluebird/js/main/cancel.js +3004209: node_modules/bluebird/js/main/using.js +2978109: node_modules/bluebird/js/main/promise_resolver.js +3034851: node_modules/bluebird/js/main/promisify.js +2981972: node_modules/bluebird/js/main/progress.js +3011204: node_modules/bluebird/js/main/generators.js +2984464: node_modules/bluebird/js/main/method.js +3046413: node_modules/bluebird/js/main/any.js +3015905: node_modules/bluebird/js/main/nodeify.js +3046834: node_modules/bluebird/js/main/each.js +2985800: node_modules/bluebird/js/main/bind.js +3047132: node_modules/bluebird/js/main/timers.js +3017541: node_modules/bluebird/js/main/call_get.js +2987812: node_modules/bluebird/js/main/finally.js +3048873: node_modules/bluebird/js/main/filter.js +2990436: node_modules/bluebird/js/main/direct_resolve.js +3021885: node_modules/bluebird/js/main/props.js +2991902: node_modules/bluebird/js/main/synchronous_inspection.js +2994543: node_modules/bluebird/js/main/join.js +3024057: node_modules/bluebird/js/main/race.js +3025282: node_modules/bluebird/js/main/reduce.js +2998433: node_modules/bluebird/js/main/map.js +2937208: node_modules/bluebird/js/main/async.js +3049187: node_modules/babel-core/package.json +3030305: node_modules/bluebird/js/main/settle.js +3002811: node_modules/bluebird/js/main/cancel.js +3031473: node_modules/bluebird/js/main/some.js +3004209: node_modules/bluebird/js/main/using.js +3034851: node_modules/bluebird/js/main/promisify.js +3011204: node_modules/bluebird/js/main/generators.js +1973294: src/compile-cache.js +3015905: node_modules/bluebird/js/main/nodeify.js +3046413: node_modules/bluebird/js/main/any.js +3046834: node_modules/bluebird/js/main/each.js +3017541: node_modules/bluebird/js/main/call_get.js +3047132: node_modules/bluebird/js/main/timers.js +3021885: node_modules/bluebird/js/main/props.js +2901506: node_modules/bluebird/js/main/promise.js +3048873: node_modules/bluebird/js/main/filter.js +3024057: node_modules/bluebird/js/main/race.js +3025282: node_modules/bluebird/js/main/reduce.js +2937208: node_modules/bluebird/js/main/async.js +3030305: node_modules/bluebird/js/main/settle.js +2901212: node_modules/bluebird/js/main/bluebird.js +3049187: node_modules/babel-core/package.json +3031473: node_modules/bluebird/js/main/some.js +2926597: node_modules/bluebird/js/main/util.js +3034851: node_modules/bluebird/js/main/promisify.js +3046413: node_modules/bluebird/js/main/any.js +3052709: src/window.js +3046834: node_modules/bluebird/js/main/each.js +3053190: src/nylas-env.js +1973294: src/compile-cache.js +3047132: node_modules/bluebird/js/main/timers.js +3048873: node_modules/bluebird/js/main/filter.js +2901506: node_modules/bluebird/js/main/promise.js +2937208: node_modules/bluebird/js/main/async.js +3049187: node_modules/babel-core/package.json +2901212: node_modules/bluebird/js/main/bluebird.js +2926597: node_modules/bluebird/js/main/util.js +1973294: src/compile-cache.js +2266273: node_modules/emissary/lib/emissary.js +2266555: node_modules/emissary/lib/helpers.js +3052709: src/window.js +2268129: node_modules/emissary/lib/behavior.js +2274072: node_modules/emissary/node_modules/underscore-plus/lib/underscore-plus.js +2901506: node_modules/bluebird/js/main/promise.js +3053190: src/nylas-env.js +2290574: node_modules/emissary/node_modules/underscore-plus/node_modules/underscore/underscore.js +2901212: node_modules/bluebird/js/main/bluebird.js +2926597: node_modules/bluebird/js/main/util.js +2266273: node_modules/emissary/lib/emissary.js +3052709: src/window.js +2266555: node_modules/emissary/lib/helpers.js +3053190: src/nylas-env.js +2268129: node_modules/emissary/lib/behavior.js +2274072: node_modules/emissary/node_modules/underscore-plus/lib/underscore-plus.js +2337929: node_modules/property-accessors/lib/property-accessors.js +2290574: node_modules/emissary/node_modules/underscore-plus/node_modules/underscore/underscore.js +2369463: node_modules/emissary/node_modules/mixto/lib/mixin.js +2343179: node_modules/emissary/lib/signal.js +2352520: node_modules/emissary/lib/emitter.js +2370863: node_modules/emissary/lib/subscriber.js +2374948: node_modules/emissary/lib/subscription.js +2266273: node_modules/emissary/lib/emissary.js +2266555: node_modules/emissary/lib/helpers.js +2268129: node_modules/emissary/lib/behavior.js +2274072: node_modules/emissary/node_modules/underscore-plus/lib/underscore-plus.js +2378208: node_modules/event-kit/lib/event-kit.js +2337929: node_modules/property-accessors/lib/property-accessors.js +2378397: node_modules/event-kit/lib/emitter.js +2369463: node_modules/emissary/node_modules/mixto/lib/mixin.js +2290574: node_modules/emissary/node_modules/underscore-plus/node_modules/underscore/underscore.js +2382101: node_modules/event-kit/lib/disposable.js +2343179: node_modules/emissary/lib/signal.js +2394595: node_modules/event-kit/lib/composite-disposable.js +3088814: node_modules/coffeestack/index.js +2352520: node_modules/emissary/lib/emitter.js +3093983: src/window-event-handler.js +2370863: node_modules/emissary/lib/subscriber.js +2374948: node_modules/emissary/lib/subscription.js +2378208: node_modules/event-kit/lib/event-kit.js +2337929: node_modules/property-accessors/lib/property-accessors.js +3106501: src/styles-element.js +2378397: node_modules/event-kit/lib/emitter.js +2369463: node_modules/emissary/node_modules/mixto/lib/mixin.js +2382101: node_modules/event-kit/lib/disposable.js +94901394: src/store-registry.js +2343179: node_modules/emissary/lib/signal.js +2394595: node_modules/event-kit/lib/composite-disposable.js +3264167: src/serializable-registry.js +3088814: node_modules/coffeestack/index.js +2352520: node_modules/emissary/lib/emitter.js +3093983: src/window-event-handler.js +2207382: src/flux/models/utils.js +2370863: node_modules/emissary/lib/subscriber.js +2374948: node_modules/emissary/lib/subscription.js +8174990: node_modules/moment-timezone/package.json +8177056: node_modules/moment-timezone/index.js +8177170: node_modules/moment-timezone/moment-timezone.js +2378208: node_modules/event-kit/lib/event-kit.js +2378397: node_modules/event-kit/lib/emitter.js +3974612: node_modules/moment/moment.js +3106501: src/styles-element.js +2382101: node_modules/event-kit/lib/disposable.js +2394595: node_modules/event-kit/lib/composite-disposable.js +94901394: src/store-registry.js +3088814: node_modules/coffeestack/index.js +3264167: src/serializable-registry.js +3093983: src/window-event-handler.js +2207382: src/flux/models/utils.js +8174990: node_modules/moment-timezone/package.json +8177056: node_modules/moment-timezone/index.js +8177170: node_modules/moment-timezone/moment-timezone.js +3106501: src/styles-element.js +3974612: node_modules/moment/moment.js +94901394: src/store-registry.js +3264167: src/serializable-registry.js +2207382: src/flux/models/utils.js +8174990: node_modules/moment-timezone/package.json +8177056: node_modules/moment-timezone/index.js +8177170: node_modules/moment-timezone/moment-timezone.js +3974612: node_modules/moment/moment.js +8190929: node_modules/moment-timezone/data/packed/latest.json +8190929: node_modules/moment-timezone/data/packed/latest.json +8190929: node_modules/moment-timezone/data/packed/latest.json +3261374: src/task-registry.js +3271782: src/database-object-registry.js +3113921: src/flux/errors.js +3261374: src/task-registry.js +3271782: src/database-object-registry.js +3113921: src/flux/errors.js +3261374: src/task-registry.js +1918089: src/error-logger.js +1918089: src/error-logger.js +3271782: src/database-object-registry.js +3113921: src/flux/errors.js +1928640: src/error-logger-extensions/nylas-private-error-reporter.js +1933337: node_modules/raven/package.json +1928640: src/error-logger-extensions/nylas-private-error-reporter.js +1935222: node_modules/raven/index.js +1935631: node_modules/raven/lib/client.js +1933337: node_modules/raven/package.json +1935222: node_modules/raven/index.js +1941308: node_modules/raven/lib/parsers.js +1935631: node_modules/raven/lib/client.js +1946217: node_modules/raven/node_modules/cookie/index.js +1948116: node_modules/raven/lib/utils.js +1941308: node_modules/raven/lib/parsers.js +1953762: node_modules/raven/lib/transports.js +1946217: node_modules/raven/node_modules/cookie/index.js +1948116: node_modules/raven/lib/utils.js +1953762: node_modules/raven/lib/transports.js +1918089: src/error-logger.js +1928640: src/error-logger-extensions/nylas-private-error-reporter.js +1933337: node_modules/raven/package.json +1935222: node_modules/raven/index.js +1935631: node_modules/raven/lib/client.js +1941308: node_modules/raven/lib/parsers.js +1946217: node_modules/raven/node_modules/cookie/index.js +1948116: node_modules/raven/lib/utils.js +1953762: node_modules/raven/lib/transports.js +1957254: node_modules/raven/node_modules/lsmod/index.js +1957254: node_modules/raven/node_modules/lsmod/index.js +1960042: node_modules/raven/node_modules/stack-trace/lib/stack-trace.js +1960042: node_modules/raven/node_modules/stack-trace/lib/stack-trace.js +1964629: node_modules/node-uuid/uuid.js +1964629: node_modules/node-uuid/uuid.js +1933337: node_modules/raven/package.json +1933337: node_modules/raven/package.json +1972642: node_modules/raven/lib/middleware/connect.js +1972642: node_modules/raven/lib/middleware/connect.js +1957254: node_modules/raven/node_modules/lsmod/index.js +1960042: node_modules/raven/node_modules/stack-trace/lib/stack-trace.js +1964629: node_modules/node-uuid/uuid.js +1933337: node_modules/raven/package.json +1972642: node_modules/raven/lib/middleware/connect.js +2238228: src/config.js +2238228: src/config.js +2261295: src/config-utils.js +2261295: src/config-utils.js +2444020: node_modules/pathwatcher/lib/main.js +2444020: node_modules/pathwatcher/lib/main.js +2450494: node_modules/pathwatcher/lib/file.js +2450494: node_modules/pathwatcher/lib/file.js +2530619: node_modules/pathwatcher/lib/directory.js +2530619: node_modules/pathwatcher/lib/directory.js +2572883: src/color.js +2572883: src/color.js +2238228: src/config.js +3115571: src/keymap-manager.js +3115571: src/keymap-manager.js +2261295: src/config-utils.js +72365667: node_modules/mousetrap/mousetrap.js +72365667: node_modules/mousetrap/mousetrap.js +2444020: node_modules/pathwatcher/lib/main.js +3122755: src/command-registry.js +3122755: src/command-registry.js +3153088: src/package-manager.js +3153088: src/package-manager.js +2450494: node_modules/pathwatcher/lib/file.js +3176053: node_modules/q/q.js +3176053: node_modules/q/q.js +2530619: node_modules/pathwatcher/lib/directory.js +2572883: src/color.js +3115571: src/keymap-manager.js +72365667: node_modules/mousetrap/mousetrap.js +3122755: src/command-registry.js +3176053: node_modules/q/q.js +3176053: node_modules/q/q.js +3153088: src/package-manager.js +3153088: src/package-manager.js +3153088: src/package-manager.js +3053190: src/nylas-env.js +3053190: src/nylas-env.js +3176053: node_modules/q/q.js +94886363: src/secondary-window-bootstrap.js +2925857: src/window-bootstrap.js +3238845: src/package.js +3238845: src/package.js +2407083: node_modules/async/lib/async.js +2407083: node_modules/async/lib/async.js +3176053: node_modules/q/q.js +3274725: src/theme-package.js +3274725: src/theme-package.js +3276790: src/flux/stores/database-store.js +3276790: src/flux/stores/database-store.js +3153088: src/package-manager.js +3053190: src/nylas-env.js +94886363: src/secondary-window-bootstrap.js +2405266: node_modules/async/package.json +2405266: node_modules/async/package.json +3305440: node_modules/sqlite3/package.json +3305440: node_modules/sqlite3/package.json +3317559: node_modules/sqlite3/lib/sqlite3.js +3317559: node_modules/sqlite3/lib/sqlite3.js +3323109: node_modules/node-pre-gyp/lib/node-pre-gyp.js +3323109: node_modules/node-pre-gyp/lib/node-pre-gyp.js +3238845: src/package.js +74661469: node_modules/nopt/lib/nopt.js +74661469: node_modules/nopt/lib/nopt.js +41220040: node_modules/abbrev/abbrev.js +41220040: node_modules/abbrev/abbrev.js +2407083: node_modules/async/lib/async.js +74736261: node_modules/npmlog/log.js +74736261: node_modules/npmlog/log.js +41757580: node_modules/are-we-there-yet/index.js +41757580: node_modules/are-we-there-yet/index.js +41760200: node_modules/are-we-there-yet/tracker-group.js +41760200: node_modules/are-we-there-yet/tracker-group.js +41759926: node_modules/are-we-there-yet/tracker-base.js +41759926: node_modules/are-we-there-yet/tracker-base.js +41764374: node_modules/are-we-there-yet/tracker.js +41764374: node_modules/are-we-there-yet/tracker.js +41763431: node_modules/are-we-there-yet/tracker-stream.js +41763431: node_modules/are-we-there-yet/tracker-stream.js +3327439: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/readable.js +3327439: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/readable.js +3327909: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/lib/_stream_readable.js +3274725: src/theme-package.js +3327909: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/lib/_stream_readable.js +3276790: src/flux/stores/database-store.js +56978227: node_modules/falafel/node_modules/isarray/index.js +56978227: node_modules/falafel/node_modules/isarray/index.js +56174974: node_modules/core-util-is/lib/util.js +56174974: node_modules/core-util-is/lib/util.js +3357009: node_modules/babel-core/node_modules/regenerator/node_modules/commoner/node_modules/glob/node_modules/inherits/inherits.js +3357009: node_modules/babel-core/node_modules/regenerator/node_modules/commoner/node_modules/glob/node_modules/inherits/inherits.js +3357051: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/lib/_stream_writable.js +2405266: node_modules/async/package.json +3357051: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/lib/_stream_writable.js +3305440: node_modules/sqlite3/package.json +3317559: node_modules/sqlite3/lib/sqlite3.js +3370120: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/lib/_stream_duplex.js +3370120: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/lib/_stream_duplex.js +3372931: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/lib/_stream_transform.js +3323109: node_modules/node-pre-gyp/lib/node-pre-gyp.js +3372931: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/lib/_stream_transform.js +3380281: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/lib/_stream_passthrough.js +74661469: node_modules/nopt/lib/nopt.js +3380281: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/lib/_stream_passthrough.js +56223160: node_modules/delegates/index.js +56223160: node_modules/delegates/index.js +41220040: node_modules/abbrev/abbrev.js +57167856: node_modules/gauge/progress-bar.js +57167856: node_modules/gauge/progress-bar.js +74736261: node_modules/npmlog/log.js +57204450: node_modules/has-unicode/index.js +57204450: node_modules/has-unicode/index.js +41738129: node_modules/ansi/lib/ansi.js +41738129: node_modules/ansi/lib/ansi.js +41757580: node_modules/are-we-there-yet/index.js +41760200: node_modules/are-we-there-yet/tracker-group.js +41759926: node_modules/are-we-there-yet/tracker-base.js +41746103: node_modules/ansi/lib/newlines.js +41746103: node_modules/ansi/lib/newlines.js +41764374: node_modules/are-we-there-yet/tracker.js +41763431: node_modules/are-we-there-yet/tracker-stream.js +3327439: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/readable.js +67599487: node_modules/lodash.pad/index.js +67599487: node_modules/lodash.pad/index.js +3327909: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/lib/_stream_readable.js +67593383: node_modules/lodash._baseslice/index.js +67593383: node_modules/lodash._baseslice/index.js +67646936: node_modules/lodash.tostring/index.js +67646936: node_modules/lodash.tostring/index.js +56978227: node_modules/falafel/node_modules/isarray/index.js +56174974: node_modules/core-util-is/lib/util.js +67615561: node_modules/lodash.padend/index.js +67615561: node_modules/lodash.padend/index.js +3357009: node_modules/babel-core/node_modules/regenerator/node_modules/commoner/node_modules/glob/node_modules/inherits/inherits.js +3357051: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/lib/_stream_writable.js +67631576: node_modules/lodash.padstart/index.js +67631576: node_modules/lodash.padstart/index.js +3370120: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/lib/_stream_duplex.js +3372931: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/lib/_stream_transform.js +3380281: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/readable-stream/lib/_stream_passthrough.js +56223160: node_modules/delegates/index.js +57167856: node_modules/gauge/progress-bar.js +3382008: node_modules/node-pre-gyp/lib/pre-binding.js +3382008: node_modules/node-pre-gyp/lib/pre-binding.js +3382833: node_modules/node-pre-gyp/lib/util/versioning.js +57204450: node_modules/has-unicode/index.js +3382833: node_modules/node-pre-gyp/lib/util/versioning.js +41738129: node_modules/ansi/lib/ansi.js +3396883: node_modules/node-pre-gyp/node_modules/semver/semver.js +3396883: node_modules/node-pre-gyp/node_modules/semver/semver.js +41746103: node_modules/ansi/lib/newlines.js +67599487: node_modules/lodash.pad/index.js +67593383: node_modules/lodash._baseslice/index.js +3429402: node_modules/node-pre-gyp/lib/util/abi_crosswalk.json +67646936: node_modules/lodash.tostring/index.js +3429402: node_modules/node-pre-gyp/lib/util/abi_crosswalk.json +3446169: node_modules/node-pre-gyp/package.json +67615561: node_modules/lodash.padend/index.js +3446169: node_modules/node-pre-gyp/package.json +3305440: node_modules/sqlite3/package.json +67631576: node_modules/lodash.padstart/index.js +3305440: node_modules/sqlite3/package.json +3382008: node_modules/node-pre-gyp/lib/pre-binding.js +3382833: node_modules/node-pre-gyp/lib/util/versioning.js +3396883: node_modules/node-pre-gyp/node_modules/semver/semver.js +3449351: src/flux/models/model.js +3449351: src/flux/models/model.js +3454751: src/flux/attributes.js +3454751: src/flux/attributes.js +3458403: src/flux/attributes/matcher.js +3458403: src/flux/attributes/matcher.js +3429402: node_modules/node-pre-gyp/lib/util/abi_crosswalk.json +3446169: node_modules/node-pre-gyp/package.json +3305440: node_modules/sqlite3/package.json +3470821: src/flux/attributes/sort-order.js +3470821: src/flux/attributes/sort-order.js +3471723: src/flux/attributes/attribute.js +3471723: src/flux/attributes/attribute.js +3474483: src/flux/attributes/attribute-number.js +3474483: src/flux/attributes/attribute-number.js +3477709: src/flux/attributes/attribute-string.js +3477709: src/flux/attributes/attribute-string.js +3479579: src/flux/attributes/attribute-object.js +3479579: src/flux/attributes/attribute-object.js +3481139: src/flux/attributes/attribute-boolean.js +3481139: src/flux/attributes/attribute-boolean.js +3482541: src/flux/attributes/attribute-datetime.js +3482541: src/flux/attributes/attribute-datetime.js +3485716: src/flux/attributes/attribute-collection.js +3485716: src/flux/attributes/attribute-collection.js +3490067: src/flux/attributes/attribute-joined-data.js +3490067: src/flux/attributes/attribute-joined-data.js +3492659: src/flux/attributes/attribute-serverid.js +3492659: src/flux/attributes/attribute-serverid.js +3494243: src/flux/actions.js +3449351: src/flux/models/model.js +3494243: src/flux/actions.js +3509768: node_modules/reflux/package.json +3454751: src/flux/attributes.js +3509768: node_modules/reflux/package.json +3511690: node_modules/reflux/src/index.js +3458403: src/flux/attributes/matcher.js +3511690: node_modules/reflux/src/index.js +3513193: node_modules/reflux/src/ListenerMethods.js +3513193: node_modules/reflux/src/ListenerMethods.js +3519984: node_modules/reflux/src/utils.js +3519984: node_modules/reflux/src/utils.js +3521383: node_modules/reflux/node_modules/eventemitter3/index.js +3521383: node_modules/reflux/node_modules/eventemitter3/index.js +3470821: src/flux/attributes/sort-order.js +3527461: node_modules/reflux/src/joins.js +3471723: src/flux/attributes/attribute.js +3527461: node_modules/reflux/src/joins.js +3530350: node_modules/reflux/src/createStore.js +3530350: node_modules/reflux/src/createStore.js +3474483: src/flux/attributes/attribute-number.js +3531848: node_modules/reflux/src/Keep.js +3477709: src/flux/attributes/attribute-string.js +3531848: node_modules/reflux/src/Keep.js +3479579: src/flux/attributes/attribute-object.js +3532111: node_modules/reflux/src/PublisherMethods.js +3532111: node_modules/reflux/src/PublisherMethods.js +3481139: src/flux/attributes/attribute-boolean.js +3534287: node_modules/reflux/src/createAction.js +3534287: node_modules/reflux/src/createAction.js +3482541: src/flux/attributes/attribute-datetime.js +3535424: node_modules/reflux/src/connect.js +3535424: node_modules/reflux/src/connect.js +3485716: src/flux/attributes/attribute-collection.js +3536186: node_modules/reflux/src/ListenerMixin.js +3536186: node_modules/reflux/src/ListenerMixin.js +3490067: src/flux/attributes/attribute-joined-data.js +3536620: node_modules/reflux/src/listenTo.js +3536620: node_modules/reflux/src/listenTo.js +3538124: node_modules/reflux/src/listenToMany.js +3492659: src/flux/attributes/attribute-serverid.js +3538124: node_modules/reflux/src/listenToMany.js +3494243: src/flux/actions.js +3509768: node_modules/reflux/package.json +3511690: node_modules/reflux/src/index.js +3513193: node_modules/reflux/src/ListenerMethods.js +3519984: node_modules/reflux/src/utils.js +3521383: node_modules/reflux/node_modules/eventemitter3/index.js +3527461: node_modules/reflux/src/joins.js +3530350: node_modules/reflux/src/createStore.js +3531848: node_modules/reflux/src/Keep.js +3532111: node_modules/reflux/src/PublisherMethods.js +3534287: node_modules/reflux/src/createAction.js +3535424: node_modules/reflux/src/connect.js +3539478: src/flux/models/query.js +3539478: src/flux/models/query.js +3536186: node_modules/reflux/src/ListenerMixin.js +3536620: node_modules/reflux/src/listenTo.js +3538124: node_modules/reflux/src/listenToMany.js +3553781: src/flux/models/query-range.js +3553781: src/flux/models/query-range.js +3556781: src/global/nylas-store.js +3556781: src/global/nylas-store.js +3557281: src/flux/modules/reflux-coffee.js +3557281: src/flux/modules/reflux-coffee.js +3564303: node_modules/underscore.string/package.json +3564303: node_modules/underscore.string/package.json +3126085: node_modules/underscore.string/index.js +3126085: node_modules/underscore.string/index.js +3130619: node_modules/underscore.string/isBlank.js +3130619: node_modules/underscore.string/isBlank.js +3130755: node_modules/underscore.string/helper/makeString.js +3130755: node_modules/underscore.string/helper/makeString.js +3130916: node_modules/underscore.string/stripTags.js +3130916: node_modules/underscore.string/stripTags.js +3131065: node_modules/underscore.string/capitalize.js +3131065: node_modules/underscore.string/capitalize.js +3131341: node_modules/underscore.string/decapitalize.js +3131341: node_modules/underscore.string/decapitalize.js +3131518: node_modules/underscore.string/chop.js +3131518: node_modules/underscore.string/chop.js +3131710: node_modules/underscore.string/trim.js +3131710: node_modules/underscore.string/trim.js +3132143: node_modules/underscore.string/helper/defaultToWhiteSpace.js +3132143: node_modules/underscore.string/helper/defaultToWhiteSpace.js +3132413: node_modules/underscore.string/helper/escapeRegExp.js +3132413: node_modules/underscore.string/helper/escapeRegExp.js +3132577: node_modules/underscore.string/clean.js +3132577: node_modules/underscore.string/clean.js +3132693: node_modules/underscore.string/cleanDiacritics.js +3132693: node_modules/underscore.string/cleanDiacritics.js +3133280: node_modules/underscore.string/count.js +3133280: node_modules/underscore.string/count.js +3133530: node_modules/underscore.string/chars.js +3133530: node_modules/underscore.string/chars.js +3539478: src/flux/models/query.js +3133658: node_modules/underscore.string/swapCase.js +3133883: node_modules/underscore.string/escapeHTML.js +3133658: node_modules/underscore.string/swapCase.js +3134273: node_modules/underscore.string/helper/escapeChars.js +3133883: node_modules/underscore.string/escapeHTML.js +3134692: node_modules/underscore.string/unescapeHTML.js +3553781: src/flux/models/query-range.js +3134273: node_modules/underscore.string/helper/escapeChars.js +3135351: node_modules/underscore.string/helper/htmlEntities.js +3134692: node_modules/underscore.string/unescapeHTML.js +3135655: node_modules/underscore.string/splice.js +3135351: node_modules/underscore.string/helper/htmlEntities.js +3556781: src/global/nylas-store.js +3135836: node_modules/underscore.string/insert.js +3135655: node_modules/underscore.string/splice.js +3135961: node_modules/underscore.string/replaceAll.js +3135836: node_modules/underscore.string/insert.js +3136217: node_modules/underscore.string/include.js +3557281: src/flux/modules/reflux-coffee.js +3135961: node_modules/underscore.string/replaceAll.js +3136402: node_modules/underscore.string/join.js +3136217: node_modules/underscore.string/include.js +3136622: node_modules/underscore.string/lines.js +3136402: node_modules/underscore.string/join.js +3136734: node_modules/underscore.string/dedent.js +3136622: node_modules/underscore.string/lines.js +3136734: node_modules/underscore.string/dedent.js +3564303: node_modules/underscore.string/package.json +3137344: node_modules/underscore.string/reverse.js +3126085: node_modules/underscore.string/index.js +3137461: node_modules/underscore.string/startsWith.js +3137811: node_modules/underscore.string/helper/toPositive.js +3130619: node_modules/underscore.string/isBlank.js +3137903: node_modules/underscore.string/endsWith.js +3130755: node_modules/underscore.string/helper/makeString.js +3137344: node_modules/underscore.string/reverse.js +3138345: node_modules/underscore.string/pred.js +3130916: node_modules/underscore.string/stripTags.js +3138460: node_modules/underscore.string/helper/adjacent.js +3137461: node_modules/underscore.string/startsWith.js +3131065: node_modules/underscore.string/capitalize.js +3137811: node_modules/underscore.string/helper/toPositive.js +3131341: node_modules/underscore.string/decapitalize.js +3138722: node_modules/underscore.string/succ.js +3137903: node_modules/underscore.string/endsWith.js +3131518: node_modules/underscore.string/chop.js +3138836: node_modules/underscore.string/titleize.js +3131710: node_modules/underscore.string/trim.js +3138345: node_modules/underscore.string/pred.js +3139043: node_modules/underscore.string/camelize.js +3132143: node_modules/underscore.string/helper/defaultToWhiteSpace.js +3138460: node_modules/underscore.string/helper/adjacent.js +3139364: node_modules/underscore.string/underscored.js +3132413: node_modules/underscore.string/helper/escapeRegExp.js +3139540: node_modules/underscore.string/dasherize.js +3132577: node_modules/underscore.string/clean.js +3139703: node_modules/underscore.string/classify.js +3132693: node_modules/underscore.string/cleanDiacritics.js +3139981: node_modules/underscore.string/humanize.js +3138722: node_modules/underscore.string/succ.js +3140227: node_modules/underscore.string/ltrim.js +3138836: node_modules/underscore.string/titleize.js +3140651: node_modules/underscore.string/rtrim.js +3133280: node_modules/underscore.string/count.js +3139043: node_modules/underscore.string/camelize.js +3141074: node_modules/underscore.string/truncate.js +3133530: node_modules/underscore.string/chars.js +3141347: node_modules/underscore.string/prune.js +3139364: node_modules/underscore.string/underscored.js +3133658: node_modules/underscore.string/swapCase.js +3139540: node_modules/underscore.string/dasherize.js +3142255: node_modules/underscore.string/words.js +3139703: node_modules/underscore.string/classify.js +3142463: node_modules/underscore.string/pad.js +3133883: node_modules/underscore.string/escapeHTML.js +3139981: node_modules/underscore.string/humanize.js +3143150: node_modules/underscore.string/helper/strRepeat.js +3134273: node_modules/underscore.string/helper/escapeChars.js +3143345: node_modules/underscore.string/lpad.js +3140227: node_modules/underscore.string/ltrim.js +3143466: node_modules/underscore.string/rpad.js +3134692: node_modules/underscore.string/unescapeHTML.js +3140651: node_modules/underscore.string/rtrim.js +3143596: node_modules/underscore.string/lrpad.js +3135351: node_modules/underscore.string/helper/htmlEntities.js +3141074: node_modules/underscore.string/truncate.js +3135655: node_modules/underscore.string/splice.js +3141347: node_modules/underscore.string/prune.js +3143726: node_modules/underscore.string/sprintf.js +3135836: node_modules/underscore.string/insert.js +4476299: node_modules/juice/node_modules/util-deprecate/node.js +3142255: node_modules/underscore.string/words.js +3135961: node_modules/underscore.string/replaceAll.js +3142463: node_modules/underscore.string/pad.js +89722232: node_modules/underscore.string/node_modules/sprintf-js/src/sprintf.js +3136217: node_modules/underscore.string/include.js +3143150: node_modules/underscore.string/helper/strRepeat.js +3136402: node_modules/underscore.string/join.js +3143345: node_modules/underscore.string/lpad.js +3136622: node_modules/underscore.string/lines.js +3143466: node_modules/underscore.string/rpad.js +3136734: node_modules/underscore.string/dedent.js +3143923: node_modules/underscore.string/vsprintf.js +3143596: node_modules/underscore.string/lrpad.js +3137344: node_modules/underscore.string/reverse.js +3144122: node_modules/underscore.string/toNumber.js +3143726: node_modules/underscore.string/sprintf.js +3137461: node_modules/underscore.string/startsWith.js +3144317: node_modules/underscore.string/numberFormat.js +4476299: node_modules/juice/node_modules/util-deprecate/node.js +3137811: node_modules/underscore.string/helper/toPositive.js +3144704: node_modules/underscore.string/strRight.js +3137903: node_modules/underscore.string/endsWith.js +3144959: node_modules/underscore.string/strRightBack.js +89722232: node_modules/underscore.string/node_modules/sprintf-js/src/sprintf.js +3138345: node_modules/underscore.string/pred.js +3145222: node_modules/underscore.string/strLeft.js +3138460: node_modules/underscore.string/helper/adjacent.js +3145454: node_modules/underscore.string/strLeftBack.js +3145682: node_modules/underscore.string/toSentence.js +3143923: node_modules/underscore.string/vsprintf.js +3138722: node_modules/underscore.string/succ.js +3146093: node_modules/underscore.string/toSentenceSerial.js +3144122: node_modules/underscore.string/toNumber.js +3144317: node_modules/underscore.string/numberFormat.js +3138836: node_modules/underscore.string/titleize.js +3146253: node_modules/underscore.string/slugify.js +3144704: node_modules/underscore.string/strRight.js +3139043: node_modules/underscore.string/camelize.js +3146513: node_modules/underscore.string/surround.js +3144959: node_modules/underscore.string/strRightBack.js +3139364: node_modules/underscore.string/underscored.js +3146610: node_modules/underscore.string/quote.js +3145222: node_modules/underscore.string/strLeft.js +3139540: node_modules/underscore.string/dasherize.js +3146744: node_modules/underscore.string/unquote.js +3145454: node_modules/underscore.string/strLeftBack.js +3139703: node_modules/underscore.string/classify.js +3146956: node_modules/underscore.string/repeat.js +3145682: node_modules/underscore.string/toSentence.js +3147436: node_modules/underscore.string/naturalCmp.js +3139981: node_modules/underscore.string/humanize.js +3146093: node_modules/underscore.string/toSentenceSerial.js +3148137: node_modules/underscore.string/levenshtein.js +3140227: node_modules/underscore.string/ltrim.js +3140651: node_modules/underscore.string/rtrim.js +3149427: node_modules/underscore.string/toBoolean.js +3146253: node_modules/underscore.string/slugify.js +3150095: node_modules/underscore.string/exports.js +3141074: node_modules/underscore.string/truncate.js +3150336: node_modules/underscore.string/wrap.js +3146513: node_modules/underscore.string/surround.js +3141347: node_modules/underscore.string/prune.js +3146610: node_modules/underscore.string/quote.js +3152857: node_modules/underscore.string/map.js +3142255: node_modules/underscore.string/words.js +3146744: node_modules/underscore.string/unquote.js +3142463: node_modules/underscore.string/pad.js +3146956: node_modules/underscore.string/repeat.js +3143150: node_modules/underscore.string/helper/strRepeat.js +3147436: node_modules/underscore.string/naturalCmp.js +3143345: node_modules/underscore.string/lpad.js +3148137: node_modules/underscore.string/levenshtein.js +3568295: src/flux/coffee-helpers.js +3143466: node_modules/underscore.string/rpad.js +3149427: node_modules/underscore.string/toBoolean.js +3143596: node_modules/underscore.string/lrpad.js +3150095: node_modules/underscore.string/exports.js +3143726: node_modules/underscore.string/sprintf.js +3150336: node_modules/underscore.string/wrap.js +4476299: node_modules/juice/node_modules/util-deprecate/node.js +3152857: node_modules/underscore.string/map.js +3570836: node_modules/promise-queue/package.json +89722232: node_modules/underscore.string/node_modules/sprintf-js/src/sprintf.js +3572457: node_modules/promise-queue/index.js +3568295: src/flux/coffee-helpers.js +3572560: node_modules/promise-queue/lib/index.js +3143923: node_modules/underscore.string/vsprintf.js +3144122: node_modules/underscore.string/toNumber.js +3144317: node_modules/underscore.string/numberFormat.js +3577195: src/priority-ui-coordinator.js +3144704: node_modules/underscore.string/strRight.js +3144959: node_modules/underscore.string/strRightBack.js +3570836: node_modules/promise-queue/package.json +3145222: node_modules/underscore.string/strLeft.js +3579245: src/flux/stores/database-setup-query-builder.js +3572457: node_modules/promise-queue/index.js +3145454: node_modules/underscore.string/strLeftBack.js +3145682: node_modules/underscore.string/toSentence.js +3583641: src/flux/stores/database-change-record.js +3572560: node_modules/promise-queue/lib/index.js +3146093: node_modules/underscore.string/toSentenceSerial.js +3585323: src/flux/stores/database-writer.js +3146253: node_modules/underscore.string/slugify.js +3577195: src/priority-ui-coordinator.js +3146513: node_modules/underscore.string/surround.js +3146610: node_modules/underscore.string/quote.js +3146744: node_modules/underscore.string/unquote.js +3146956: node_modules/underscore.string/repeat.js +3579245: src/flux/stores/database-setup-query-builder.js +3147436: node_modules/underscore.string/naturalCmp.js +3148137: node_modules/underscore.string/levenshtein.js +3583641: src/flux/stores/database-change-record.js +3149427: node_modules/underscore.string/toBoolean.js +3150095: node_modules/underscore.string/exports.js +3585323: src/flux/stores/database-writer.js +3150336: node_modules/underscore.string/wrap.js +3152857: node_modules/underscore.string/map.js +3568295: src/flux/coffee-helpers.js +3599638: src/apm-wrapper.js +3570836: node_modules/promise-queue/package.json +3572457: node_modules/promise-queue/index.js +3613961: src/buffered-process.js +3572560: node_modules/promise-queue/lib/index.js +3577195: src/priority-ui-coordinator.js +3599638: src/apm-wrapper.js +3621867: src/clipboard.js +3579245: src/flux/stores/database-setup-query-builder.js +3583641: src/flux/stores/database-change-record.js +3613961: src/buffered-process.js +3622937: src/theme-manager.js +3585323: src/flux/stores/database-writer.js +3621867: src/clipboard.js +3642517: src/style-manager.js +3648228: src/flux/action-bridge.js +3622937: src/theme-manager.js +3654791: src/menu-manager.js +3658911: src/menu-helpers.js +3642517: src/style-manager.js +3648228: src/flux/action-bridge.js +3599638: src/apm-wrapper.js +3654791: src/menu-manager.js +3658911: src/menu-helpers.js +3613961: src/buffered-process.js +3621867: src/clipboard.js +3622937: src/theme-manager.js +3677020: menus/darwin.json +3642517: src/style-manager.js +3648228: src/flux/action-bridge.js +3654791: src/menu-manager.js +3677020: menus/darwin.json +3662409: src/nylas-spellchecker.js +3658911: src/menu-helpers.js +3662409: src/nylas-spellchecker.js +3677020: menus/darwin.json +3662409: src/nylas-spellchecker.js +3798011: src/global/nylas-exports.js +3798011: src/global/nylas-exports.js +5912469: src/deprecate-utils.js +3668481: src/config-schema.js +5912469: src/deprecate-utils.js +3798011: src/global/nylas-exports.js +5912469: src/deprecate-utils.js +3668481: src/config-schema.js +4687050: src/global/nylas-observables.js +4697858: node_modules/rx-lite/package.json +4699647: node_modules/rx-lite/rx.lite.js +4687050: src/global/nylas-observables.js +4697858: node_modules/rx-lite/package.json +4699647: node_modules/rx-lite/rx.lite.js +4699647: node_modules/rx-lite/rx.lite.js +4687050: src/global/nylas-observables.js +4699647: node_modules/rx-lite/rx.lite.js +4687050: src/global/nylas-observables.js +3925280: src/flux/models/category.js +5180618: src/flux/models/query-subscription-pool.js +5195386: src/flux/models/query-subscription.js +5231737: src/flux/models/mutable-query-result-set.js +5237299: src/flux/models/query-result-set.js +5389575: src/flux/stores/task-queue.js +3925280: src/flux/models/category.js +5180618: src/flux/models/query-subscription-pool.js +3813280: src/flux/tasks/task.js +5195386: src/flux/models/query-subscription.js +5231737: src/flux/models/mutable-query-result-set.js +4200026: src/flux/nylas-api.js +5237299: src/flux/models/query-result-set.js +5389575: src/flux/stores/task-queue.js +4222287: node_modules/request/package.json +4225420: node_modules/request/index.js +80787899: node_modules/request/node_modules/extend/index.js +4229450: node_modules/request/lib/cookies.js +4230419: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/cookie.js +3813280: src/flux/tasks/task.js +4267962: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/pubsuffix.js +4200026: src/flux/nylas-api.js +4222287: node_modules/request/package.json +4225420: node_modules/request/index.js +80787899: node_modules/request/node_modules/extend/index.js +4229450: node_modules/request/lib/cookies.js +4230419: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/cookie.js +4267962: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/pubsuffix.js +4417589: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/store.js +4420430: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/memstore.js +4425944: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/permuteDomain.js +4428210: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/pathMatch.js +4430645: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/package.json +4432858: node_modules/request/lib/helpers.js +4434482: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/json-stringify-safe/stringify.js +4435389: node_modules/request/request.js +4417589: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/store.js +80567623: node_modules/request/node_modules/bl/bl.js +4420430: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/memstore.js +4425944: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/permuteDomain.js +80636139: node_modules/request/node_modules/bl/node_modules/readable-stream/duplex.js +4428210: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/pathMatch.js +80636191: node_modules/request/node_modules/bl/node_modules/readable-stream/lib/_stream_duplex.js +4430645: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/package.json +75226954: node_modules/process-nextick-args/index.js +80638643: node_modules/request/node_modules/bl/node_modules/readable-stream/lib/_stream_readable.js +4432858: node_modules/request/lib/helpers.js +4434482: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/json-stringify-safe/stringify.js +4435389: node_modules/request/request.js +57417682: node_modules/isarray/index.js +80670635: node_modules/request/node_modules/bl/node_modules/readable-stream/lib/_stream_writable.js +81345592: node_modules/request/node_modules/hawk/lib/index.js +80567623: node_modules/request/node_modules/bl/bl.js +81416906: node_modules/request/node_modules/hawk/node_modules/boom/lib/index.js +80636139: node_modules/request/node_modules/bl/node_modules/readable-stream/duplex.js +80636191: node_modules/request/node_modules/bl/node_modules/readable-stream/lib/_stream_duplex.js +75226954: node_modules/process-nextick-args/index.js +80638643: node_modules/request/node_modules/bl/node_modules/readable-stream/lib/_stream_readable.js +81490930: node_modules/request/node_modules/hawk/node_modules/hoek/lib/index.js +57417682: node_modules/isarray/index.js +80670635: node_modules/request/node_modules/bl/node_modules/readable-stream/lib/_stream_writable.js +81488262: node_modules/request/node_modules/hawk/node_modules/hoek/lib/escape.js +81520773: node_modules/request/node_modules/hawk/node_modules/sntp/index.js +81345592: node_modules/request/node_modules/hawk/lib/index.js +81416906: node_modules/request/node_modules/hawk/node_modules/boom/lib/index.js +81520807: node_modules/request/node_modules/hawk/node_modules/sntp/lib/index.js +81345973: node_modules/request/node_modules/hawk/lib/server.js +81490930: node_modules/request/node_modules/hawk/node_modules/hoek/lib/index.js +81488262: node_modules/request/node_modules/hawk/node_modules/hoek/lib/escape.js +81520773: node_modules/request/node_modules/hawk/node_modules/sntp/index.js +81520807: node_modules/request/node_modules/hawk/node_modules/sntp/lib/index.js +81428267: node_modules/request/node_modules/hawk/node_modules/cryptiles/lib/index.js +81342006: node_modules/request/node_modules/hawk/lib/crypto.js +81364519: node_modules/request/node_modules/hawk/lib/utils.js +81331415: node_modules/request/node_modules/hawk/lib/client.js +80480692: node_modules/request/node_modules/aws-sign2/index.js +81551606: node_modules/request/node_modules/http-signature/lib/index.js +81552232: node_modules/request/node_modules/http-signature/lib/parser.js +81585919: node_modules/request/node_modules/http-signature/node_modules/assert-plus/assert.js +81345973: node_modules/request/node_modules/hawk/lib/server.js +81428267: node_modules/request/node_modules/hawk/node_modules/cryptiles/lib/index.js +81342006: node_modules/request/node_modules/hawk/lib/crypto.js +81574938: node_modules/request/node_modules/http-signature/lib/utils.js +81364519: node_modules/request/node_modules/hawk/lib/utils.js +81887698: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/index.js +81888429: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/key.js +81331415: node_modules/request/node_modules/hawk/lib/client.js +81950012: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/assert-plus/assert.js +80480692: node_modules/request/node_modules/aws-sign2/index.js +81551606: node_modules/request/node_modules/http-signature/lib/index.js +81552232: node_modules/request/node_modules/http-signature/lib/parser.js +81585919: node_modules/request/node_modules/http-signature/node_modules/assert-plus/assert.js +81830178: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/algs.js +81847974: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/fingerprint.js +81574938: node_modules/request/node_modules/http-signature/lib/utils.js +81845672: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/errors.js +81911179: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/utils.js +81887698: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/index.js +81888429: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/key.js +81895828: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/private-key.js +81950012: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/assert-plus/assert.js +81901963: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/signature.js +81942773: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/index.js +81928490: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/index.js +81830178: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/algs.js +81928251: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/errors.js +81934548: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/types.js +81847974: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/fingerprint.js +81845672: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/errors.js +81928959: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/reader.js +81911179: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/utils.js +81935186: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/writer.js +81895828: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/private-key.js +81901963: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/signature.js +81908010: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/ssh-buffer.js +81942773: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/index.js +81843337: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/ed-compat.js +81928490: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/index.js +81928251: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/errors.js +81851404: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/auto.js +81934548: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/types.js +81853302: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pem.js +81858111: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs1.js +81928959: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/reader.js +81865807: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs8.js +81935186: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/writer.js +81881346: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh-private.js +81877740: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/rfc4253.js +81908010: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/ssh-buffer.js +81843337: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/ed-compat.js +81884606: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh.js +81851404: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/auto.js +81835020: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/dhe.js +81853302: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pem.js +81858111: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs1.js +81562004: node_modules/request/node_modules/http-signature/lib/signer.js +81865807: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs8.js +81602307: node_modules/request/node_modules/http-signature/node_modules/jsprim/lib/jsprim.js +81881346: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh-private.js +81632804: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/extsprintf/lib/extsprintf.js +81877740: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/rfc4253.js +81797823: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/verror/lib/verror.js +81763963: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/json-schema/lib/validate.js +81884606: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh.js +81835020: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/dhe.js +81577777: node_modules/request/node_modules/http-signature/lib/verify.js +82371286: node_modules/request/node_modules/mime-types/index.js +81562004: node_modules/request/node_modules/http-signature/lib/signer.js +82530510: node_modules/request/node_modules/mime-types/node_modules/mime-db/index.js +82387545: node_modules/request/node_modules/mime-types/node_modules/mime-db/db.json +81602307: node_modules/request/node_modules/http-signature/node_modules/jsprim/lib/jsprim.js +81632804: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/extsprintf/lib/extsprintf.js +81797823: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/verror/lib/verror.js +81763963: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/json-schema/lib/validate.js +81577777: node_modules/request/node_modules/http-signature/lib/verify.js +82371286: node_modules/request/node_modules/mime-types/index.js +82530510: node_modules/request/node_modules/mime-types/node_modules/mime-db/index.js +82387545: node_modules/request/node_modules/mime-types/node_modules/mime-db/db.json +4476422: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/stringstream/stringstream.js +80754327: node_modules/request/node_modules/caseless/index.js +80801324: node_modules/request/node_modules/forever-agent/index.js +80814876: node_modules/request/node_modules/form-data/lib/form_data.js +3668481: src/config-schema.js +80764215: node_modules/request/node_modules/combined-stream/lib/combined_stream.js +80773528: node_modules/request/node_modules/combined-stream/node_modules/delayed-stream/lib/delayed_stream.js +80944514: node_modules/request/node_modules/form-data/node_modules/async/lib/async.js +4476422: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/stringstream/stringstream.js +80754327: node_modules/request/node_modules/caseless/index.js +80801324: node_modules/request/node_modules/forever-agent/index.js +80814876: node_modules/request/node_modules/form-data/lib/form_data.js +80826033: node_modules/request/node_modules/form-data/lib/populate.js +82348558: node_modules/request/node_modules/isstream/isstream.js +82341499: node_modules/request/node_modules/is-typedarray/index.js +80764215: node_modules/request/node_modules/combined-stream/lib/combined_stream.js +4479214: node_modules/request/lib/getProxyFromURI.js +4481482: node_modules/request/lib/querystring.js +80773528: node_modules/request/node_modules/combined-stream/node_modules/delayed-stream/lib/delayed_stream.js +82587962: node_modules/request/node_modules/qs/lib/index.js +82593363: node_modules/request/node_modules/qs/lib/stringify.js +82597342: node_modules/request/node_modules/qs/lib/utils.js +80944514: node_modules/request/node_modules/form-data/node_modules/async/lib/async.js +82588115: node_modules/request/node_modules/qs/lib/parse.js +4482815: node_modules/request/lib/har.js +81001381: node_modules/request/node_modules/har-validator/lib/index.js +81163939: node_modules/request/node_modules/har-validator/node_modules/pinkie-promise/index.js +81001935: node_modules/request/node_modules/har-validator/lib/runner.js +80826033: node_modules/request/node_modules/form-data/lib/populate.js +81005359: node_modules/request/node_modules/har-validator/lib/schemas/index.js +81002525: node_modules/request/node_modules/har-validator/lib/schemas/cache.json +82348558: node_modules/request/node_modules/isstream/isstream.js +81002712: node_modules/request/node_modules/har-validator/lib/schemas/cacheEntry.json +81003206: node_modules/request/node_modules/har-validator/lib/schemas/content.json +82341499: node_modules/request/node_modules/is-typedarray/index.js +81003583: node_modules/request/node_modules/har-validator/lib/schemas/cookie.json +81004081: node_modules/request/node_modules/har-validator/lib/schemas/creator.json +4479214: node_modules/request/lib/getProxyFromURI.js +81004311: node_modules/request/node_modules/har-validator/lib/schemas/entry.json +81005242: node_modules/request/node_modules/har-validator/lib/schemas/har.json +4481482: node_modules/request/lib/querystring.js +81007111: node_modules/request/node_modules/har-validator/lib/schemas/log.json +81007604: node_modules/request/node_modules/har-validator/lib/schemas/page.json +82587962: node_modules/request/node_modules/qs/lib/index.js +81008181: node_modules/request/node_modules/har-validator/lib/schemas/pageTimings.json +81008406: node_modules/request/node_modules/har-validator/lib/schemas/postData.json +82593363: node_modules/request/node_modules/qs/lib/stringify.js +81009060: node_modules/request/node_modules/har-validator/lib/schemas/record.json +81009286: node_modules/request/node_modules/har-validator/lib/schemas/request.json +81010139: node_modules/request/node_modules/har-validator/lib/schemas/response.json +82597342: node_modules/request/node_modules/qs/lib/utils.js +81010946: node_modules/request/node_modules/har-validator/lib/schemas/timings.json +81001195: node_modules/request/node_modules/har-validator/lib/error.js +81108729: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/index.js +82588115: node_modules/request/node_modules/qs/lib/parse.js +81132607: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-object-property/index.js +4482815: node_modules/request/lib/har.js +81134601: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-object-property/node_modules/is-property/is-property.js +81001381: node_modules/request/node_modules/har-validator/lib/index.js +81127606: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-function/index.js +81163939: node_modules/request/node_modules/har-validator/node_modules/pinkie-promise/index.js +81149530: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/jsonpointer/jsonpointer.js +81001935: node_modules/request/node_modules/har-validator/lib/runner.js +4525735: node_modules/babel-core/node_modules/output-file-sync/node_modules/xtend/immutable.js +81106380: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/formats.js +81005359: node_modules/request/node_modules/har-validator/lib/schemas/index.js +81002525: node_modules/request/node_modules/har-validator/lib/schemas/cache.json +81002712: node_modules/request/node_modules/har-validator/lib/schemas/cacheEntry.json +4533728: node_modules/request/lib/auth.js +81003206: node_modules/request/node_modules/har-validator/lib/schemas/content.json +81003583: node_modules/request/node_modules/har-validator/lib/schemas/cookie.json +81004081: node_modules/request/node_modules/har-validator/lib/schemas/creator.json +81004311: node_modules/request/node_modules/har-validator/lib/schemas/entry.json +81005242: node_modules/request/node_modules/har-validator/lib/schemas/har.json +81007111: node_modules/request/node_modules/har-validator/lib/schemas/log.json +4550226: node_modules/request/lib/oauth.js +81007604: node_modules/request/node_modules/har-validator/lib/schemas/page.json +81008181: node_modules/request/node_modules/har-validator/lib/schemas/pageTimings.json +81008406: node_modules/request/node_modules/har-validator/lib/schemas/postData.json +81009060: node_modules/request/node_modules/har-validator/lib/schemas/record.json +81009286: node_modules/request/node_modules/har-validator/lib/schemas/request.json +82544802: node_modules/request/node_modules/oauth-sign/index.js +81010139: node_modules/request/node_modules/har-validator/lib/schemas/response.json +81010946: node_modules/request/node_modules/har-validator/lib/schemas/timings.json +81001195: node_modules/request/node_modules/har-validator/lib/error.js +4568756: node_modules/request/lib/multipart.js +81108729: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/index.js +4585628: node_modules/request/lib/redirect.js +4594531: node_modules/request/lib/tunnel.js +81132607: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-object-property/index.js +4601413: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tunnel-agent/index.js +81134601: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-object-property/node_modules/is-property/is-property.js +81127606: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-function/index.js +81149530: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/jsonpointer/jsonpointer.js +91866084: src/flux/nylas-long-connection.js +4525735: node_modules/babel-core/node_modules/output-file-sync/node_modules/xtend/immutable.js +81106380: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/formats.js +2376215: node_modules/event-kit/package.json +4533728: node_modules/request/lib/auth.js +3905749: src/flux/models/account.js +3911874: src/flux/models/model-with-metadata.js +4550226: node_modules/request/lib/oauth.js +82544802: node_modules/request/node_modules/oauth-sign/index.js +4568756: node_modules/request/lib/multipart.js +4585628: node_modules/request/lib/redirect.js +3959955: src/flux/models/message.js +4594531: node_modules/request/lib/tunnel.js +3971136: node_modules/moment/package.json +4601413: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tunnel-agent/index.js +3809622: src/flux/models/file.js +91866084: src/flux/nylas-long-connection.js +3870842: src/flux/models/event.js +3875483: src/flux/models/contact.js +2376215: node_modules/event-kit/package.json +3886010: src/regexp-utils.js +3905749: src/flux/models/account.js +56664169: node_modules/emoji-data/lib/emoji_data.js +3911874: src/flux/models/model-with-metadata.js +56662240: node_modules/emoji-data/lib/emoji_char.js +56691400: node_modules/emoji-data/node_modules/underscore.string/lib/underscore.string.js +3959955: src/flux/models/message.js +3971136: node_modules/moment/package.json +3809622: src/flux/models/file.js +3870842: src/flux/models/event.js +3875483: src/flux/models/contact.js +3886010: src/regexp-utils.js +56664169: node_modules/emoji-data/lib/emoji_data.js +56662240: node_modules/emoji-data/lib/emoji_char.js +56691400: node_modules/emoji-data/node_modules/underscore.string/lib/underscore.string.js +3889367: src/flux/stores/account-store.js +65006241: node_modules/keytar/package.json +65004180: node_modules/keytar/lib/keytar.js +3889367: src/flux/stores/account-store.js +65006241: node_modules/keytar/package.json +65004180: node_modules/keytar/lib/keytar.js +4687050: src/global/nylas-observables.js +4697858: node_modules/rx-lite/package.json +3925080: src/flux/models/label.js +4699647: node_modules/rx-lite/rx.lite.js +3930438: src/flux/models/folder.js +3930638: src/flux/models/thread.js +4096909: src/flux/models/calendar.js +4098495: src/flux/models/json-blob.js +3925080: src/flux/models/label.js +3930438: src/flux/models/folder.js +3930638: src/flux/models/thread.js +4096909: src/flux/models/calendar.js +5951047: src/flux/stores/badge-store.js +5726955: src/flux/stores/focused-perspective-store.js +5744701: src/flux/stores/workspace-store.js +5951047: src/flux/stores/badge-store.js +5726955: src/flux/stores/focused-perspective-store.js +4648657: src/flux/stores/category-store.js +5744701: src/flux/stores/workspace-store.js +9942964: src/flux/stores/nylas-sync-status-store.js +4648657: src/flux/stores/category-store.js +9942964: src/flux/stores/nylas-sync-status-store.js +4699647: node_modules/rx-lite/rx.lite.js +4098495: src/flux/models/json-blob.js +4687050: src/global/nylas-observables.js +4100089: src/mailbox-perspective.js +3925280: src/flux/models/category.js +4120446: src/flux/tasks/task-factory.js +4139229: src/flux/tasks/change-folder-task.js +5180618: src/flux/models/query-subscription-pool.js +4156736: src/flux/tasks/change-mail-task.js +5195386: src/flux/models/query-subscription.js +4100089: src/mailbox-perspective.js +5231737: src/flux/models/mutable-query-result-set.js +4120446: src/flux/tasks/task-factory.js +4636283: src/flux/tasks/syncback-category-task.js +5237299: src/flux/models/query-result-set.js +4139229: src/flux/tasks/change-folder-task.js +5389575: src/flux/stores/task-queue.js +5273907: src/flux/tasks/change-labels-task.js +4156736: src/flux/tasks/change-mail-task.js +5304677: src/flux/tasks/change-unread-task.js +3813280: src/flux/tasks/task.js +4636283: src/flux/tasks/syncback-category-task.js +5341333: src/flux/tasks/change-starred-task.js +4200026: src/flux/nylas-api.js +5350052: src/flux/stores/outbox-store.js +5356198: src/flux/tasks/send-draft-task.js +5273907: src/flux/tasks/change-labels-task.js +5417961: src/sound-registry.js +91943581: src/flux/tasks/base-draft-task.js +4222287: node_modules/request/package.json +5304677: src/flux/tasks/change-unread-task.js +4225420: node_modules/request/index.js +5419733: src/flux/tasks/syncback-metadata-task.js +80787899: node_modules/request/node_modules/extend/index.js +4229450: node_modules/request/lib/cookies.js +5431422: src/flux/tasks/syncback-model-task.js +5341333: src/flux/tasks/change-starred-task.js +4230419: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/cookie.js +91991403: src/flux/tasks/notify-plugins-of-send-task.js +5350052: src/flux/stores/outbox-store.js +5356198: src/flux/tasks/send-draft-task.js +4267962: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/pubsuffix.js +91821594: src/flux/edgehill-api.js +5417961: src/sound-registry.js +5459931: src/flux/tasks/syncback-draft-task.js +91943581: src/flux/tasks/base-draft-task.js +5471010: src/flux/stores/task-queue-status-store.js +5419733: src/flux/tasks/syncback-metadata-task.js +5431422: src/flux/tasks/syncback-model-task.js +91991403: src/flux/tasks/notify-plugins-of-send-task.js +5512912: src/flux/stores/thread-counts-store.js +91821594: src/flux/edgehill-api.js +91919008: src/flux/stores/recently-read-store.js +5459931: src/flux/tasks/syncback-draft-task.js +5501930: src/flux/models/mutable-query-subscription.js +5471010: src/flux/stores/task-queue-status-store.js +91852488: src/flux/models/unread-query-subscription.js +5512912: src/flux/stores/thread-counts-store.js +91919008: src/flux/stores/recently-read-store.js +5501930: src/flux/models/mutable-query-subscription.js +4417589: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/store.js +91852488: src/flux/models/unread-query-subscription.js +4420430: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/memstore.js +4425944: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/permuteDomain.js +4428210: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/lib/pathMatch.js +4430645: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tough-cookie/package.json +5680865: src/flux/stores/draft-store.js +4432858: node_modules/request/lib/helpers.js +4434482: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/json-stringify-safe/stringify.js +4435389: node_modules/request/request.js +5704584: src/flux/stores/draft-editing-session.js +5886976: src/extension-registry.js +5895610: src/extensions/composer-extension-adapter.js +80567623: node_modules/request/node_modules/bl/bl.js +5860903: src/dom-utils.js +80636139: node_modules/request/node_modules/bl/node_modules/readable-stream/duplex.js +80636191: node_modules/request/node_modules/bl/node_modules/readable-stream/lib/_stream_duplex.js +5680865: src/flux/stores/draft-store.js +75226954: node_modules/process-nextick-args/index.js +80638643: node_modules/request/node_modules/bl/node_modules/readable-stream/lib/_stream_readable.js +5913732: src/extensions/extension-utils.js +5915158: src/extensions/message-view-extension-adapter.js +5704584: src/flux/stores/draft-editing-session.js +57417682: node_modules/isarray/index.js +5886976: src/extension-registry.js +91896596: src/flux/stores/draft-factory.js +80670635: node_modules/request/node_modules/bl/node_modules/readable-stream/lib/_stream_writable.js +5895610: src/extensions/composer-extension-adapter.js +5714320: src/flux/stores/contact-store.js +81345592: node_modules/request/node_modules/hawk/lib/index.js +5860903: src/dom-utils.js +81416906: node_modules/request/node_modules/hawk/node_modules/boom/lib/index.js +5723311: src/flux/stores/contact-ranking-store.js +5913732: src/extensions/extension-utils.js +81490930: node_modules/request/node_modules/hawk/node_modules/hoek/lib/index.js +5915158: src/extensions/message-view-extension-adapter.js +5523481: src/window-bridge.js +91896596: src/flux/stores/draft-factory.js +5714320: src/flux/stores/contact-store.js +5917902: src/flux/stores/message-store.js +5723311: src/flux/stores/contact-ranking-store.js +5733766: src/flux/stores/focused-content-store.js +5523481: src/window-bridge.js +5756110: src/services/inline-style-transformer.js +81488262: node_modules/request/node_modules/hawk/node_modules/hoek/lib/escape.js +81520773: node_modules/request/node_modules/hawk/node_modules/sntp/index.js +5917902: src/flux/stores/message-store.js +81520807: node_modules/request/node_modules/hawk/node_modules/sntp/lib/index.js +5758660: src/services/sanitize-transformer.js +5763630: node_modules/sanitize-html/package.json +5733766: src/flux/stores/focused-content-store.js +81345973: node_modules/request/node_modules/hawk/lib/server.js +5765975: node_modules/sanitize-html/index.js +81428267: node_modules/request/node_modules/hawk/node_modules/cryptiles/lib/index.js +5775765: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/lib/index.js +81342006: node_modules/request/node_modules/hawk/lib/crypto.js +81364519: node_modules/request/node_modules/hawk/lib/utils.js +5777529: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/lib/Parser.js +81331415: node_modules/request/node_modules/hawk/lib/client.js +5756110: src/services/inline-style-transformer.js +5785458: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/lib/Tokenizer.js +80480692: node_modules/request/node_modules/aws-sign2/index.js +81551606: node_modules/request/node_modules/http-signature/lib/index.js +81552232: node_modules/request/node_modules/http-signature/lib/parser.js +5810958: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/entities/lib/decode_codepoint.js +81585919: node_modules/request/node_modules/http-signature/node_modules/assert-plus/assert.js +5758660: src/services/sanitize-transformer.js +5811580: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/entities/maps/decode.json +5811878: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/entities/maps/entities.json +5763630: node_modules/sanitize-html/package.json +5765975: node_modules/sanitize-html/index.js +81574938: node_modules/request/node_modules/http-signature/lib/utils.js +5775765: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/lib/index.js +81887698: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/index.js +81888429: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/key.js +5777529: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/lib/Parser.js +81950012: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/assert-plus/assert.js +5852494: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/entities/maps/legacy.json +5785458: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/lib/Tokenizer.js +5854241: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/entities/maps/xml.json +81830178: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/algs.js +5854294: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/domhandler/index.js +5810958: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/entities/lib/decode_codepoint.js +5858733: node_modules/juice/node_modules/cheerio/node_modules/css-select/node_modules/domutils/node_modules/domelementtype/index.js +81847974: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/fingerprint.js +5811580: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/entities/maps/decode.json +5859144: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/domhandler/lib/node.js +5811878: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/entities/maps/entities.json +81845672: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/errors.js +5860059: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/domhandler/lib/element.js +81911179: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/utils.js +5860502: node_modules/sanitize-html/node_modules/regexp-quote/regexp-quote.js +81895828: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/private-key.js +5860597: src/flux/models/message-utils.js +81901963: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/signature.js +92019120: src/flux/tasks/syncback-draft-files-task.js +81942773: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/index.js +92064500: src/multi-request-progress-monitor.js +81928490: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/index.js +5852494: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/entities/maps/legacy.json +81928251: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/errors.js +5555349: src/flux/tasks/destroy-draft-task.js +81934548: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/types.js +81928959: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/reader.js +5854241: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/entities/maps/xml.json +81935186: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/writer.js +5854294: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/domhandler/index.js +81908010: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/ssh-buffer.js +5858733: node_modules/juice/node_modules/cheerio/node_modules/css-select/node_modules/domutils/node_modules/domelementtype/index.js +6145019: src/flux/stores/modal-store.js +5859144: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/domhandler/lib/node.js +81843337: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/ed-compat.js +4488696: node_modules/react/package.json +81851404: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/auto.js +5860059: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/domhandler/lib/element.js +4490634: node_modules/react/react.js +81853302: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pem.js +4490690: node_modules/react/lib/React.js +5860502: node_modules/sanitize-html/node_modules/regexp-quote/regexp-quote.js +79997473: node_modules/react/node_modules/object-assign/index.js +81858111: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs1.js +5860597: src/flux/models/message-utils.js +4503558: node_modules/react/lib/ReactChildren.js +92019120: src/flux/tasks/syncback-draft-files-task.js +4509426: node_modules/react/lib/PooledClass.js +81865807: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs8.js +92064500: src/multi-request-progress-monitor.js +77945402: node_modules/react/node_modules/fbjs/lib/invariant.js +4515504: node_modules/react/lib/ReactElement.js +81881346: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh-private.js +5555349: src/flux/tasks/destroy-draft-task.js +81877740: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/rfc4253.js +4526119: node_modules/react/lib/ReactCurrentOwner.js +77997237: node_modules/react/node_modules/fbjs/lib/warning.js +77865983: node_modules/react/node_modules/fbjs/lib/emptyFunction.js +81884606: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh.js +77610157: node_modules/react/lib/canDefineProperty.js +81835020: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/dhe.js +4526776: node_modules/react/lib/traverseAllChildren.js +6145019: src/flux/stores/modal-store.js +4549078: node_modules/react/lib/getIteratorFn.js +4488696: node_modules/react/package.json +81562004: node_modules/request/node_modules/http-signature/lib/signer.js +4554180: node_modules/react/lib/ReactComponent.js +4490634: node_modules/react/react.js +77543851: node_modules/react/lib/ReactNoopUpdateQueue.js +4490690: node_modules/react/lib/React.js +77541351: node_modules/react/lib/ReactInstrumentation.js +81602307: node_modules/request/node_modules/http-signature/node_modules/jsprim/lib/jsprim.js +77520716: node_modules/react/lib/ReactDebugTool.js +79997473: node_modules/react/node_modules/object-assign/index.js +4503558: node_modules/react/lib/ReactChildren.js +77541813: node_modules/react/lib/ReactInvalidSetStateWarningDevTool.js +81632804: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/extsprintf/lib/extsprintf.js +4509426: node_modules/react/lib/PooledClass.js +77868121: node_modules/react/node_modules/fbjs/lib/emptyObject.js +81797823: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/verror/lib/verror.js +4658325: node_modules/react/lib/ReactClass.js +77945402: node_modules/react/node_modules/fbjs/lib/invariant.js +4515504: node_modules/react/lib/ReactElement.js +81763963: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/json-schema/lib/validate.js +4622568: node_modules/react/lib/ReactPropTypeLocations.js +4526119: node_modules/react/lib/ReactCurrentOwner.js +77955086: node_modules/react/node_modules/fbjs/lib/keyMirror.js +77997237: node_modules/react/node_modules/fbjs/lib/warning.js +81577777: node_modules/request/node_modules/http-signature/lib/verify.js +77865983: node_modules/react/node_modules/fbjs/lib/emptyFunction.js +4623120: node_modules/react/lib/ReactPropTypeLocationNames.js +77610157: node_modules/react/lib/canDefineProperty.js +77961237: node_modules/react/node_modules/fbjs/lib/keyOf.js +82371286: node_modules/request/node_modules/mime-types/index.js +4526776: node_modules/react/lib/traverseAllChildren.js +77505957: node_modules/react/lib/ReactDOMFactories.js +82530510: node_modules/request/node_modules/mime-types/node_modules/mime-db/index.js +4611818: node_modules/react/lib/ReactElementValidator.js +4549078: node_modules/react/lib/getIteratorFn.js +82387545: node_modules/request/node_modules/mime-types/node_modules/mime-db/db.json +4554180: node_modules/react/lib/ReactComponent.js +77963437: node_modules/react/node_modules/fbjs/lib/mapObject.js +77543851: node_modules/react/lib/ReactNoopUpdateQueue.js +77541351: node_modules/react/lib/ReactInstrumentation.js +77520716: node_modules/react/lib/ReactDebugTool.js +77541813: node_modules/react/lib/ReactInvalidSetStateWarningDevTool.js +77868121: node_modules/react/node_modules/fbjs/lib/emptyObject.js +4658325: node_modules/react/lib/ReactClass.js +4622568: node_modules/react/lib/ReactPropTypeLocations.js +77955086: node_modules/react/node_modules/fbjs/lib/keyMirror.js +5328125: node_modules/react/lib/ReactPropTypes.js +4623120: node_modules/react/lib/ReactPropTypeLocationNames.js +77961237: node_modules/react/node_modules/fbjs/lib/keyOf.js +77505957: node_modules/react/lib/ReactDOMFactories.js +77568528: node_modules/react/lib/ReactVersion.js +4611818: node_modules/react/lib/ReactElementValidator.js +5544770: node_modules/react/lib/onlyChild.js +75786540: node_modules/react-dom/package.json +75786477: node_modules/react-dom/index.js +77963437: node_modules/react/node_modules/fbjs/lib/mapObject.js +4476422: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/stringstream/stringstream.js +4693093: node_modules/react/lib/ReactDOM.js +80754327: node_modules/request/node_modules/caseless/index.js +77494923: node_modules/react/lib/ReactDOMComponentTree.js +4954924: node_modules/react/lib/DOMProperty.js +80801324: node_modules/request/node_modules/forever-agent/index.js +77494452: node_modules/react/lib/ReactDOMComponentFlags.js +5154239: node_modules/react/lib/ReactDefaultInjection.js +5157979: node_modules/react/lib/BeforeInputEventPlugin.js +80814876: node_modules/request/node_modules/form-data/lib/form_data.js +4501390: node_modules/react/lib/EventConstants.js +80764215: node_modules/request/node_modules/combined-stream/lib/combined_stream.js +5171831: node_modules/react/lib/EventPropagators.js +80773528: node_modules/request/node_modules/combined-stream/node_modules/delayed-stream/lib/delayed_stream.js +5034693: node_modules/react/lib/EventPluginHub.js +5328125: node_modules/react/lib/ReactPropTypes.js +5042578: node_modules/react/lib/EventPluginRegistry.js +80944514: node_modules/request/node_modules/form-data/node_modules/async/lib/async.js +4493310: node_modules/react/lib/EventPluginUtils.js +77568528: node_modules/react/lib/ReactVersion.js +4684789: node_modules/react/lib/ReactErrorUtils.js +5544770: node_modules/react/lib/onlyChild.js +5051772: node_modules/react/lib/accumulateInto.js +75786540: node_modules/react-dom/package.json +5053515: node_modules/react/lib/forEachAccumulated.js +75786477: node_modules/react-dom/index.js +77682133: node_modules/react/node_modules/fbjs/lib/ExecutionEnvironment.js +4693093: node_modules/react/lib/ReactDOM.js +5177151: node_modules/react/lib/FallbackCompositionState.js +77494923: node_modules/react/lib/ReactDOMComponentTree.js +80826033: node_modules/request/node_modules/form-data/lib/populate.js +5179621: node_modules/react/lib/getTextContentAccessor.js +82348558: node_modules/request/node_modules/isstream/isstream.js +4954924: node_modules/react/lib/DOMProperty.js +5185473: node_modules/react/lib/SyntheticCompositionEvent.js +82341499: node_modules/request/node_modules/is-typedarray/index.js +5186617: node_modules/react/lib/SyntheticEvent.js +77494452: node_modules/react/lib/ReactDOMComponentFlags.js +4479214: node_modules/request/lib/getProxyFromURI.js +5154239: node_modules/react/lib/ReactDefaultInjection.js +4481482: node_modules/request/lib/querystring.js +5157979: node_modules/react/lib/BeforeInputEventPlugin.js +5236171: node_modules/react/lib/SyntheticInputEvent.js +82587962: node_modules/request/node_modules/qs/lib/index.js +82593363: node_modules/request/node_modules/qs/lib/stringify.js +5241892: node_modules/react/lib/ChangeEventPlugin.js +4501390: node_modules/react/lib/EventConstants.js +82597342: node_modules/request/node_modules/qs/lib/utils.js +5171831: node_modules/react/lib/EventPropagators.js +4571389: node_modules/react/lib/ReactUpdates.js +5034693: node_modules/react/lib/EventPluginHub.js +82588115: node_modules/request/node_modules/qs/lib/parse.js +5042578: node_modules/react/lib/EventPluginRegistry.js +4580474: node_modules/react/lib/CallbackQueue.js +4482815: node_modules/request/lib/har.js +77540690: node_modules/react/lib/ReactFeatureFlags.js +4493310: node_modules/react/lib/EventPluginUtils.js +4583141: node_modules/react/lib/ReactPerf.js +81001381: node_modules/request/node_modules/har-validator/lib/index.js +4590060: node_modules/react/lib/ReactReconciler.js +81163939: node_modules/request/node_modules/har-validator/node_modules/pinkie-promise/index.js +4684789: node_modules/react/lib/ReactErrorUtils.js +4599068: node_modules/react/lib/ReactRef.js +81001935: node_modules/request/node_modules/har-validator/lib/runner.js +5051772: node_modules/react/lib/accumulateInto.js +4608257: node_modules/react/lib/ReactOwner.js +5053515: node_modules/react/lib/forEachAccumulated.js +81005359: node_modules/request/node_modules/har-validator/lib/schemas/index.js +4626735: node_modules/react/lib/Transaction.js +77682133: node_modules/react/node_modules/fbjs/lib/ExecutionEnvironment.js +81002525: node_modules/request/node_modules/har-validator/lib/schemas/cache.json +5177151: node_modules/react/lib/FallbackCompositionState.js +81002712: node_modules/request/node_modules/har-validator/lib/schemas/cacheEntry.json +5230693: node_modules/react/lib/getEventTarget.js +81003206: node_modules/request/node_modules/har-validator/lib/schemas/content.json +5179621: node_modules/react/lib/getTextContentAccessor.js +81003583: node_modules/request/node_modules/har-validator/lib/schemas/cookie.json +5056054: node_modules/react/lib/isEventSupported.js +81004081: node_modules/request/node_modules/har-validator/lib/schemas/creator.json +5185473: node_modules/react/lib/SyntheticCompositionEvent.js +81004311: node_modules/request/node_modules/har-validator/lib/schemas/entry.json +5253386: node_modules/react/lib/isTextInputElement.js +5186617: node_modules/react/lib/SyntheticEvent.js +81005242: node_modules/request/node_modules/har-validator/lib/schemas/har.json +81007111: node_modules/request/node_modules/har-validator/lib/schemas/log.json +81007604: node_modules/request/node_modules/har-validator/lib/schemas/page.json +81008181: node_modules/request/node_modules/har-validator/lib/schemas/pageTimings.json +81008406: node_modules/request/node_modules/har-validator/lib/schemas/postData.json +5254419: node_modules/react/lib/DefaultEventPluginOrder.js +81009060: node_modules/request/node_modules/har-validator/lib/schemas/record.json +5236171: node_modules/react/lib/SyntheticInputEvent.js +81009286: node_modules/request/node_modules/har-validator/lib/schemas/request.json +5255683: node_modules/react/lib/EnterLeaveEventPlugin.js +81010139: node_modules/request/node_modules/har-validator/lib/schemas/response.json +81010946: node_modules/request/node_modules/har-validator/lib/schemas/timings.json +5241892: node_modules/react/lib/ChangeEventPlugin.js +5259147: node_modules/react/lib/SyntheticMouseEvent.js +81001195: node_modules/request/node_modules/har-validator/lib/error.js +5261327: node_modules/react/lib/SyntheticUIEvent.js +81108729: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/index.js +4571389: node_modules/react/lib/ReactUpdates.js +5055413: node_modules/react/lib/ViewportMetrics.js +5262952: node_modules/react/lib/getEventModifierState.js +4580474: node_modules/react/lib/CallbackQueue.js +5264226: node_modules/react/lib/HTMLDOMPropertyConfig.js +77540690: node_modules/react/lib/ReactFeatureFlags.js +81132607: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-object-property/index.js +4583141: node_modules/react/lib/ReactPerf.js +81134601: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-object-property/node_modules/is-property/is-property.js +4965198: node_modules/react/lib/ReactComponentBrowserEnvironment.js +4590060: node_modules/react/lib/ReactReconciler.js +4981199: node_modules/react/lib/DOMChildrenOperations.js +4599068: node_modules/react/lib/ReactRef.js +81127606: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-function/index.js +4608257: node_modules/react/lib/ReactOwner.js +77469073: node_modules/react/lib/DOMLazyTree.js +4626735: node_modules/react/lib/Transaction.js +81149530: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/jsonpointer/jsonpointer.js +77613716: node_modules/react/lib/createMicrosoftUnsafeLocalFunction.js +4525735: node_modules/babel-core/node_modules/output-file-sync/node_modules/xtend/immutable.js +4994328: node_modules/react/lib/setTextContent.js +81106380: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/formats.js +4964346: node_modules/react/lib/escapeTextContentForBrowser.js +4995531: node_modules/react/lib/setInnerHTML.js +5230693: node_modules/react/lib/getEventTarget.js +5056054: node_modules/react/lib/isEventSupported.js +4533728: node_modules/request/lib/auth.js +4986498: node_modules/react/lib/Danger.js +5253386: node_modules/react/lib/isTextInputElement.js +4550226: node_modules/request/lib/oauth.js +77856589: node_modules/react/node_modules/fbjs/lib/createNodesFromMarkup.js +5254419: node_modules/react/lib/DefaultEventPluginOrder.js +77848650: node_modules/react/node_modules/fbjs/lib/createArrayFromMixed.js +5255683: node_modules/react/lib/EnterLeaveEventPlugin.js +82544802: node_modules/request/node_modules/oauth-sign/index.js +77922333: node_modules/react/node_modules/fbjs/lib/getMarkupWrap.js +5259147: node_modules/react/lib/SyntheticMouseEvent.js +4568756: node_modules/request/lib/multipart.js +5261327: node_modules/react/lib/SyntheticUIEvent.js +4585628: node_modules/request/lib/redirect.js +4594531: node_modules/request/lib/tunnel.js +5055413: node_modules/react/lib/ViewportMetrics.js +5262952: node_modules/react/lib/getEventModifierState.js +4993464: node_modules/react/lib/ReactMultiChildUpdateTypes.js +4601413: node_modules/less-cache/node_modules/less/node_modules/request/node_modules/tunnel-agent/index.js +5264226: node_modules/react/lib/HTMLDOMPropertyConfig.js +4966626: node_modules/react/lib/ReactDOMIDOperations.js +91866084: src/flux/nylas-long-connection.js +5100253: node_modules/react/lib/ReactDOMComponent.js +4965198: node_modules/react/lib/ReactComponentBrowserEnvironment.js +4981199: node_modules/react/lib/DOMChildrenOperations.js +77469073: node_modules/react/lib/DOMLazyTree.js +77613716: node_modules/react/lib/createMicrosoftUnsafeLocalFunction.js +2376215: node_modules/event-kit/package.json +4994328: node_modules/react/lib/setTextContent.js +4964346: node_modules/react/lib/escapeTextContentForBrowser.js +77468440: node_modules/react/lib/AutoFocusUtils.js +4995531: node_modules/react/lib/setInnerHTML.js +3905749: src/flux/models/account.js +77909940: node_modules/react/node_modules/fbjs/lib/focusNode.js +4986498: node_modules/react/lib/Danger.js +4967813: node_modules/react/lib/CSSPropertyOperations.js +3911874: src/flux/models/model-with-metadata.js +77856589: node_modules/react/node_modules/fbjs/lib/createNodesFromMarkup.js +4974604: node_modules/react/lib/CSSProperty.js +77848650: node_modules/react/node_modules/fbjs/lib/createArrayFromMixed.js +77922333: node_modules/react/node_modules/fbjs/lib/getMarkupWrap.js +77831606: node_modules/react/node_modules/fbjs/lib/camelizeStyleName.js +77830175: node_modules/react/node_modules/fbjs/lib/camelize.js +4993464: node_modules/react/lib/ReactMultiChildUpdateTypes.js +3959955: src/flux/models/message.js +4978298: node_modules/react/lib/dangerousStyleValue.js +4966626: node_modules/react/lib/ReactDOMIDOperations.js +77943412: node_modules/react/node_modules/fbjs/lib/hyphenateStyleName.js +3971136: node_modules/moment/package.json +5100253: node_modules/react/lib/ReactDOMComponent.js +77941796: node_modules/react/node_modules/fbjs/lib/hyphenate.js +77967845: node_modules/react/node_modules/fbjs/lib/memoizeStringOnly.js +3809622: src/flux/models/file.js +77472180: node_modules/react/lib/DOMNamespaces.js +4947570: node_modules/react/lib/DOMPropertyOperations.js +3870842: src/flux/models/event.js +77468440: node_modules/react/lib/AutoFocusUtils.js +77509750: node_modules/react/lib/ReactDOMInstrumentation.js +3875483: src/flux/models/contact.js +77502083: node_modules/react/lib/ReactDOMDebugTool.js +77514692: node_modules/react/lib/ReactDOMUnknownPropertyDevtool.js +77909940: node_modules/react/node_modules/fbjs/lib/focusNode.js +4967813: node_modules/react/lib/CSSPropertyOperations.js +3886010: src/regexp-utils.js +4963597: node_modules/react/lib/quoteAttributeValueForBrowser.js +5022146: node_modules/react/lib/ReactBrowserEventEmitter.js +4974604: node_modules/react/lib/CSSProperty.js +56664169: node_modules/emoji-data/lib/emoji_data.js +56662240: node_modules/emoji-data/lib/emoji_char.js +5054412: node_modules/react/lib/ReactEventEmitterMixin.js +77617819: node_modules/react/lib/getVendorPrefixedEventName.js +56691400: node_modules/emoji-data/node_modules/underscore.string/lib/underscore.string.js +77831606: node_modules/react/node_modules/fbjs/lib/camelizeStyleName.js +77830175: node_modules/react/node_modules/fbjs/lib/camelize.js +5303529: node_modules/react/lib/ReactDOMButton.js +4978298: node_modules/react/lib/dangerousStyleValue.js +5313340: node_modules/react/lib/ReactDOMInput.js +77943412: node_modules/react/node_modules/fbjs/lib/hyphenateStyleName.js +5322982: node_modules/react/lib/LinkedValueUtils.js +77941796: node_modules/react/node_modules/fbjs/lib/hyphenate.js +77967845: node_modules/react/node_modules/fbjs/lib/memoizeStringOnly.js +5379029: node_modules/react/lib/ReactDOMOption.js +77472180: node_modules/react/lib/DOMNamespaces.js +5382256: node_modules/react/lib/ReactDOMSelect.js +4947570: node_modules/react/lib/DOMPropertyOperations.js +5405374: node_modules/react/lib/ReactDOMTextarea.js +77509750: node_modules/react/lib/ReactDOMInstrumentation.js +77502083: node_modules/react/lib/ReactDOMDebugTool.js +5134927: node_modules/react/lib/ReactMultiChild.js +77514692: node_modules/react/lib/ReactDOMUnknownPropertyDevtool.js +4963597: node_modules/react/lib/quoteAttributeValueForBrowser.js +5097145: node_modules/react/lib/ReactComponentEnvironment.js +5147906: node_modules/react/lib/ReactChildReconciler.js +5022146: node_modules/react/lib/ReactBrowserEventEmitter.js +5061495: node_modules/react/lib/instantiateReactComponent.js +5054412: node_modules/react/lib/ReactEventEmitterMixin.js +5065922: node_modules/react/lib/ReactCompositeComponent.js +77617819: node_modules/react/lib/getVendorPrefixedEventName.js +5303529: node_modules/react/lib/ReactDOMButton.js +4567499: node_modules/react/lib/ReactInstanceMap.js +5313340: node_modules/react/lib/ReactDOMInput.js +77542853: node_modules/react/lib/ReactNodeTypes.js +4559113: node_modules/react/lib/ReactUpdateQueue.js +5322982: node_modules/react/lib/LinkedValueUtils.js +5098804: node_modules/react/lib/shouldUpdateReactComponent.js +5379029: node_modules/react/lib/ReactDOMOption.js +5058027: node_modules/react/lib/ReactEmptyComponent.js +5382256: node_modules/react/lib/ReactDOMSelect.js +4623734: node_modules/react/lib/ReactNativeComponent.js +5152586: node_modules/react/lib/flattenChildren.js +5405374: node_modules/react/lib/ReactDOMTextarea.js +5134927: node_modules/react/lib/ReactMultiChild.js +77987568: node_modules/react/node_modules/fbjs/lib/shallowEqual.js +77629907: node_modules/react/lib/validateDOMNesting.js +5097145: node_modules/react/lib/ReactComponentEnvironment.js +5147906: node_modules/react/lib/ReactChildReconciler.js +5061495: node_modules/react/lib/instantiateReactComponent.js +77503986: node_modules/react/lib/ReactDOMEmptyComponent.js +5065922: node_modules/react/lib/ReactCompositeComponent.js +77510993: node_modules/react/lib/ReactDOMTreeTraversal.js +4941549: node_modules/react/lib/ReactDOMTextComponent.js +5271987: node_modules/react/lib/ReactDefaultBatchingStrategy.js +4567499: node_modules/react/lib/ReactInstanceMap.js +77542853: node_modules/react/lib/ReactNodeTypes.js +5411190: node_modules/react/lib/ReactEventListener.js +4559113: node_modules/react/lib/ReactUpdateQueue.js +77676891: node_modules/react/node_modules/fbjs/lib/EventListener.js +5098804: node_modules/react/lib/shouldUpdateReactComponent.js +5058027: node_modules/react/lib/ReactEmptyComponent.js +77934933: node_modules/react/node_modules/fbjs/lib/getUnboundedScrollPosition.js +4623734: node_modules/react/lib/ReactNativeComponent.js +5416577: node_modules/react/lib/ReactInjection.js +5152586: node_modules/react/lib/flattenChildren.js +5426571: node_modules/react/lib/ReactReconcileTransaction.js +5447146: node_modules/react/lib/ReactInputSelection.js +77987568: node_modules/react/node_modules/fbjs/lib/shallowEqual.js +5451456: node_modules/react/lib/ReactDOMSelection.js +77629907: node_modules/react/lib/validateDOMNesting.js +5458269: node_modules/react/lib/getNodeForCharacterOffset.js +77503986: node_modules/react/lib/ReactDOMEmptyComponent.js +77836612: node_modules/react/node_modules/fbjs/lib/containsNode.js +77951977: node_modules/react/node_modules/fbjs/lib/isTextNode.js +77510993: node_modules/react/lib/ReactDOMTreeTraversal.js +77950580: node_modules/react/node_modules/fbjs/lib/isNode.js +4941549: node_modules/react/lib/ReactDOMTextComponent.js +77913930: node_modules/react/node_modules/fbjs/lib/getActiveElement.js +5530086: node_modules/react/lib/SVGDOMPropertyConfig.js +5271987: node_modules/react/lib/ReactDefaultBatchingStrategy.js +3889367: src/flux/stores/account-store.js +5411190: node_modules/react/lib/ReactEventListener.js +77676891: node_modules/react/node_modules/fbjs/lib/EventListener.js +77934933: node_modules/react/node_modules/fbjs/lib/getUnboundedScrollPosition.js +5416577: node_modules/react/lib/ReactInjection.js +65006241: node_modules/keytar/package.json +5426571: node_modules/react/lib/ReactReconcileTransaction.js +65004180: node_modules/keytar/lib/keytar.js +5447146: node_modules/react/lib/ReactInputSelection.js +5451456: node_modules/react/lib/ReactDOMSelection.js +5458269: node_modules/react/lib/getNodeForCharacterOffset.js +5474425: node_modules/react/lib/SelectEventPlugin.js +77836612: node_modules/react/node_modules/fbjs/lib/containsNode.js +77951977: node_modules/react/node_modules/fbjs/lib/isTextNode.js +77950580: node_modules/react/node_modules/fbjs/lib/isNode.js +5480910: node_modules/react/lib/SimpleEventPlugin.js +77913930: node_modules/react/node_modules/fbjs/lib/getActiveElement.js +5530086: node_modules/react/lib/SVGDOMPropertyConfig.js +77602647: node_modules/react/lib/SyntheticAnimationEvent.js +5499613: node_modules/react/lib/SyntheticClipboardEvent.js +5500825: node_modules/react/lib/SyntheticFocusEvent.js +5508624: node_modules/react/lib/SyntheticKeyboardEvent.js +5511371: node_modules/react/lib/getEventCharCode.js +5519469: node_modules/react/lib/getEventKey.js +5522372: node_modules/react/lib/SyntheticDragEvent.js +5526795: node_modules/react/lib/SyntheticTouchEvent.js +77603899: node_modules/react/lib/SyntheticTransitionEvent.js +5528111: node_modules/react/lib/SyntheticWheelEvent.js +5474425: node_modules/react/lib/SelectEventPlugin.js +5480910: node_modules/react/lib/SimpleEventPlugin.js +4998763: node_modules/react/lib/ReactMount.js +77602647: node_modules/react/lib/SyntheticAnimationEvent.js +5499613: node_modules/react/lib/SyntheticClipboardEvent.js +5500825: node_modules/react/lib/SyntheticFocusEvent.js +77501092: node_modules/react/lib/ReactDOMContainerInfo.js +77509290: node_modules/react/lib/ReactDOMFeatureFlags.js +5508624: node_modules/react/lib/SyntheticKeyboardEvent.js +5058770: node_modules/react/lib/ReactMarkupChecksum.js +5511371: node_modules/react/lib/getEventCharCode.js +5519469: node_modules/react/lib/getEventKey.js +5060281: node_modules/react/lib/adler32.js +5269677: node_modules/react/lib/findDOMNode.js +5522372: node_modules/react/lib/SyntheticDragEvent.js +5526795: node_modules/react/lib/SyntheticTouchEvent.js +77616438: node_modules/react/lib/getNativeComponentFromComposite.js +77627726: node_modules/react/lib/renderSubtreeIntoContainer.js +77603899: node_modules/react/lib/SyntheticTransitionEvent.js +5528111: node_modules/react/lib/SyntheticWheelEvent.js +4998763: node_modules/react/lib/ReactMount.js +77501092: node_modules/react/lib/ReactDOMContainerInfo.js +77509290: node_modules/react/lib/ReactDOMFeatureFlags.js +5058770: node_modules/react/lib/ReactMarkupChecksum.js +5060281: node_modules/react/lib/adler32.js +5269677: node_modules/react/lib/findDOMNode.js +77616438: node_modules/react/lib/getNativeComponentFromComposite.js +77627726: node_modules/react/lib/renderSubtreeIntoContainer.js +5634095: src/global/nylas-component-kit.js +3925080: src/flux/models/label.js +5634095: src/global/nylas-component-kit.js +3930438: src/flux/models/folder.js +3930638: src/flux/models/thread.js +6135910: src/flux/stores/popover-store.js +91364936: src/components/fixed-popover.js +4096909: src/flux/models/calendar.js +5951047: src/flux/stores/badge-store.js +5726955: src/flux/stores/focused-perspective-store.js +5744701: src/flux/stores/workspace-store.js +5932960: src/flux/stores/metadata-store.js +4648657: src/flux/stores/category-store.js +6135910: src/flux/stores/popover-store.js +9942964: src/flux/stores/nylas-sync-status-store.js +91364936: src/components/fixed-popover.js +5541756: src/flux/stores/undo-redo-store.js +5614032: src/flux/stores/mail-rules-store.js +4098495: src/flux/models/json-blob.js +5592720: src/flux/tasks/reprocess-mail-rules-task.js +5605792: src/mail-rules-processor.js +5621142: src/mail-rules-templates.js +5629111: src/components/scenario-editor-models.js +5932960: src/flux/stores/metadata-store.js +5541756: src/flux/stores/undo-redo-store.js +5941725: src/flux/stores/file-upload-store.js +5614032: src/flux/stores/mail-rules-store.js +5592720: src/flux/tasks/reprocess-mail-rules-task.js +1894875: node_modules/mkdirp/package.json +1896279: node_modules/mkdirp/index.js +5605792: src/mail-rules-processor.js +5621142: src/mail-rules-templates.js +5629111: src/components/scenario-editor-models.js +5941725: src/flux/stores/file-upload-store.js +4100089: src/mailbox-perspective.js +1894875: node_modules/mkdirp/package.json +1896279: node_modules/mkdirp/index.js +4120446: src/flux/tasks/task-factory.js +4139229: src/flux/tasks/change-folder-task.js +4156736: src/flux/tasks/change-mail-task.js +4636283: src/flux/tasks/syncback-category-task.js +5273907: src/flux/tasks/change-labels-task.js +5304677: src/flux/tasks/change-unread-task.js +5341333: src/flux/tasks/change-starred-task.js +5350052: src/flux/stores/outbox-store.js +5960988: src/flux/stores/file-download-store.js +5356198: src/flux/tasks/send-draft-task.js +5417961: src/sound-registry.js +5975123: node_modules/request-progress/package.json +91943581: src/flux/tasks/base-draft-task.js +5976554: node_modules/request-progress/index.js +5978608: node_modules/request-progress/node_modules/throttleit/index.js +5419733: src/flux/tasks/syncback-metadata-task.js +5431422: src/flux/tasks/syncback-model-task.js +91991403: src/flux/tasks/notify-plugins-of-send-task.js +91821594: src/flux/edgehill-api.js +5960988: src/flux/stores/file-download-store.js +5459931: src/flux/tasks/syncback-draft-task.js +5975123: node_modules/request-progress/package.json +5976554: node_modules/request-progress/index.js +5471010: src/flux/stores/task-queue-status-store.js +5978608: node_modules/request-progress/node_modules/throttleit/index.js +5986780: src/flux/stores/preferences-ui-store.js +5512912: src/flux/stores/thread-counts-store.js +5990595: node_modules/immutable/package.json +5992964: node_modules/immutable/dist/immutable.js +91919008: src/flux/stores/recently-read-store.js +5501930: src/flux/models/mutable-query-subscription.js +91852488: src/flux/models/unread-query-subscription.js +5986780: src/flux/stores/preferences-ui-store.js +5990595: node_modules/immutable/package.json +5992964: node_modules/immutable/dist/immutable.js +5680865: src/flux/stores/draft-store.js +5704584: src/flux/stores/draft-editing-session.js +5886976: src/extension-registry.js +5895610: src/extensions/composer-extension-adapter.js +5860903: src/dom-utils.js +5913732: src/extensions/extension-utils.js +5915158: src/extensions/message-view-extension-adapter.js +91896596: src/flux/stores/draft-factory.js +5714320: src/flux/stores/contact-store.js +5723311: src/flux/stores/contact-ranking-store.js +6183183: src/flux/stores/message-body-processor.js +5523481: src/window-bridge.js +5979358: src/flux/stores/focused-contacts-store.js +5917902: src/flux/stores/message-store.js +6153847: src/flux/stores/searchable-component-store.js +5733766: src/flux/stores/focused-content-store.js +94875052: src/searchable-components/search-constants.js +3672501: keymaps/base.json +6183183: src/flux/stores/message-body-processor.js +5756110: src/services/inline-style-transformer.js +5979358: src/flux/stores/focused-contacts-store.js +5758660: src/services/sanitize-transformer.js +5763630: node_modules/sanitize-html/package.json +6153847: src/flux/stores/searchable-component-store.js +5765975: node_modules/sanitize-html/index.js +94875052: src/searchable-components/search-constants.js +5775765: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/lib/index.js +5777529: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/lib/Parser.js +3672501: keymaps/base.json +5785458: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/lib/Tokenizer.js +3674831: keymaps/base-darwin.json +5810958: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/entities/lib/decode_codepoint.js +5811580: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/entities/maps/decode.json +3675274: keymaps/templates/Gmail.json +5811878: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/entities/maps/entities.json +5852494: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/entities/maps/legacy.json +5854241: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/entities/maps/xml.json +3675274: keymaps/templates/Gmail.json +3674831: keymaps/base-darwin.json +5854294: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/domhandler/index.js +3682264: src/less-compile-cache.js +5858733: node_modules/juice/node_modules/cheerio/node_modules/css-select/node_modules/domutils/node_modules/domelementtype/index.js +3675274: keymaps/templates/Gmail.json +3684143: node_modules/less-cache/lib/less-cache.js +5859144: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/domhandler/lib/node.js +5860059: node_modules/juice/node_modules/cheerio/node_modules/htmlparser2/node_modules/domhandler/lib/element.js +3693157: node_modules/less-cache/node_modules/less/lib/less/fs.js +5860502: node_modules/sanitize-html/node_modules/regexp-quote/regexp-quote.js +3693264: node_modules/less-cache/node_modules/less/node_modules/graceful-fs/graceful-fs.js +5860597: src/flux/models/message-utils.js +3696362: node_modules/less-cache/node_modules/less/node_modules/graceful-fs/fs.js +92019120: src/flux/tasks/syncback-draft-files-task.js +92064500: src/multi-request-progress-monitor.js +5555349: src/flux/tasks/destroy-draft-task.js +3675274: keymaps/templates/Gmail.json +3682264: src/less-compile-cache.js +3684143: node_modules/less-cache/lib/less-cache.js +6145019: src/flux/stores/modal-store.js +3696740: node_modules/less-cache/node_modules/less/node_modules/graceful-fs/polyfills.js +3693157: node_modules/less-cache/node_modules/less/lib/less/fs.js +4488696: node_modules/react/package.json +3693264: node_modules/less-cache/node_modules/less/node_modules/graceful-fs/graceful-fs.js +4490634: node_modules/react/react.js +4490690: node_modules/react/lib/React.js +3696362: node_modules/less-cache/node_modules/less/node_modules/graceful-fs/fs.js +79997473: node_modules/react/node_modules/object-assign/index.js +3703233: node_modules/jasmine-tagged/node_modules/jasmine-focused/node_modules/walkdir/walkdir.js +4503558: node_modules/react/lib/ReactChildren.js +4509426: node_modules/react/lib/PooledClass.js +77945402: node_modules/react/node_modules/fbjs/lib/invariant.js +4515504: node_modules/react/lib/ReactElement.js +4526119: node_modules/react/lib/ReactCurrentOwner.js +77997237: node_modules/react/node_modules/fbjs/lib/warning.js +77865983: node_modules/react/node_modules/fbjs/lib/emptyFunction.js +3696740: node_modules/less-cache/node_modules/less/node_modules/graceful-fs/polyfills.js +77610157: node_modules/react/lib/canDefineProperty.js +4526776: node_modules/react/lib/traverseAllChildren.js +4549078: node_modules/react/lib/getIteratorFn.js +3703233: node_modules/jasmine-tagged/node_modules/jasmine-focused/node_modules/walkdir/walkdir.js +4554180: node_modules/react/lib/ReactComponent.js +77543851: node_modules/react/lib/ReactNoopUpdateQueue.js +77541351: node_modules/react/lib/ReactInstrumentation.js +77520716: node_modules/react/lib/ReactDebugTool.js +77541813: node_modules/react/lib/ReactInvalidSetStateWarningDevTool.js +77868121: node_modules/react/node_modules/fbjs/lib/emptyObject.js +4658325: node_modules/react/lib/ReactClass.js +4622568: node_modules/react/lib/ReactPropTypeLocations.js +77955086: node_modules/react/node_modules/fbjs/lib/keyMirror.js +4623120: node_modules/react/lib/ReactPropTypeLocationNames.js +77961237: node_modules/react/node_modules/fbjs/lib/keyOf.js +77505957: node_modules/react/lib/ReactDOMFactories.js +4611818: node_modules/react/lib/ReactElementValidator.js +77963437: node_modules/react/node_modules/fbjs/lib/mapObject.js +5328125: node_modules/react/lib/ReactPropTypes.js +77568528: node_modules/react/lib/ReactVersion.js +5544770: node_modules/react/lib/onlyChild.js +75786540: node_modules/react-dom/package.json +75786477: node_modules/react-dom/index.js +4693093: node_modules/react/lib/ReactDOM.js +77494923: node_modules/react/lib/ReactDOMComponentTree.js +4954924: node_modules/react/lib/DOMProperty.js +77494452: node_modules/react/lib/ReactDOMComponentFlags.js +5154239: node_modules/react/lib/ReactDefaultInjection.js +5157979: node_modules/react/lib/BeforeInputEventPlugin.js +4501390: node_modules/react/lib/EventConstants.js +5171831: node_modules/react/lib/EventPropagators.js +5034693: node_modules/react/lib/EventPluginHub.js +5042578: node_modules/react/lib/EventPluginRegistry.js +4493310: node_modules/react/lib/EventPluginUtils.js +4684789: node_modules/react/lib/ReactErrorUtils.js +5051772: node_modules/react/lib/accumulateInto.js +5053515: node_modules/react/lib/forEachAccumulated.js +77682133: node_modules/react/node_modules/fbjs/lib/ExecutionEnvironment.js +5177151: node_modules/react/lib/FallbackCompositionState.js +5179621: node_modules/react/lib/getTextContentAccessor.js +5185473: node_modules/react/lib/SyntheticCompositionEvent.js +5186617: node_modules/react/lib/SyntheticEvent.js +5236171: node_modules/react/lib/SyntheticInputEvent.js +5241892: node_modules/react/lib/ChangeEventPlugin.js +4571389: node_modules/react/lib/ReactUpdates.js +4580474: node_modules/react/lib/CallbackQueue.js +77540690: node_modules/react/lib/ReactFeatureFlags.js +4583141: node_modules/react/lib/ReactPerf.js +4590060: node_modules/react/lib/ReactReconciler.js +4599068: node_modules/react/lib/ReactRef.js +4608257: node_modules/react/lib/ReactOwner.js +4626735: node_modules/react/lib/Transaction.js +5230693: node_modules/react/lib/getEventTarget.js +5056054: node_modules/react/lib/isEventSupported.js +5253386: node_modules/react/lib/isTextInputElement.js +5254419: node_modules/react/lib/DefaultEventPluginOrder.js +5255683: node_modules/react/lib/EnterLeaveEventPlugin.js +5259147: node_modules/react/lib/SyntheticMouseEvent.js +5261327: node_modules/react/lib/SyntheticUIEvent.js +5055413: node_modules/react/lib/ViewportMetrics.js +5262952: node_modules/react/lib/getEventModifierState.js +5264226: node_modules/react/lib/HTMLDOMPropertyConfig.js +4965198: node_modules/react/lib/ReactComponentBrowserEnvironment.js +4981199: node_modules/react/lib/DOMChildrenOperations.js +77469073: node_modules/react/lib/DOMLazyTree.js +77613716: node_modules/react/lib/createMicrosoftUnsafeLocalFunction.js +4994328: node_modules/react/lib/setTextContent.js +4964346: node_modules/react/lib/escapeTextContentForBrowser.js +4995531: node_modules/react/lib/setInnerHTML.js +4986498: node_modules/react/lib/Danger.js +77856589: node_modules/react/node_modules/fbjs/lib/createNodesFromMarkup.js +77848650: node_modules/react/node_modules/fbjs/lib/createArrayFromMixed.js +77922333: node_modules/react/node_modules/fbjs/lib/getMarkupWrap.js +4993464: node_modules/react/lib/ReactMultiChildUpdateTypes.js +4966626: node_modules/react/lib/ReactDOMIDOperations.js +5100253: node_modules/react/lib/ReactDOMComponent.js +77468440: node_modules/react/lib/AutoFocusUtils.js +77909940: node_modules/react/node_modules/fbjs/lib/focusNode.js +4967813: node_modules/react/lib/CSSPropertyOperations.js +4974604: node_modules/react/lib/CSSProperty.js +77831606: node_modules/react/node_modules/fbjs/lib/camelizeStyleName.js +77830175: node_modules/react/node_modules/fbjs/lib/camelize.js +4978298: node_modules/react/lib/dangerousStyleValue.js +77943412: node_modules/react/node_modules/fbjs/lib/hyphenateStyleName.js +77941796: node_modules/react/node_modules/fbjs/lib/hyphenate.js +77967845: node_modules/react/node_modules/fbjs/lib/memoizeStringOnly.js +77472180: node_modules/react/lib/DOMNamespaces.js +4947570: node_modules/react/lib/DOMPropertyOperations.js +77509750: node_modules/react/lib/ReactDOMInstrumentation.js +77502083: node_modules/react/lib/ReactDOMDebugTool.js +77514692: node_modules/react/lib/ReactDOMUnknownPropertyDevtool.js +4963597: node_modules/react/lib/quoteAttributeValueForBrowser.js +5022146: node_modules/react/lib/ReactBrowserEventEmitter.js +5054412: node_modules/react/lib/ReactEventEmitterMixin.js +77617819: node_modules/react/lib/getVendorPrefixedEventName.js +5303529: node_modules/react/lib/ReactDOMButton.js +5313340: node_modules/react/lib/ReactDOMInput.js +5322982: node_modules/react/lib/LinkedValueUtils.js +5379029: node_modules/react/lib/ReactDOMOption.js +5382256: node_modules/react/lib/ReactDOMSelect.js +5405374: node_modules/react/lib/ReactDOMTextarea.js +5134927: node_modules/react/lib/ReactMultiChild.js +5097145: node_modules/react/lib/ReactComponentEnvironment.js +5147906: node_modules/react/lib/ReactChildReconciler.js +5061495: node_modules/react/lib/instantiateReactComponent.js +5065922: node_modules/react/lib/ReactCompositeComponent.js +4567499: node_modules/react/lib/ReactInstanceMap.js +77542853: node_modules/react/lib/ReactNodeTypes.js +4559113: node_modules/react/lib/ReactUpdateQueue.js +5098804: node_modules/react/lib/shouldUpdateReactComponent.js +5058027: node_modules/react/lib/ReactEmptyComponent.js +4623734: node_modules/react/lib/ReactNativeComponent.js +5152586: node_modules/react/lib/flattenChildren.js +77987568: node_modules/react/node_modules/fbjs/lib/shallowEqual.js +77629907: node_modules/react/lib/validateDOMNesting.js +77503986: node_modules/react/lib/ReactDOMEmptyComponent.js +77510993: node_modules/react/lib/ReactDOMTreeTraversal.js +4941549: node_modules/react/lib/ReactDOMTextComponent.js +5271987: node_modules/react/lib/ReactDefaultBatchingStrategy.js +5411190: node_modules/react/lib/ReactEventListener.js +77676891: node_modules/react/node_modules/fbjs/lib/EventListener.js +77934933: node_modules/react/node_modules/fbjs/lib/getUnboundedScrollPosition.js +5416577: node_modules/react/lib/ReactInjection.js +5426571: node_modules/react/lib/ReactReconcileTransaction.js +5447146: node_modules/react/lib/ReactInputSelection.js +5451456: node_modules/react/lib/ReactDOMSelection.js +5458269: node_modules/react/lib/getNodeForCharacterOffset.js +77836612: node_modules/react/node_modules/fbjs/lib/containsNode.js +77951977: node_modules/react/node_modules/fbjs/lib/isTextNode.js +77950580: node_modules/react/node_modules/fbjs/lib/isNode.js +77913930: node_modules/react/node_modules/fbjs/lib/getActiveElement.js +5530086: node_modules/react/lib/SVGDOMPropertyConfig.js +5474425: node_modules/react/lib/SelectEventPlugin.js +5480910: node_modules/react/lib/SimpleEventPlugin.js +77602647: node_modules/react/lib/SyntheticAnimationEvent.js +5499613: node_modules/react/lib/SyntheticClipboardEvent.js +5500825: node_modules/react/lib/SyntheticFocusEvent.js +5508624: node_modules/react/lib/SyntheticKeyboardEvent.js +5511371: node_modules/react/lib/getEventCharCode.js +5519469: node_modules/react/lib/getEventKey.js +5522372: node_modules/react/lib/SyntheticDragEvent.js +5526795: node_modules/react/lib/SyntheticTouchEvent.js +77603899: node_modules/react/lib/SyntheticTransitionEvent.js +5528111: node_modules/react/lib/SyntheticWheelEvent.js +4998763: node_modules/react/lib/ReactMount.js +77501092: node_modules/react/lib/ReactDOMContainerInfo.js +77509290: node_modules/react/lib/ReactDOMFeatureFlags.js +5058770: node_modules/react/lib/ReactMarkupChecksum.js +5060281: node_modules/react/lib/adler32.js +5269677: node_modules/react/lib/findDOMNode.js +77616438: node_modules/react/lib/getNativeComponentFromComposite.js +77627726: node_modules/react/lib/renderSubtreeIntoContainer.js +5634095: src/global/nylas-component-kit.js +6135910: src/flux/stores/popover-store.js +91364936: src/components/fixed-popover.js +5932960: src/flux/stores/metadata-store.js +5541756: src/flux/stores/undo-redo-store.js +5614032: src/flux/stores/mail-rules-store.js +5592720: src/flux/tasks/reprocess-mail-rules-task.js +5605792: src/mail-rules-processor.js +5621142: src/mail-rules-templates.js +5629111: src/components/scenario-editor-models.js +5941725: src/flux/stores/file-upload-store.js +1894875: node_modules/mkdirp/package.json +1896279: node_modules/mkdirp/index.js +5960988: src/flux/stores/file-download-store.js +5975123: node_modules/request-progress/package.json +5976554: node_modules/request-progress/index.js +3708754: static/index.less +5978608: node_modules/request-progress/node_modules/throttleit/index.js +3709902: static/variables/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3719008: static/normalize.less +3726688: static/type.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3731800: static/inputs.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3733216: static/buttons.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3737856: static/dropdowns.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3738091: static/workspace.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3746538: static/resizable.less +3747129: static/selection.less +3747443: static/utilities.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3708754: static/index.less +3717773: static/variables/ui-mixins.less +3748613: static/components/popover.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3750797: static/components/menu.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3752952: static/components/switch.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3753558: static/components/tokenizing-text-field.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3759326: static/components/extra.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3761494: static/components/list-tabular.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3765991: static/components/disclosure-triangle.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3709902: static/variables/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3717773: static/variables/ui-mixins.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3766478: static/components/button-dropdown.less +3719008: static/normalize.less +3733216: static/buttons.less +3726688: static/type.less +3770706: static/components/scroll-region.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3731800: static/inputs.less +3773921: static/components/spinner.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3733216: static/buttons.less +3774954: static/components/generated-form.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3776905: static/components/unsafe.less +3717773: static/variables/ui-mixins.less +3737856: static/dropdowns.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3738091: static/workspace.less +3777342: static/components/key-commands-region.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3777420: static/components/contenteditable.less +3746538: static/resizable.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3747129: static/selection.less +3781503: static/components/editable-list.less +3747443: static/utilities.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3784402: static/components/outline-view.less +3717773: static/variables/ui-mixins.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3748613: static/components/popover.less +3717773: static/variables/ui-mixins.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3750797: static/components/menu.less +3787888: static/components/fixed-popover.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3790891: static/components/modal.less +3752952: static/components/switch.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3791241: static/components/date-input.less +3753558: static/components/tokenizing-text-field.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97491765: static/components/nylas-calendar.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97490322: static/components/empty-list-state.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97489398: static/components/date-picker.less +3759326: static/components/extra.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97499252: static/components/time-picker.less +3717773: static/variables/ui-mixins.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3761494: static/components/list-tabular.less +97498180: static/components/table.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97489787: static/components/editable-table.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3765991: static/components/disclosure-triangle.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3766478: static/components/button-dropdown.less +3733216: static/buttons.less +3770706: static/components/scroll-region.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3773921: static/components/spinner.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3774954: static/components/generated-form.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3776905: static/components/unsafe.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3777342: static/components/key-commands-region.less +3777420: static/components/contenteditable.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3781503: static/components/editable-list.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3784402: static/components/outline-view.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3787888: static/components/fixed-popover.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3790891: static/components/modal.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3791241: static/components/date-input.less +3718416: internal_packages/ui-light/styles/ui-variables.less +5986780: src/flux/stores/preferences-ui-store.js +97491765: static/components/nylas-calendar.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97490322: static/components/empty-list-state.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97489398: static/components/date-picker.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97499252: static/components/time-picker.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97498180: static/components/table.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97489787: static/components/editable-table.less +3718416: internal_packages/ui-light/styles/ui-variables.less +5990595: node_modules/immutable/package.json +5992964: node_modules/immutable/dist/immutable.js +3791399: static/email-frame.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3791399: static/email-frame.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +6330626: src/sheet-container.js +75770892: node_modules/react-addons-css-transition-group/index.js +6285435: node_modules/react/lib/ReactCSSTransitionGroup.js +6288370: node_modules/react/lib/ReactTransitionGroup.js +6294511: node_modules/react/lib/ReactTransitionChildMapping.js +6300645: node_modules/react/lib/ReactCSSTransitionGroupChild.js +77653837: node_modules/react/node_modules/fbjs/lib/CSSCore.js +6305123: node_modules/react/lib/ReactTransitionEvents.js +6336675: src/sheet.js +3795009: internal_packages/send-later/package.json +6259077: src/component-registry.js +956764: package.json +5658995: src/components/retina-img.js +6345466: src/components/flexbox.js +6347603: src/components/injected-component-set.js +6355674: src/components/unsafe-component.js +6361626: src/components/injected-component-label.js +6363606: src/components/resizable-region.js +6372438: src/sheet-toolbar.js +6183183: src/flux/stores/message-body-processor.js +3795593: internal_packages/nylas-private-analytics/lib/main.js +5979358: src/flux/stores/focused-contacts-store.js +3795926: internal_packages/nylas-private-fonts/stylesheets/nylas-fonts.less +3709902: static/variables/ui-variables.less +3797131: internal_packages/nylas-private-fonts/lib/main.js +6153847: src/flux/stores/searchable-component-store.js +3797276: internal_packages/nylas-private-sounds/lib/main.js +94875052: src/searchable-components/search-constants.js +6217392: internal_packages/screenshot-mode/lib/main.js +3672501: keymaps/base.json +38810714: internal_packages/thread-search-index/lib/main.js +38818707: internal_packages/thread-search-index/lib/search-index-store.js +6218172: internal_packages/deltas/lib/main.js +6218486: internal_packages/deltas/lib/account-delta-connection-pool.js +6226311: internal_packages/deltas/lib/nylas-long-connection.js +3674831: keymaps/base-darwin.json +6234072: internal_packages/deltas/lib/account-delta-connection.js +3675274: keymaps/templates/Gmail.json +6249716: internal_packages/deltas/lib/contact-rankings-cache.js +6251800: internal_packages/deltas/lib/refreshing-json-cache.js +6254175: internal_packages/worker-ui/stylesheets/worker-ui.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +6258340: internal_packages/worker-ui/lib/main.js +3675274: keymaps/templates/Gmail.json +3682264: src/less-compile-cache.js +6259077: src/component-registry.js +3684143: node_modules/less-cache/lib/less-cache.js +6267062: internal_packages/worker-ui/lib/developer-bar.js +3693157: node_modules/less-cache/node_modules/less/lib/less/fs.js +3693264: node_modules/less-cache/node_modules/less/node_modules/graceful-fs/graceful-fs.js +3696362: node_modules/less-cache/node_modules/less/node_modules/graceful-fs/fs.js +6311954: internal_packages/worker-ui/lib/developer-bar-store.js +6318392: internal_packages/worker-ui/lib/developer-bar-task.js +6322329: node_modules/classnames/package.json +6323631: node_modules/classnames/index.js +6324643: internal_packages/worker-ui/lib/developer-bar-curl-item.js +3696740: node_modules/less-cache/node_modules/less/node_modules/graceful-fs/polyfills.js +6327819: internal_packages/worker-ui/lib/developer-bar-long-poll-item.js +6330626: src/sheet-container.js +3703233: node_modules/jasmine-tagged/node_modules/jasmine-focused/node_modules/walkdir/walkdir.js +75770892: node_modules/react-addons-css-transition-group/index.js +6285435: node_modules/react/lib/ReactCSSTransitionGroup.js +6288370: node_modules/react/lib/ReactTransitionGroup.js +6294511: node_modules/react/lib/ReactTransitionChildMapping.js +6300645: node_modules/react/lib/ReactCSSTransitionGroupChild.js +77653837: node_modules/react/node_modules/fbjs/lib/CSSCore.js +6305123: node_modules/react/lib/ReactTransitionEvents.js +6336675: src/sheet.js +5658995: src/components/retina-img.js +6345466: src/components/flexbox.js +6347603: src/components/injected-component-set.js +6355674: src/components/unsafe-component.js +6361626: src/components/injected-component-label.js +6363606: src/components/resizable-region.js +6372438: src/sheet-toolbar.js +6388649: internal_packages/nylas-private-analytics/lib/analytics-store.js +6395255: internal_packages/nylas-private-analytics/node_modules/underscore/package.json +6397211: internal_packages/nylas-private-analytics/node_modules/underscore/underscore.js +6450130: internal_packages/nylas-private-analytics/node_modules/mixpanel/package.json +6451584: internal_packages/nylas-private-analytics/node_modules/mixpanel/lib/mixpanel-node.js +3709902: static/variables/ui-variables.less +3708754: static/index.less +3709902: static/variables/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3719008: static/normalize.less +3726688: static/type.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3731800: static/inputs.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3733216: static/buttons.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3737856: static/dropdowns.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3738091: static/workspace.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3746538: static/resizable.less +3747129: static/selection.less +3747443: static/utilities.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3748613: static/components/popover.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3750797: static/components/menu.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3752952: static/components/switch.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3753558: static/components/tokenizing-text-field.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3759326: static/components/extra.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3761494: static/components/list-tabular.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3765991: static/components/disclosure-triangle.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3766478: static/components/button-dropdown.less +3733216: static/buttons.less +3770706: static/components/scroll-region.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3773921: static/components/spinner.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3774954: static/components/generated-form.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3776905: static/components/unsafe.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3777342: static/components/key-commands-region.less +3777420: static/components/contenteditable.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3781503: static/components/editable-list.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3784402: static/components/outline-view.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3787888: static/components/fixed-popover.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3790891: static/components/modal.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3791241: static/components/date-input.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97491765: static/components/nylas-calendar.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97490322: static/components/empty-list-state.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97489398: static/components/date-picker.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97499252: static/components/time-picker.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97498180: static/components/table.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97489787: static/components/editable-table.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3791399: static/email-frame.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3795009: internal_packages/send-later/package.json +956764: package.json +4487636: internal_packages/account-sidebar/stylesheets/account-sidebar.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +4488033: internal_packages/account-sidebar/lib/main.js +5625206: internal_packages/account-sidebar/lib/components/account-sidebar.js +5639191: src/components/outline-view.js +5658995: src/components/retina-img.js +6475049: src/components/outline-view-item.js +6322329: node_modules/classnames/package.json +6323631: node_modules/classnames/index.js +6515079: src/components/disclosure-triangle.js +6516563: src/components/drop-zone.js +6519250: src/components/scroll-region.js +6541041: src/components/scrollbar-ticks.js +6345466: src/components/flexbox.js +6259077: src/component-registry.js +6547498: internal_packages/account-sidebar/lib/components/account-switcher.js +6550507: internal_packages/account-sidebar/lib/account-commands.js +6555086: internal_packages/account-sidebar/lib/sidebar-actions.js +6555434: internal_packages/account-sidebar/lib/sidebar-store.js +6561356: internal_packages/account-sidebar/lib/sidebar-section.js +5568947: src/flux/tasks/destroy-category-task.js +6568512: internal_packages/account-sidebar/lib/sidebar-item.js +11121379: internal_packages/activity-list/stylesheets/activity-list.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +11116731: internal_packages/activity-list/lib/main.js +11044916: internal_packages/activity-list/lib/activity-list-button.js +11103679: internal_packages/activity-list/lib/activity-list.js +11078805: internal_packages/activity-list/lib/activity-list-store.js +11043296: internal_packages/activity-list/lib/activity-list-actions.js +11119272: internal_packages/activity-list/lib/plugins.js +11059386: internal_packages/activity-list/lib/activity-list-item-container.js +11054031: internal_packages/activity-list/lib/activity-list-empty-state.js +6576439: internal_packages/attachments/stylesheets/attachments.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +6581032: internal_packages/attachments/lib/main.js +6583425: internal_packages/attachments/lib/attachment-component.js +6602716: internal_packages/attachments/lib/image-attachment-component.js +6621086: internal_packages/category-picker/stylesheets/category-picker.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +6622362: internal_packages/category-picker/lib/main.js +6622941: internal_packages/category-picker/lib/category-picker.js +6650228: src/components/key-commands-region.js +11125195: internal_packages/category-picker/lib/category-picker-popover.js +6662242: internal_packages/composer/stylesheets/composer.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3733216: static/buttons.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +6672270: internal_packages/composer/lib/main.js +6688298: internal_packages/composer/lib/compose-button.js +6693285: internal_packages/composer/lib/composer-view.js +6801894: internal_packages/composer/lib/file-upload.js +6810718: internal_packages/composer/lib/image-upload.js +6817376: internal_packages/composer/lib/composer-editor.js +15488775: internal_packages/composer/lib/composer-header.js +6881838: internal_packages/composer/lib/account-contact-field.js +6918854: internal_packages/composer/lib/collapsed-participants.js +15477990: internal_packages/composer/lib/composer-header-actions.js +6916905: internal_packages/composer/lib/fields.js +91309256: src/components/decorators/listens-to-flux-store.js +6854571: internal_packages/composer/lib/send-action-button.js +15467409: internal_packages/composer/lib/action-bar-plugins.js +15526379: internal_packages/composer/lib/decorators/inflate-draft-client-id.js +12062174: internal_packages/composer-emoji/stylesheets/composer-emoji.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +11895302: internal_packages/composer-emoji/lib/main.js +11884154: internal_packages/composer-emoji/lib/emoji-store.js +11220121: internal_packages/composer-emoji/lib/emoji-actions.js +11311506: internal_packages/composer-emoji/lib/emoji-data.js +3708754: static/index.less +3709902: static/variables/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3719008: static/normalize.less +3726688: static/type.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3731800: static/inputs.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3733216: static/buttons.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3737856: static/dropdowns.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3738091: static/workspace.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3746538: static/resizable.less +3747129: static/selection.less +3747443: static/utilities.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3748613: static/components/popover.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3750797: static/components/menu.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3752952: static/components/switch.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3753558: static/components/tokenizing-text-field.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3759326: static/components/extra.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3761494: static/components/list-tabular.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3765991: static/components/disclosure-triangle.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3766478: static/components/button-dropdown.less +3733216: static/buttons.less +3770706: static/components/scroll-region.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3773921: static/components/spinner.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3774954: static/components/generated-form.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3776905: static/components/unsafe.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3777342: static/components/key-commands-region.less +3777420: static/components/contenteditable.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3781503: static/components/editable-list.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3784402: static/components/outline-view.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3787888: static/components/fixed-popover.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3790891: static/components/modal.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3791241: static/components/date-input.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97491765: static/components/nylas-calendar.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97490322: static/components/empty-list-state.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97489398: static/components/date-picker.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97499252: static/components/time-picker.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97498180: static/components/table.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97489787: static/components/editable-table.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3791399: static/email-frame.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +11269567: internal_packages/composer-emoji/lib/emoji-composer-extension.js +11874201: internal_packages/composer-emoji/lib/emoji-picker.js +12036836: internal_packages/composer-emoji/node_modules/node-emoji/package.json +11900180: internal_packages/composer-emoji/node_modules/node-emoji/index.js +11900220: internal_packages/composer-emoji/node_modules/node-emoji/lib/emoji.js +11901752: internal_packages/composer-emoji/node_modules/node-emoji/lib/emoji.json +6209520: src/extensions/composer-extension.js +6199449: src/extensions/contenteditable-extension.js +11863789: internal_packages/composer-emoji/lib/emoji-message-extension.js +6216016: src/extensions/message-view-extension.js +11263608: internal_packages/composer-emoji/lib/emoji-button.js +11220330: internal_packages/composer-emoji/lib/emoji-button-popover.js +11197213: internal_packages/composer-emoji/lib/categorized-emoji.js +12365614: internal_packages/composer-mail-merge/stylesheets/mail-merge.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +12246852: internal_packages/composer-mail-merge/lib/main.js +12078922: internal_packages/composer-mail-merge/lib/mail-merge-button.js +12115700: internal_packages/composer-mail-merge/lib/mail-merge-draft-editing-session.js +12255616: internal_packages/composer-mail-merge/lib/table-data-reducer.js +12097808: internal_packages/composer-mail-merge/lib/mail-merge-constants.js +12365120: internal_packages/composer-mail-merge/package.json +12275198: internal_packages/composer-mail-merge/lib/workspace-data-reducer.js +12195717: internal_packages/composer-mail-merge/lib/mail-merge-utils.js +12294536: internal_packages/composer-mail-merge/node_modules/papaparse/package.json +12296719: internal_packages/composer-mail-merge/node_modules/papaparse/papaparse.js +75788185: node_modules/react-dom/server.js +77510224: node_modules/react/lib/ReactDOMServer.js +5537399: node_modules/react/lib/ReactServerRendering.js +77547198: node_modules/react/lib/ReactServerBatchingStrategy.js +5539994: node_modules/react/lib/ReactServerRenderingTransaction.js +12182972: internal_packages/composer-mail-merge/lib/mail-merge-token.js +12164340: internal_packages/composer-mail-merge/lib/mail-merge-send-button.js +12143212: internal_packages/composer-mail-merge/lib/mail-merge-participants-text-field.js +12101370: internal_packages/composer-mail-merge/lib/mail-merge-container.js +12224650: internal_packages/composer-mail-merge/lib/mail-merge-workspace.js +12175665: internal_packages/composer-mail-merge/lib/mail-merge-table.js +12133498: internal_packages/composer-mail-merge/lib/mail-merge-header-input.js +91336318: src/components/editable-table.js +91714196: src/components/selectable-table.js +91306664: src/components/decorators/compose.js +91296149: src/components/decorators/auto-focuses.js +91318034: src/components/decorators/listens-to-movement-keys.js +91751055: src/components/table.js +91429095: src/components/lazy-rendered-list.js +12089622: internal_packages/composer-mail-merge/lib/mail-merge-composer-extension.js +12774978: internal_packages/composer-scheduler/stylesheets/scheduler.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +12597570: internal_packages/composer-scheduler/lib/main.js +12401184: internal_packages/composer-scheduler/lib/calendar/proposed-time-event.js +12634143: internal_packages/composer-scheduler/lib/scheduler-actions.js +12637241: internal_packages/composer-scheduler/lib/scheduler-constants.js +12639598: internal_packages/composer-scheduler/package.json +12409433: internal_packages/composer-scheduler/lib/calendar/proposed-time-picker.js +12611238: internal_packages/composer-scheduler/lib/proposed-time-calendar-store.js +12604799: internal_packages/composer-scheduler/lib/proposal.js +67807413: node_modules/moment-round/package.json +67804644: node_modules/moment-round/dist/moment-round.js +12394346: internal_packages/composer-scheduler/lib/calendar/proposed-time-calendar-data-source.js +91490885: src/components/nylas-calendar/calendar-data-source.js +12458476: internal_packages/composer-scheduler/lib/composer/new-event-card-container.js +12472599: internal_packages/composer-scheduler/lib/composer/new-event-card.js +12509765: internal_packages/composer-scheduler/lib/composer/new-event-helper.js +12529558: internal_packages/composer-scheduler/lib/composer/proposed-time-list.js +12435429: internal_packages/composer-scheduler/lib/composer/email-b64-images.js +12557233: internal_packages/composer-scheduler/lib/composer/scheduler-composer-button.js +12577398: internal_packages/composer-scheduler/lib/composer/scheduler-composer-extension.js +12517105: internal_packages/composer-scheduler/lib/composer/new-event-preview.js +6940725: internal_packages/composer-signature/styles/composer-signature.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +6942112: internal_packages/composer-signature/lib/main.js +6945805: internal_packages/composer-signature/lib/signature-composer-extension.js +6952671: internal_packages/composer-signature/lib/signature-utils.js +6956430: internal_packages/composer-signature/lib/signature-store.js +12790035: internal_packages/composer-signature/lib/signature-actions.js +6964420: internal_packages/composer-signature/lib/preferences-signatures.js +7087046: internal_packages/composer-spellcheck/lib/main.js +7088733: internal_packages/composer-spellcheck/lib/spellcheck-composer-extension.js +15649511: internal_packages/draft-list/stylesheets/draft-list.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +15640684: internal_packages/draft-list/lib/main.js +15634286: internal_packages/draft-list/lib/draft-list.js +10328875: src/components/flux-container.js +91409314: src/components/focus-container.js +91356893: src/components/empty-list-state.js +9573988: src/components/evented-iframe.js +9590240: src/searchable-components/searchable-component-maker.js +9604588: src/searchable-components/virtual-dom-parser.js +9625948: src/searchable-components/search-match.js +9630803: src/searchable-components/unified-dom-parser.js +9656602: src/searchable-components/iframe-searcher.js +9660189: src/searchable-components/real-dom-parser.js +10348470: src/components/multiselect-list.js +10242985: src/components/list-tabular.js +6613819: src/components/spinner.js +10255150: src/components/list-data-source.js +10259550: src/components/list-selection.js +10266495: src/components/list-tabular-item.js +10270866: src/components/swipe-container.js +10361741: src/components/multiselect-list-interaction-handler.js +10365624: src/components/multiselect-split-interaction-handler.js +15622621: internal_packages/draft-list/lib/draft-list-store.js +10317803: src/flux/stores/observable-list-data-source.js +15613521: internal_packages/draft-list/lib/draft-list-columns.js +6347603: src/components/injected-component-set.js +6355674: src/components/unsafe-component.js +6361626: src/components/injected-component-label.js +15626659: internal_packages/draft-list/lib/draft-list-toolbar.js +91329623: src/components/decorators/listens-to-observable.js +15616539: internal_packages/draft-list/lib/draft-list-send-status.js +15647651: internal_packages/draft-list/lib/sending-progress-bar.js +15637453: internal_packages/draft-list/lib/draft-toolbar-buttons.js +8166806: internal_packages/events/stylesheets/events.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +8168831: internal_packages/events/lib/main.js +8169447: internal_packages/events/lib/event-header.js +5545981: src/flux/tasks/event-rsvp-task.js +9406822: internal_packages/link-tracking/stylesheets/main.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +9407650: internal_packages/link-tracking/lib/main.js +9411086: internal_packages/link-tracking/lib/link-tracking-button.js +9419481: internal_packages/link-tracking/lib/link-tracking-constants.js +9421038: internal_packages/link-tracking/package.json +9421801: internal_packages/link-tracking/lib/link-tracking-composer-extension.js +9433912: internal_packages/link-tracking/lib/link-tracking-message-extension.js +9443214: internal_packages/message-autoload-images/stylesheets/message-autoload-images.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +9443764: internal_packages/message-autoload-images/lib/main.js +9447860: internal_packages/message-autoload-images/lib/autoload-images-extension.js +9452536: internal_packages/message-autoload-images/lib/autoload-images-store.js +9464787: internal_packages/message-autoload-images/lib/autoload-images-actions.js +9466378: internal_packages/message-autoload-images/lib/autoload-images-header.js +9475435: internal_packages/message-list/stylesheets/find-in-thread.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +9476423: internal_packages/message-list/stylesheets/message-list.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +9492060: internal_packages/message-list/lib/main.js +9493451: internal_packages/message-list/lib/message-list.js +9514819: internal_packages/message-list/lib/find-in-thread.js +9533550: internal_packages/message-list/lib/message-item-container.js +9538634: internal_packages/message-list/lib/message-item.js +9554145: internal_packages/message-list/lib/email-frame.js +21971387: internal_packages/message-list/lib/autolinker.js +21985271: internal_packages/message-list/lib/autoscale-images.js +9674883: internal_packages/message-list/lib/email-frame-styles-store.js +9677072: internal_packages/message-list/lib/message-participants.js +9684453: internal_packages/message-list/lib/message-item-body.js +10181263: src/canvas-utils.js +6774422: src/services/quoted-html-transformer.js +6783209: src/services/quote-string-detector.js +6789262: src/dom-walkers.js +9691982: internal_packages/message-list/lib/message-timestamp.js +9694947: internal_packages/message-list/lib/message-controls.js +6876890: src/components/button-dropdown.js +6626794: src/components/menu.js +6791782: src/components/injected-component.js +9704877: src/components/mail-label-set.js +6659234: src/components/mail-label.js +9717628: src/components/mail-important-icon.js +9723341: internal_packages/message-list/lib/message-list-hidden-messages-toggle.js +22004384: internal_packages/message-list/lib/sidebar-plugin-container.js +21992491: internal_packages/message-list/lib/sidebar-participant-picker.js +9914437: internal_packages/mode-switch/stylesheets/mode-switch.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +9914642: internal_packages/mode-switch/lib/main.js +9915888: internal_packages/mode-switch/lib/mode-toggle.js +9918736: internal_packages/notification-mailto/lib/main.js +9920876: src/default-client-helper.js +9923701: internal_packages/notification-update-available/stylesheets/release-bar.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +9924225: internal_packages/notification-update-available/lib/main.js +9927264: internal_packages/notifications/stylesheets/notifications.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +9931964: internal_packages/notifications/lib/main.js +25522410: internal_packages/notifications/lib/sidebar/activity-sidebar.js +75770954: node_modules/react-addons-css-transition-group/package.json +75770892: node_modules/react-addons-css-transition-group/index.js +6285435: node_modules/react/lib/ReactCSSTransitionGroup.js +6288370: node_modules/react/lib/ReactTransitionGroup.js +6294511: node_modules/react/lib/ReactTransitionChildMapping.js +6300645: node_modules/react/lib/ReactCSSTransitionGroupChild.js +77653837: node_modules/react/node_modules/fbjs/lib/CSSCore.js +6305123: node_modules/react/lib/ReactTransitionEvents.js +9936813: internal_packages/notifications/lib/notifications-store.js +25534637: internal_packages/notifications/lib/sidebar/streaming-sync-activity.js +25528307: internal_packages/notifications/lib/sidebar/initial-sync-activity.js +25500590: internal_packages/notifications/lib/headers/connection-status-header.js +25486624: internal_packages/notifications/lib/headers/account-error-header.js +25515642: internal_packages/notifications/lib/headers/notifications-header.js +25518220: internal_packages/notifications/lib/headers/notifications-item.js +3795593: internal_packages/nylas-private-analytics/lib/main.js +3795926: internal_packages/nylas-private-fonts/stylesheets/nylas-fonts.less +3709902: static/variables/ui-variables.less +3797131: internal_packages/nylas-private-fonts/lib/main.js +3797276: internal_packages/nylas-private-sounds/lib/main.js +9948811: internal_packages/open-tracking/stylesheets/main.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +9949921: internal_packages/open-tracking/lib/main.js +9954068: internal_packages/open-tracking/lib/open-tracking-button.js +9962734: internal_packages/open-tracking/lib/open-tracking-constants.js +9964291: internal_packages/open-tracking/package.json +9965050: internal_packages/open-tracking/lib/open-tracking-icon.js +9974239: internal_packages/open-tracking/lib/open-tracking-message-status.js +9983231: internal_packages/open-tracking/lib/open-tracking-composer-extension.js +29621096: internal_packages/participant-profile/stylesheets/participant-profile.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +27770485: internal_packages/participant-profile/lib/main.js +27771226: internal_packages/participant-profile/lib/participant-profile-store.js +27766292: internal_packages/participant-profile/lib/clearbit-data-source.js +27772813: internal_packages/participant-profile/lib/sidebar-participant-profile.js +27794981: internal_packages/participant-profile/lib/sidebar-related-threads.js +10002861: internal_packages/plugins/stylesheets/plugins.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +10005819: internal_packages/plugins/lib/main.js +10584362: internal_packages/plugins/lib/preferences-plugins.js +10591138: internal_packages/plugins/lib/tabs-store.js +10593603: internal_packages/plugins/lib/plugins-actions.js +10595942: internal_packages/plugins/lib/tabs.js +10597146: internal_packages/plugins/lib/tab-installed.js +10612734: internal_packages/plugins/lib/package-set.js +10619274: internal_packages/plugins/lib/package.js +10636183: internal_packages/plugins/lib/packages-store.js +10007988: internal_packages/preferences/stylesheets/preferences-accounts.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +10010736: internal_packages/preferences/stylesheets/preferences-mail-rules.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +10013552: internal_packages/preferences/stylesheets/preferences.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +10018740: internal_packages/preferences/lib/main.js +10942956: internal_packages/preferences/lib/preferences-root.js +36888774: internal_packages/preferences/lib/preferences-tabs-bar.js +10678179: internal_packages/preferences/lib/tabs/preferences-general.js +10687249: internal_packages/preferences/lib/tabs/config-schema-item.js +10702281: internal_packages/preferences/lib/tabs/workspace-section.js +10744972: internal_packages/preferences/lib/tabs/sending-section.js +10752957: internal_packages/preferences/lib/tabs/preferences-accounts.js +10763596: internal_packages/preferences/lib/tabs/preferences-account-list.js +10775702: internal_packages/preferences/lib/tabs/preferences-account-details.js +36901668: internal_packages/preferences/lib/tabs/preferences-appearance.js +10803666: internal_packages/preferences/lib/tabs/preferences-keymaps.js +10835061: internal_packages/preferences/lib/tabs/preferences-mail-rules.js +10027324: internal_packages/print/lib/main.js +10028732: internal_packages/print/lib/printer.js +10034622: internal_packages/print/lib/print-window.js +36971547: internal_packages/remove-tracking-pixels/lib/main.js +6217392: internal_packages/screenshot-mode/lib/main.js +10043968: internal_packages/send-and-archive/styles/send-and-archive.less +3709902: static/variables/ui-variables.less +10043990: internal_packages/send-and-archive/lib/main.js +10044567: internal_packages/send-and-archive/lib/send-and-archive-extension.js +3795009: internal_packages/send-later/package.json +10046563: internal_packages/send-later/stylesheets/send-later.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +10047505: internal_packages/send-later/lib/main.js +37038743: internal_packages/send-later/lib/send-later-button.js +10048157: internal_packages/send-later/lib/send-later-popover.js +10062336: src/date-utils.js +10080490: node_modules/chrono-node/src/chrono.js +10082187: node_modules/chrono-node/src/options.js +10084390: node_modules/chrono-node/src/parsers/parser.js +10087416: node_modules/chrono-node/src/parsers/EN/ENISOFormatParser.js +10090699: node_modules/chrono-node/src/result.js +10094393: node_modules/chrono-node/src/parsers/EN/ENDeadlineFormatParser.js +10096163: node_modules/chrono-node/src/parsers/EN/ENMonthNameLittleEndianParser.js +10099767: node_modules/chrono-node/src/utils/EN.js +10100643: node_modules/chrono-node/src/parsers/EN/ENMonthNameMiddleEndianParser.js +54657192: node_modules/chrono-node/src/parsers/EN/ENMonthNameParser.js +10104644: node_modules/chrono-node/src/parsers/EN/ENSlashDateFormatParser.js +10107976: node_modules/chrono-node/src/parsers/EN/ENSlashDateFormatStartWithYearParser.js +54659809: node_modules/chrono-node/src/parsers/EN/ENSlashMonthFormatParser.js +10109509: node_modules/chrono-node/src/parsers/EN/ENTimeAgoFormatParser.js +10112162: node_modules/chrono-node/src/parsers/EN/ENTimeExpressionParser.js +10119452: node_modules/chrono-node/src/parsers/EN/ENWeekdayParser.js +10122038: node_modules/chrono-node/src/parsers/EN/ENCasualDateParser.js +10124502: node_modules/chrono-node/src/parsers/JP/JPStandardParser.js +10127111: node_modules/chrono-node/src/utils/JP.js +10128540: node_modules/chrono-node/src/parsers/JP/JPCasualDateParser.js +54661209: node_modules/chrono-node/src/parsers/ES/ESCasualDateParser.js +54664898: node_modules/chrono-node/src/parsers/ES/ESDeadlineFormatParser.js +54673468: node_modules/chrono-node/src/parsers/ES/ESTimeAgoFormatParser.js +54675930: node_modules/chrono-node/src/parsers/ES/ESTimeExpressionParser.js +54682534: node_modules/chrono-node/src/parsers/ES/ESWeekdayParser.js +54666650: node_modules/chrono-node/src/parsers/ES/ESMonthNameLittleEndianParser.js +54686880: node_modules/chrono-node/src/utils/ES.js +54670068: node_modules/chrono-node/src/parsers/ES/ESSlashDateFormatParser.js +10130127: node_modules/chrono-node/src/refiners/refiner.js +10131338: node_modules/chrono-node/src/refiners/OverlapRemovalRefiner.js +10132351: node_modules/chrono-node/src/refiners/ExtractTimezoneOffsetRefiner.js +10133766: node_modules/chrono-node/src/refiners/ExtractTimezoneAbbrRefiner.js +10137202: node_modules/chrono-node/src/refiners/UnlikelyFormatFilter.js +10137518: node_modules/chrono-node/src/refiners/EN/ENMergeDateTimeRefiner.js +10141811: node_modules/chrono-node/src/refiners/EN/ENMergeDateRangeRefiner.js +10144348: node_modules/chrono-node/src/refiners/JP/JPMergeDateRangeRefiner.js +10061936: internal_packages/send-later/lib/send-later-actions.js +10062177: internal_packages/send-later/lib/send-later-constants.js +3795009: internal_packages/send-later/package.json +10144604: internal_packages/send-later/lib/send-later-store.js +10146708: internal_packages/send-later/lib/send-later-status.js +10154325: internal_packages/sidebar-fullcontact/stylesheets/sidebar-fullcontact.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +10155637: internal_packages/sidebar-fullcontact/lib/main.js +10156198: internal_packages/sidebar-fullcontact/lib/sidebar-fullcontact.js +10158666: internal_packages/sidebar-fullcontact/lib/fullcontact-store.js +10160817: internal_packages/sidebar-fullcontact/lib/sidebar-fullcontact-details.js +10169244: internal_packages/system-tray/lib/main.js +10170796: internal_packages/system-tray/lib/system-tray-icon-store.js +37317747: internal_packages/theme-picker/styles/theme-picker.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +10189521: internal_packages/theme-picker/lib/main.js +10189999: internal_packages/theme-picker/lib/theme-picker.js +10205690: internal_packages/theme-picker/lib/theme-option.js +38809494: internal_packages/thread-list/stylesheets/selected-items-stack.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +10222854: internal_packages/thread-list/stylesheets/thread-list.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +10235013: internal_packages/thread-list/lib/main.js +10331274: internal_packages/thread-list/lib/thread-list.js +10370299: internal_packages/thread-list/lib/thread-list-columns.js +10379446: internal_packages/thread-list/lib/thread-list-quick-actions.js +10383457: internal_packages/thread-list/lib/thread-list-participants.js +10238079: internal_packages/thread-list/lib/thread-list-store.js +10313585: internal_packages/thread-list/lib/thread-list-data-source.js +10389852: internal_packages/thread-list/lib/thread-list-icon.js +10393161: internal_packages/thread-list/lib/thread-list-scroll-tooltip.js +10395728: internal_packages/thread-list/lib/thread-list-context-menu.js +10414166: internal_packages/thread-list/lib/category-removal-target-rulesets.js +38750037: internal_packages/thread-list/lib/thread-list-toolbar.js +38717639: internal_packages/thread-list/lib/injects-toolbar-buttons.js +38726612: internal_packages/thread-list/lib/message-list-toolbar.js +38735117: internal_packages/thread-list/lib/selected-items-stack.js +38756427: internal_packages/thread-list/lib/thread-toolbar-buttons.js +38900474: internal_packages/thread-search/stylesheets/search-bar.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +38846525: internal_packages/thread-search/lib/main.js +38848780: internal_packages/thread-search/lib/search-bar.js +38890613: internal_packages/thread-search/lib/search-store.js +38848436: internal_packages/thread-search/lib/search-actions.js +38857942: internal_packages/thread-search/lib/search-mailbox-perspective.js +38869907: internal_packages/thread-search/lib/search-query-subscription.js +10422956: internal_packages/thread-snooze/stylesheets/snooze-mail-label.less +3709902: static/variables/ui-variables.less +10423117: internal_packages/thread-snooze/stylesheets/snooze-popover.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +10424653: internal_packages/thread-snooze/lib/main.js +39002329: internal_packages/thread-snooze/lib/snooze-buttons.js +10425464: internal_packages/thread-snooze/lib/snooze-popover.js +10439168: internal_packages/thread-snooze/lib/snooze-actions.js +10439380: internal_packages/thread-snooze/lib/snooze-mail-label.js +10448736: internal_packages/thread-snooze/lib/snooze-constants.js +10448947: internal_packages/thread-snooze/package.json +10449468: internal_packages/thread-snooze/lib/snooze-utils.js +10453553: internal_packages/thread-snooze/lib/snooze-store.js +10456694: internal_packages/undo-redo/stylesheets/undo-redo.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +10458145: internal_packages/undo-redo/lib/main.js +10458810: internal_packages/undo-redo/lib/undo-redo-component.js +10463691: internal_packages/unread-notifications/lib/main.js +10471380: src/native-notifications.js +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +6330626: src/sheet-container.js +6336675: src/sheet.js +6363606: src/components/resizable-region.js +6372438: src/sheet-toolbar.js +94914523: src/task.js +6388649: internal_packages/nylas-private-analytics/lib/analytics-store.js +6395255: internal_packages/nylas-private-analytics/node_modules/underscore/package.json +6397211: internal_packages/nylas-private-analytics/node_modules/underscore/underscore.js +6450130: internal_packages/nylas-private-analytics/node_modules/mixpanel/package.json +6451584: internal_packages/nylas-private-analytics/node_modules/mixpanel/lib/mixpanel-node.js +3709902: static/variables/ui-variables.less +1973294: src/compile-cache.js +1766806: node_modules/fs-plus/package.json +1768751: node_modules/fs-plus/lib/fs-plus.js +3708754: static/index.less +3709902: static/variables/ui-variables.less +3717773: static/variables/ui-mixins.less +3717937: static/mixins/common-ui-elements.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3718450: static/mixins/text-emphasis.less +3718566: static/mixins/background-variant.less +3718705: static/mixins/windows.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3719008: static/normalize.less +3726688: static/type.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3731800: static/inputs.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +1789356: node_modules/fs-plus/node_modules/underscore-plus/package.json +3733216: static/buttons.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3737856: static/dropdowns.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3738091: static/workspace.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +1791321: node_modules/fs-plus/node_modules/underscore-plus/lib/underscore-plus.js +3746538: static/resizable.less +3747129: static/selection.less +3747443: static/utilities.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3748613: static/components/popover.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3750797: static/components/menu.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3752952: static/components/switch.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3753558: static/components/tokenizing-text-field.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3759326: static/components/extra.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3761494: static/components/list-tabular.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3765991: static/components/disclosure-triangle.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3766478: static/components/button-dropdown.less +3733216: static/buttons.less +3770706: static/components/scroll-region.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3773921: static/components/spinner.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3774954: static/components/generated-form.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3776905: static/components/unsafe.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3777342: static/components/key-commands-region.less +3777420: static/components/contenteditable.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3781503: static/components/editable-list.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3784402: static/components/outline-view.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3717773: static/variables/ui-mixins.less +3787888: static/components/fixed-popover.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3790891: static/components/modal.less +3718416: internal_packages/ui-light/styles/ui-variables.less +3791241: static/components/date-input.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97491765: static/components/nylas-calendar.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97490322: static/components/empty-list-state.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97489398: static/components/date-picker.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97499252: static/components/time-picker.less +1806109: node_modules/fs-plus/node_modules/underscore-plus/node_modules/underscore/package.json +3718416: internal_packages/ui-light/styles/ui-variables.less +97498180: static/components/table.less +3718416: internal_packages/ui-light/styles/ui-variables.less +97489787: static/components/editable-table.less +3718416: internal_packages/ui-light/styles/ui-variables.less +1807823: node_modules/fs-plus/node_modules/underscore-plus/node_modules/underscore/underscore.js +3791399: static/email-frame.less +3709902: static/variables/ui-variables.less +3718416: internal_packages/ui-light/styles/ui-variables.less +1853312: node_modules/fs-plus/node_modules/async/package.json +1854649: node_modules/fs-plus/node_modules/async/lib/async.js +1884050: node_modules/fs-plus/node_modules/mkdirp/package.json +1885107: node_modules/fs-plus/node_modules/mkdirp/index.js +1887478: node_modules/fs-plus/node_modules/rimraf/package.json +1889121: node_modules/fs-plus/node_modules/rimraf/rimraf.js +1978902: src/compile-support/babel.js +1980527: static/babelrc.json +1980671: src/compile-support/coffee-script.js +1981853: src/compile-support/typescript.js +1983139: node_modules/underscore/package.json +1985095: node_modules/underscore/underscore.js +2038014: node_modules/source-map-support/package.json +2039642: node_modules/source-map-support/source-map-support.js +2054178: node_modules/source-map-support/node_modules/source-map/package.json +2057217: node_modules/source-map-support/node_modules/source-map/lib/source-map.js +2057643: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-map-generator.js +2070902: node_modules/source-map-support/node_modules/source-map/node_modules/amdefine/package.json +2072148: node_modules/source-map-support/node_modules/source-map/node_modules/amdefine/amdefine.js +2082064: node_modules/source-map-support/node_modules/source-map/lib/source-map/base64-vlq.js +2086956: node_modules/source-map-support/node_modules/source-map/lib/source-map/base64.js +2088093: node_modules/source-map-support/node_modules/source-map/lib/source-map/util.js +2093422: node_modules/source-map-support/node_modules/source-map/lib/source-map/array-set.js +2096140: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-map-consumer.js +2113947: node_modules/source-map-support/node_modules/source-map/lib/source-map/binary-search.js +2117157: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-node.js +94911340: src/task-bootstrap.js +2264399: node_modules/emissary/package.json +2266273: node_modules/emissary/lib/emissary.js +2266555: node_modules/emissary/lib/helpers.js +2268129: node_modules/emissary/lib/behavior.js +2272107: node_modules/emissary/node_modules/underscore-plus/package.json +2274072: node_modules/emissary/node_modules/underscore-plus/lib/underscore-plus.js +2288860: node_modules/emissary/node_modules/underscore-plus/node_modules/underscore/package.json +2290574: node_modules/emissary/node_modules/underscore-plus/node_modules/underscore/underscore.js +2336063: node_modules/property-accessors/package.json +2337929: node_modules/property-accessors/lib/property-accessors.js +2340201: node_modules/property-accessors/node_modules/mixto/package.json +2341779: node_modules/property-accessors/node_modules/mixto/lib/mixin.js +2343179: node_modules/emissary/lib/signal.js +2352520: node_modules/emissary/lib/emitter.js +2367779: node_modules/emissary/node_modules/mixto/package.json +2369463: node_modules/emissary/node_modules/mixto/lib/mixin.js +2370863: node_modules/emissary/lib/subscriber.js +2374948: node_modules/emissary/lib/subscription.js +4222287: node_modules/request/package.json +4225420: node_modules/request/index.js +80790165: node_modules/request/node_modules/extend/package.json +80787899: node_modules/request/node_modules/extend/index.js +4229450: node_modules/request/lib/cookies.js +82838107: node_modules/request/node_modules/tough-cookie/package.json +82637881: node_modules/request/node_modules/tough-cookie/lib/cookie.js +82685639: node_modules/request/node_modules/tough-cookie/lib/pubsuffix.js +82835266: node_modules/request/node_modules/tough-cookie/lib/store.js +82675424: node_modules/request/node_modules/tough-cookie/lib/memstore.js +82683373: node_modules/request/node_modules/tough-cookie/lib/permuteDomain.js +82680938: node_modules/request/node_modules/tough-cookie/lib/pathMatch.js +82838107: node_modules/request/node_modules/tough-cookie/package.json +4432858: node_modules/request/lib/helpers.js +82361121: node_modules/request/node_modules/json-stringify-safe/package.json +82362804: node_modules/request/node_modules/json-stringify-safe/stringify.js +4435389: node_modules/request/request.js +80742369: node_modules/request/node_modules/bl/package.json +80567623: node_modules/request/node_modules/bl/bl.js +80636139: node_modules/request/node_modules/bl/node_modules/readable-stream/duplex.js +80636191: node_modules/request/node_modules/bl/node_modules/readable-stream/lib/_stream_duplex.js +80720291: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/process-nextick-args/package.json +80718759: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/process-nextick-args/index.js +80705839: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/core-util-is/package.json +80702818: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/core-util-is/lib/util.js +80712444: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/inherits/package.json +80711730: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/inherits/inherits.js +80638643: node_modules/request/node_modules/bl/node_modules/readable-stream/lib/_stream_readable.js +80716737: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/isarray/package.json +80716605: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/isarray/index.js +80670635: node_modules/request/node_modules/bl/node_modules/readable-stream/lib/_stream_writable.js +80738031: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/util-deprecate/package.json +80737908: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/util-deprecate/node.js +81531974: node_modules/request/node_modules/hawk/package.json +81345592: node_modules/request/node_modules/hawk/lib/index.js +81424556: node_modules/request/node_modules/hawk/node_modules/boom/package.json +81416906: node_modules/request/node_modules/hawk/node_modules/boom/lib/index.js +81515577: node_modules/request/node_modules/hawk/node_modules/hoek/package.json +81490930: node_modules/request/node_modules/hawk/node_modules/hoek/lib/index.js +81488262: node_modules/request/node_modules/hawk/node_modules/hoek/lib/escape.js +81530565: node_modules/request/node_modules/hawk/node_modules/sntp/package.json +81520773: node_modules/request/node_modules/hawk/node_modules/sntp/index.js +81520807: node_modules/request/node_modules/hawk/node_modules/sntp/lib/index.js +81345973: node_modules/request/node_modules/hawk/lib/server.js +81429631: node_modules/request/node_modules/hawk/node_modules/cryptiles/package.json +81428267: node_modules/request/node_modules/hawk/node_modules/cryptiles/lib/index.js +81342006: node_modules/request/node_modules/hawk/lib/crypto.js +81364519: node_modules/request/node_modules/hawk/lib/utils.js +81331415: node_modules/request/node_modules/hawk/lib/client.js +80485113: node_modules/request/node_modules/aws-sign2/package.json +80480692: node_modules/request/node_modules/aws-sign2/index.js +82337952: node_modules/request/node_modules/http-signature/package.json +81551606: node_modules/request/node_modules/http-signature/lib/index.js +81552232: node_modules/request/node_modules/http-signature/lib/parser.js +81591249: node_modules/request/node_modules/http-signature/node_modules/assert-plus/package.json +81585919: node_modules/request/node_modules/http-signature/node_modules/assert-plus/assert.js +81574938: node_modules/request/node_modules/http-signature/lib/utils.js +82335507: node_modules/request/node_modules/http-signature/node_modules/sshpk/package.json +81887698: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/index.js +81888429: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/key.js +81955466: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/assert-plus/package.json +81950012: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/assert-plus/assert.js +81830178: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/algs.js +81847974: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/fingerprint.js +81845672: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/errors.js +81911179: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/utils.js +81895828: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/private-key.js +81901963: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/signature.js +81943093: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/package.json +81942773: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/index.js +81928490: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/index.js +81928251: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/errors.js +81934548: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/types.js +81928959: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/reader.js +81935186: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/writer.js +81908010: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/ssh-buffer.js +81843337: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/ed-compat.js +81851404: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/auto.js +81853302: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pem.js +81858111: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs1.js +81865807: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs8.js +81881346: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh-private.js +81877740: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/rfc4253.js +81884606: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh.js +81835020: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/dhe.js +81562004: node_modules/request/node_modules/http-signature/lib/signer.js +81802301: node_modules/request/node_modules/http-signature/node_modules/jsprim/package.json +81602307: node_modules/request/node_modules/http-signature/node_modules/jsprim/lib/jsprim.js +81636568: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/extsprintf/package.json +81632804: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/extsprintf/lib/extsprintf.js +81801412: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/verror/package.json +81797823: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/verror/lib/verror.js +81774451: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/json-schema/package.json +81763963: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/json-schema/lib/validate.js +81577777: node_modules/request/node_modules/http-signature/lib/verify.js +82533240: node_modules/request/node_modules/mime-types/package.json +82371286: node_modules/request/node_modules/mime-types/index.js +82530646: node_modules/request/node_modules/mime-types/node_modules/mime-db/package.json +82530510: node_modules/request/node_modules/mime-types/node_modules/mime-db/index.js +82387545: node_modules/request/node_modules/mime-types/node_modules/mime-db/db.json +82606571: node_modules/request/node_modules/stringstream/package.json +82607891: node_modules/request/node_modules/stringstream/stringstream.js +80756086: node_modules/request/node_modules/caseless/package.json +80754327: node_modules/request/node_modules/caseless/index.js +80805500: node_modules/request/node_modules/forever-agent/package.json +80801324: node_modules/request/node_modules/forever-agent/index.js +80986121: node_modules/request/node_modules/form-data/package.json +80814876: node_modules/request/node_modules/form-data/lib/form_data.js +80777465: node_modules/request/node_modules/combined-stream/package.json +80764215: node_modules/request/node_modules/combined-stream/lib/combined_stream.js +80775847: node_modules/request/node_modules/combined-stream/node_modules/delayed-stream/package.json +80773528: node_modules/request/node_modules/combined-stream/node_modules/delayed-stream/lib/delayed_stream.js +80983078: node_modules/request/node_modules/form-data/node_modules/async/package.json +80944514: node_modules/request/node_modules/form-data/node_modules/async/lib/async.js +80826033: node_modules/request/node_modules/form-data/lib/populate.js +82349146: node_modules/request/node_modules/isstream/package.json +82348558: node_modules/request/node_modules/isstream/isstream.js +82342515: node_modules/request/node_modules/is-typedarray/package.json +82341499: node_modules/request/node_modules/is-typedarray/index.js +4479214: node_modules/request/lib/getProxyFromURI.js +4481482: node_modules/request/lib/querystring.js +82601463: node_modules/request/node_modules/qs/package.json +82587962: node_modules/request/node_modules/qs/lib/index.js +82593363: node_modules/request/node_modules/qs/lib/stringify.js +82597342: node_modules/request/node_modules/qs/lib/utils.js +82588115: node_modules/request/node_modules/qs/lib/parse.js +4482815: node_modules/request/lib/har.js +81178912: node_modules/request/node_modules/har-validator/package.json +81001381: node_modules/request/node_modules/har-validator/lib/index.js +81176502: node_modules/request/node_modules/har-validator/node_modules/pinkie-promise/package.json +81163939: node_modules/request/node_modules/har-validator/node_modules/pinkie-promise/index.js +81001935: node_modules/request/node_modules/har-validator/lib/runner.js +81005359: node_modules/request/node_modules/har-validator/lib/schemas/index.js +81002525: node_modules/request/node_modules/har-validator/lib/schemas/cache.json +81002712: node_modules/request/node_modules/har-validator/lib/schemas/cacheEntry.json +81003206: node_modules/request/node_modules/har-validator/lib/schemas/content.json +81003583: node_modules/request/node_modules/har-validator/lib/schemas/cookie.json +81004081: node_modules/request/node_modules/har-validator/lib/schemas/creator.json +81004311: node_modules/request/node_modules/har-validator/lib/schemas/entry.json +81005242: node_modules/request/node_modules/har-validator/lib/schemas/har.json +81007111: node_modules/request/node_modules/har-validator/lib/schemas/log.json +81007604: node_modules/request/node_modules/har-validator/lib/schemas/page.json +81008181: node_modules/request/node_modules/har-validator/lib/schemas/pageTimings.json +81008406: node_modules/request/node_modules/har-validator/lib/schemas/postData.json +81009060: node_modules/request/node_modules/har-validator/lib/schemas/record.json +81009286: node_modules/request/node_modules/har-validator/lib/schemas/request.json +81010139: node_modules/request/node_modules/har-validator/lib/schemas/response.json +81010946: node_modules/request/node_modules/har-validator/lib/schemas/timings.json +81001195: node_modules/request/node_modules/har-validator/lib/error.js +81161590: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/package.json +81108729: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/index.js +81147021: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-object-property/package.json +81132607: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-object-property/index.js +81145615: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-object-property/node_modules/is-property/package.json +81134601: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-object-property/node_modules/is-property/is-property.js +81128902: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-function/package.json +81127606: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-function/index.js +81151111: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/jsonpointer/package.json +81149530: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/jsonpointer/jsonpointer.js +81158029: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/xtend/package.json +81157276: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/xtend/immutable.js +81106380: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/formats.js +4533728: node_modules/request/lib/auth.js +1962761: node_modules/node-uuid/package.json +1964629: node_modules/node-uuid/uuid.js +4550226: node_modules/request/lib/oauth.js +82548387: node_modules/request/node_modules/oauth-sign/package.json +82544802: node_modules/request/node_modules/oauth-sign/index.js +4568756: node_modules/request/lib/multipart.js +4585628: node_modules/request/lib/redirect.js +4594531: node_modules/request/lib/tunnel.js +82856379: node_modules/request/node_modules/tunnel-agent/package.json +82849535: node_modules/request/node_modules/tunnel-agent/index.js +3599638: src/apm-wrapper.js +3613961: src/buffered-process.js +94914523: src/task.js +1973294: src/compile-cache.js +1766806: node_modules/fs-plus/package.json +1768751: node_modules/fs-plus/lib/fs-plus.js +1789356: node_modules/fs-plus/node_modules/underscore-plus/package.json +1791321: node_modules/fs-plus/node_modules/underscore-plus/lib/underscore-plus.js +1806109: node_modules/fs-plus/node_modules/underscore-plus/node_modules/underscore/package.json +1807823: node_modules/fs-plus/node_modules/underscore-plus/node_modules/underscore/underscore.js +1853312: node_modules/fs-plus/node_modules/async/package.json +1854649: node_modules/fs-plus/node_modules/async/lib/async.js +1884050: node_modules/fs-plus/node_modules/mkdirp/package.json +1885107: node_modules/fs-plus/node_modules/mkdirp/index.js +1887478: node_modules/fs-plus/node_modules/rimraf/package.json +1889121: node_modules/fs-plus/node_modules/rimraf/rimraf.js +1978902: src/compile-support/babel.js +1980527: static/babelrc.json +1980671: src/compile-support/coffee-script.js +1981853: src/compile-support/typescript.js +1983139: node_modules/underscore/package.json +1985095: node_modules/underscore/underscore.js +2038014: node_modules/source-map-support/package.json +2039642: node_modules/source-map-support/source-map-support.js +2054178: node_modules/source-map-support/node_modules/source-map/package.json +2057217: node_modules/source-map-support/node_modules/source-map/lib/source-map.js +2057643: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-map-generator.js +2070902: node_modules/source-map-support/node_modules/source-map/node_modules/amdefine/package.json +2072148: node_modules/source-map-support/node_modules/source-map/node_modules/amdefine/amdefine.js +2082064: node_modules/source-map-support/node_modules/source-map/lib/source-map/base64-vlq.js +2086956: node_modules/source-map-support/node_modules/source-map/lib/source-map/base64.js +2088093: node_modules/source-map-support/node_modules/source-map/lib/source-map/util.js +2093422: node_modules/source-map-support/node_modules/source-map/lib/source-map/array-set.js +2096140: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-map-consumer.js +2113947: node_modules/source-map-support/node_modules/source-map/lib/source-map/binary-search.js +2117157: node_modules/source-map-support/node_modules/source-map/lib/source-map/source-node.js +94911340: src/task-bootstrap.js +2264399: node_modules/emissary/package.json +2266273: node_modules/emissary/lib/emissary.js +2266555: node_modules/emissary/lib/helpers.js +2268129: node_modules/emissary/lib/behavior.js +2272107: node_modules/emissary/node_modules/underscore-plus/package.json +2274072: node_modules/emissary/node_modules/underscore-plus/lib/underscore-plus.js +2288860: node_modules/emissary/node_modules/underscore-plus/node_modules/underscore/package.json +2290574: node_modules/emissary/node_modules/underscore-plus/node_modules/underscore/underscore.js +2336063: node_modules/property-accessors/package.json +2337929: node_modules/property-accessors/lib/property-accessors.js +2340201: node_modules/property-accessors/node_modules/mixto/package.json +2341779: node_modules/property-accessors/node_modules/mixto/lib/mixin.js +2343179: node_modules/emissary/lib/signal.js +2352520: node_modules/emissary/lib/emitter.js +2367779: node_modules/emissary/node_modules/mixto/package.json +2369463: node_modules/emissary/node_modules/mixto/lib/mixin.js +2370863: node_modules/emissary/lib/subscriber.js +2374948: node_modules/emissary/lib/subscription.js +4222287: node_modules/request/package.json +4225420: node_modules/request/index.js +80790165: node_modules/request/node_modules/extend/package.json +80787899: node_modules/request/node_modules/extend/index.js +4229450: node_modules/request/lib/cookies.js +82838107: node_modules/request/node_modules/tough-cookie/package.json +82637881: node_modules/request/node_modules/tough-cookie/lib/cookie.js +82685639: node_modules/request/node_modules/tough-cookie/lib/pubsuffix.js +82835266: node_modules/request/node_modules/tough-cookie/lib/store.js +82675424: node_modules/request/node_modules/tough-cookie/lib/memstore.js +82683373: node_modules/request/node_modules/tough-cookie/lib/permuteDomain.js +82680938: node_modules/request/node_modules/tough-cookie/lib/pathMatch.js +82838107: node_modules/request/node_modules/tough-cookie/package.json +4432858: node_modules/request/lib/helpers.js +82361121: node_modules/request/node_modules/json-stringify-safe/package.json +82362804: node_modules/request/node_modules/json-stringify-safe/stringify.js +4435389: node_modules/request/request.js +80742369: node_modules/request/node_modules/bl/package.json +80567623: node_modules/request/node_modules/bl/bl.js +80636139: node_modules/request/node_modules/bl/node_modules/readable-stream/duplex.js +80636191: node_modules/request/node_modules/bl/node_modules/readable-stream/lib/_stream_duplex.js +80720291: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/process-nextick-args/package.json +80718759: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/process-nextick-args/index.js +80705839: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/core-util-is/package.json +80702818: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/core-util-is/lib/util.js +80712444: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/inherits/package.json +80711730: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/inherits/inherits.js +80638643: node_modules/request/node_modules/bl/node_modules/readable-stream/lib/_stream_readable.js +80716737: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/isarray/package.json +80716605: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/isarray/index.js +80670635: node_modules/request/node_modules/bl/node_modules/readable-stream/lib/_stream_writable.js +80738031: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/util-deprecate/package.json +80737908: node_modules/request/node_modules/bl/node_modules/readable-stream/node_modules/util-deprecate/node.js +81531974: node_modules/request/node_modules/hawk/package.json +81345592: node_modules/request/node_modules/hawk/lib/index.js +81424556: node_modules/request/node_modules/hawk/node_modules/boom/package.json +81416906: node_modules/request/node_modules/hawk/node_modules/boom/lib/index.js +81515577: node_modules/request/node_modules/hawk/node_modules/hoek/package.json +81490930: node_modules/request/node_modules/hawk/node_modules/hoek/lib/index.js +81488262: node_modules/request/node_modules/hawk/node_modules/hoek/lib/escape.js +81530565: node_modules/request/node_modules/hawk/node_modules/sntp/package.json +81520773: node_modules/request/node_modules/hawk/node_modules/sntp/index.js +81520807: node_modules/request/node_modules/hawk/node_modules/sntp/lib/index.js +81345973: node_modules/request/node_modules/hawk/lib/server.js +81429631: node_modules/request/node_modules/hawk/node_modules/cryptiles/package.json +81428267: node_modules/request/node_modules/hawk/node_modules/cryptiles/lib/index.js +81342006: node_modules/request/node_modules/hawk/lib/crypto.js +81364519: node_modules/request/node_modules/hawk/lib/utils.js +81331415: node_modules/request/node_modules/hawk/lib/client.js +80485113: node_modules/request/node_modules/aws-sign2/package.json +80480692: node_modules/request/node_modules/aws-sign2/index.js +82337952: node_modules/request/node_modules/http-signature/package.json +81551606: node_modules/request/node_modules/http-signature/lib/index.js +81552232: node_modules/request/node_modules/http-signature/lib/parser.js +81591249: node_modules/request/node_modules/http-signature/node_modules/assert-plus/package.json +81585919: node_modules/request/node_modules/http-signature/node_modules/assert-plus/assert.js +81574938: node_modules/request/node_modules/http-signature/lib/utils.js +82335507: node_modules/request/node_modules/http-signature/node_modules/sshpk/package.json +81887698: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/index.js +81888429: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/key.js +81955466: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/assert-plus/package.json +81950012: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/assert-plus/assert.js +81830178: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/algs.js +81847974: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/fingerprint.js +81845672: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/errors.js +81911179: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/utils.js +81895828: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/private-key.js +81901963: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/signature.js +81943093: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/package.json +81942773: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/index.js +81928490: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/index.js +81928251: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/errors.js +81934548: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/types.js +81928959: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/reader.js +81935186: node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/asn1/lib/ber/writer.js +81908010: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/ssh-buffer.js +81843337: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/ed-compat.js +81851404: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/auto.js +81853302: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pem.js +81858111: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs1.js +81865807: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/pkcs8.js +81881346: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh-private.js +81877740: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/rfc4253.js +81884606: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/formats/ssh.js +81835020: node_modules/request/node_modules/http-signature/node_modules/sshpk/lib/dhe.js +81562004: node_modules/request/node_modules/http-signature/lib/signer.js +81802301: node_modules/request/node_modules/http-signature/node_modules/jsprim/package.json +81602307: node_modules/request/node_modules/http-signature/node_modules/jsprim/lib/jsprim.js +81636568: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/extsprintf/package.json +81632804: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/extsprintf/lib/extsprintf.js +81801412: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/verror/package.json +81797823: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/verror/lib/verror.js +81774451: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/json-schema/package.json +81763963: node_modules/request/node_modules/http-signature/node_modules/jsprim/node_modules/json-schema/lib/validate.js +81577777: node_modules/request/node_modules/http-signature/lib/verify.js +82533240: node_modules/request/node_modules/mime-types/package.json +82371286: node_modules/request/node_modules/mime-types/index.js +82530646: node_modules/request/node_modules/mime-types/node_modules/mime-db/package.json +82530510: node_modules/request/node_modules/mime-types/node_modules/mime-db/index.js +82387545: node_modules/request/node_modules/mime-types/node_modules/mime-db/db.json +82606571: node_modules/request/node_modules/stringstream/package.json +82607891: node_modules/request/node_modules/stringstream/stringstream.js +80756086: node_modules/request/node_modules/caseless/package.json +80754327: node_modules/request/node_modules/caseless/index.js +80805500: node_modules/request/node_modules/forever-agent/package.json +80801324: node_modules/request/node_modules/forever-agent/index.js +80986121: node_modules/request/node_modules/form-data/package.json +80814876: node_modules/request/node_modules/form-data/lib/form_data.js +80777465: node_modules/request/node_modules/combined-stream/package.json +80764215: node_modules/request/node_modules/combined-stream/lib/combined_stream.js +80775847: node_modules/request/node_modules/combined-stream/node_modules/delayed-stream/package.json +80773528: node_modules/request/node_modules/combined-stream/node_modules/delayed-stream/lib/delayed_stream.js +80983078: node_modules/request/node_modules/form-data/node_modules/async/package.json +80944514: node_modules/request/node_modules/form-data/node_modules/async/lib/async.js +80826033: node_modules/request/node_modules/form-data/lib/populate.js +82349146: node_modules/request/node_modules/isstream/package.json +82348558: node_modules/request/node_modules/isstream/isstream.js +82342515: node_modules/request/node_modules/is-typedarray/package.json +82341499: node_modules/request/node_modules/is-typedarray/index.js +4479214: node_modules/request/lib/getProxyFromURI.js +4481482: node_modules/request/lib/querystring.js +82601463: node_modules/request/node_modules/qs/package.json +82587962: node_modules/request/node_modules/qs/lib/index.js +82593363: node_modules/request/node_modules/qs/lib/stringify.js +82597342: node_modules/request/node_modules/qs/lib/utils.js +82588115: node_modules/request/node_modules/qs/lib/parse.js +4482815: node_modules/request/lib/har.js +81178912: node_modules/request/node_modules/har-validator/package.json +81001381: node_modules/request/node_modules/har-validator/lib/index.js +81176502: node_modules/request/node_modules/har-validator/node_modules/pinkie-promise/package.json +81163939: node_modules/request/node_modules/har-validator/node_modules/pinkie-promise/index.js +81001935: node_modules/request/node_modules/har-validator/lib/runner.js +81005359: node_modules/request/node_modules/har-validator/lib/schemas/index.js +81002525: node_modules/request/node_modules/har-validator/lib/schemas/cache.json +81002712: node_modules/request/node_modules/har-validator/lib/schemas/cacheEntry.json +81003206: node_modules/request/node_modules/har-validator/lib/schemas/content.json +81003583: node_modules/request/node_modules/har-validator/lib/schemas/cookie.json +81004081: node_modules/request/node_modules/har-validator/lib/schemas/creator.json +81004311: node_modules/request/node_modules/har-validator/lib/schemas/entry.json +81005242: node_modules/request/node_modules/har-validator/lib/schemas/har.json +81007111: node_modules/request/node_modules/har-validator/lib/schemas/log.json +81007604: node_modules/request/node_modules/har-validator/lib/schemas/page.json +81008181: node_modules/request/node_modules/har-validator/lib/schemas/pageTimings.json +81008406: node_modules/request/node_modules/har-validator/lib/schemas/postData.json +81009060: node_modules/request/node_modules/har-validator/lib/schemas/record.json +81009286: node_modules/request/node_modules/har-validator/lib/schemas/request.json +81010139: node_modules/request/node_modules/har-validator/lib/schemas/response.json +81010946: node_modules/request/node_modules/har-validator/lib/schemas/timings.json +81001195: node_modules/request/node_modules/har-validator/lib/error.js +81161590: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/package.json +81108729: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/index.js +81147021: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-object-property/package.json +81132607: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-object-property/index.js +81145615: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-object-property/node_modules/is-property/package.json +81134601: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-object-property/node_modules/is-property/is-property.js +81128902: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-function/package.json +81127606: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/generate-function/index.js +81151111: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/jsonpointer/package.json +81149530: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/jsonpointer/jsonpointer.js +81158029: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/xtend/package.json +81157276: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/node_modules/xtend/immutable.js +81106380: node_modules/request/node_modules/har-validator/node_modules/is-my-json-valid/formats.js +4533728: node_modules/request/lib/auth.js +1962761: node_modules/node-uuid/package.json +1964629: node_modules/node-uuid/uuid.js +4550226: node_modules/request/lib/oauth.js +82548387: node_modules/request/node_modules/oauth-sign/package.json +82544802: node_modules/request/node_modules/oauth-sign/index.js +4568756: node_modules/request/lib/multipart.js +4585628: node_modules/request/lib/redirect.js +4594531: node_modules/request/lib/tunnel.js +82856379: node_modules/request/node_modules/tunnel-agent/package.json +82849535: node_modules/request/node_modules/tunnel-agent/index.js diff --git a/packages/client-app/build/resources/linux/debian/control.in b/packages/client-app/build/resources/linux/debian/control.in new file mode 100644 index 0000000000..7a6693e0cc --- /dev/null +++ b/packages/client-app/build/resources/linux/debian/control.in @@ -0,0 +1,10 @@ +Package: <%= name %> +Version: <%= version %> +Depends: libgnome-keyring0, gir1.2-gnomekeyring-1.0, git, gconf2, gconf-service, libgtk2.0-0, libudev0 | libudev1, libgcrypt11 | libgcrypt20, libnotify4, libxtst6, libnss3, python, gvfs-bin, xdg-utils +Section: <%= section %> +Priority: optional +Architecture: <%= arch %> +Installed-Size: <%= installedSize %> +Maintainer: <%= maintainer %> +Description: <%= description %> + <%= description %> diff --git a/packages/client-app/build/resources/linux/debian/lintian-overrides b/packages/client-app/build/resources/linux/debian/lintian-overrides new file mode 100644 index 0000000000..0c946dfc06 --- /dev/null +++ b/packages/client-app/build/resources/linux/debian/lintian-overrides @@ -0,0 +1,9 @@ +nylas: arch-dependent-file-in-usr-share +nylas: changelog-file-missing-in-native-package +nylas: copyright-file-contains-full-apache-2-license +nylas: copyright-should-refer-to-common-license-file-for-apache-2 +nylas: copyright-should-refer-to-common-license-file-for-lgpl +nylas: embedded-library +nylas: package-installs-python-bytecode +nylas: unstripped-binary-or-object +nylas: extra-license-file diff --git a/packages/client-app/build/resources/linux/debian/postinst b/packages/client-app/build/resources/linux/debian/postinst new file mode 100755 index 0000000000..42c4b3e1b2 --- /dev/null +++ b/packages/client-app/build/resources/linux/debian/postinst @@ -0,0 +1,161 @@ +#!/bin/sh +# postinst script for Nylas +# +# see: dh_installdeb(1) + +# summary of how this script can be called: +# * `configure' +# * abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +UBUNTU_CODENAMES="precise trusty utopic vivid" # "xenial is not yet available in nylas repos." +DEBIAN_CODENAMES="squeeze wheezy jessie sid" + +case "$1" in + configure) + gtk-update-icon-cache /usr/share/icons/hicolor > /dev/null 2>&1 + + DISTRO=`lsb_release -s -i` + + if [ "$DISTRO" = "Ubuntu" ] || [ "$DISTRO" = "elementary OS" ] || [ "$DISTRO" = "LinuxMint" ] ; then + DISTS=$UBUNTU_CODENAMES + DISTRO="ubuntu" + elif [ "$DISTRO" = "Debian" ]; then + DISTS=$DEBIAN_CODENAMES + DISTRO="debian" + else + echo "You are not running Debian, Ubuntu, ElementaryOS or LinuxMint. Not adding Nylas repository." + DISTRO="" + fi + + if [ -n "$DISTRO" ]; then + # Add the Nylas repository. + # Copyright (c) 2009 The Chromium Authors. All rights reserved. + # Use of this source code is governed by a BSD-style license. + + # Install the repository signing key + install_key() { + APT_KEY="`which apt-key 2> /dev/null`" + if [ -x "$APT_KEY" ]; then + "$APT_KEY" add - >/dev/null 2>&1 </dev/null | cut -d ':' -f 1) + if [ -n "$SOURCELIST" ]; then + return 0 + fi + + printf "$REPOCONFIG\n" > "$APT_SOURCESDIR/nylas.list" + if [ $? -eq 0 ]; then + return 1 + fi + fi + return 2 + } + + install_key + update_sources_lists + + fi + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument '$1'" >&2 + exit 1 + ;; +esac + +set -e + +exit 0 diff --git a/packages/client-app/build/resources/linux/debian/postrm b/packages/client-app/build/resources/linux/debian/postrm new file mode 100755 index 0000000000..33c8300657 --- /dev/null +++ b/packages/client-app/build/resources/linux/debian/postrm @@ -0,0 +1,58 @@ +#!/bin/sh +# Remove the Nylas repository. +# Copyright (c) 2009 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license. + +set -e +action="$1" + +# Only do complete clean-up on purge. +if [ "$action" != "purge" ] ; then + exit 0 +fi + +APT_GET="`which apt-get 2> /dev/null`" +APT_CONFIG="`which apt-config 2> /dev/null`" + +# Parse apt configuration and return requested variable value. +apt_config_val() { + APTVAR="$1" + if [ -x "$APT_CONFIG" ]; then + "$APT_CONFIG" dump | sed -e "/^$APTVAR /"'!d' -e "s/^$APTVAR \"\(.*\)\".*/\1/" + fi +} + +uninstall_key() { + APT_KEY="`which apt-key 2> /dev/null`" + if [ -x "$APT_KEY" ]; then + # don't fail if the key wasn't found + "$APT_KEY" rm 7D0ACF4A >/dev/null 2>&1 || true + fi +} + +# Set variables for the locations of the apt sources lists. +find_apt_sources() { + APTDIR=$(apt_config_val Dir) + APTETC=$(apt_config_val 'Dir::Etc') + APT_SOURCES="$APTDIR$APTETC$(apt_config_val 'Dir::Etc::sourcelist')" + APT_SOURCESDIR="$APTDIR$APTETC$(apt_config_val 'Dir::Etc::sourceparts')" +} + +# Remove a repository from the apt sources. +# Returns: +# 0 - successfully removed, or not configured +# 1 - failed to remove +clean_sources_lists() { + find_apt_sources + + if [ -d "$APT_SOURCESDIR" ]; then + rm -f "$APT_SOURCESDIR/nylas.list" + fi + + return 0 +} + +uninstall_key +clean_sources_lists + +exit 0 diff --git a/packages/client-app/build/resources/linux/icons/128.png b/packages/client-app/build/resources/linux/icons/128.png new file mode 100644 index 0000000000..fb86db5b36 Binary files /dev/null and b/packages/client-app/build/resources/linux/icons/128.png differ diff --git a/packages/client-app/build/resources/linux/icons/16.png b/packages/client-app/build/resources/linux/icons/16.png new file mode 100644 index 0000000000..dd2b7d9821 Binary files /dev/null and b/packages/client-app/build/resources/linux/icons/16.png differ diff --git a/packages/client-app/build/resources/linux/icons/256.png b/packages/client-app/build/resources/linux/icons/256.png new file mode 100644 index 0000000000..0a03af5598 Binary files /dev/null and b/packages/client-app/build/resources/linux/icons/256.png differ diff --git a/packages/client-app/build/resources/linux/icons/32.png b/packages/client-app/build/resources/linux/icons/32.png new file mode 100644 index 0000000000..7e67b8eea5 Binary files /dev/null and b/packages/client-app/build/resources/linux/icons/32.png differ diff --git a/packages/client-app/build/resources/linux/icons/512.png b/packages/client-app/build/resources/linux/icons/512.png new file mode 100644 index 0000000000..1616522972 Binary files /dev/null and b/packages/client-app/build/resources/linux/icons/512.png differ diff --git a/packages/client-app/build/resources/linux/icons/64.png b/packages/client-app/build/resources/linux/icons/64.png new file mode 100644 index 0000000000..f55b61e0ab Binary files /dev/null and b/packages/client-app/build/resources/linux/icons/64.png differ diff --git a/packages/client-app/build/resources/linux/nylas-mail.desktop.in b/packages/client-app/build/resources/linux/nylas-mail.desktop.in new file mode 100644 index 0000000000..fe6d68f2e8 --- /dev/null +++ b/packages/client-app/build/resources/linux/nylas-mail.desktop.in @@ -0,0 +1,11 @@ +[Desktop Entry] +Name=<%= productName %> +Comment=<%= description %> +GenericName=<%= productName %> +Exec=/usr/bin/nylas-mail %U +Icon=nylas-mail +Type=Application +StartupNotify=true +StartupWMClass=<%= productName %> +Categories=GNOME;GTK;Network;Email;Utility;Development; +MimeType=text/plain;x-scheme-handler/mailto;x-scheme-handler/nylas; diff --git a/packages/client-app/build/resources/linux/redhat/nylas.spec.in b/packages/client-app/build/resources/linux/redhat/nylas.spec.in new file mode 100644 index 0000000000..900e649f59 --- /dev/null +++ b/packages/client-app/build/resources/linux/redhat/nylas.spec.in @@ -0,0 +1,40 @@ +Name: <%= name %> +Version: <%= version %> +Release: 0.1%{?dist} +Summary: <%= description %> +License: GPLv3 +URL: https://github.com/nylas/nylas-mail +AutoReqProv: no # Avoid libchromiumcontent.so missing dependency + +requires: libgnome-keyring + +%description +<%= description %> + +%install +mkdir -p %{buildroot}/usr/share/nylas-mail +cp -r <%= contentsDir %>/* %{buildroot}/usr/share/nylas-mail + +mkdir -p %{buildroot}/usr/bin/ + +ln -s ../share/nylas-mail/nylas %{buildroot}/usr/bin/nylas-mail +chmod 755 %{buildroot}/usr/bin/nylas-mail + +mkdir -p %{buildroot}/usr/share/applications/ +mv nylas-mail.desktop %{buildroot}/usr/share/applications/ + +for s in 16 32 64 128 256 512; do + mkdir -p %{buildroot}/usr/share/icons/hicolor/${s}x${s}/apps + cp -p <%= linuxAssetsDir %>/icons/${s}.png %{buildroot}/usr/share/icons/hicolor/${s}x${s}/apps/nylas-mail.png +done + +%files +/usr/bin/nylas-mail +/usr/share/nylas-mail +/usr/share/applications/nylas-mail.desktop +/usr/share/icons/hicolor/16x16/apps/nylas-mail.png +/usr/share/icons/hicolor/32x32/apps/nylas-mail.png +/usr/share/icons/hicolor/64x64/apps/nylas-mail.png +/usr/share/icons/hicolor/128x128/apps/nylas-mail.png +/usr/share/icons/hicolor/256x256/apps/nylas-mail.png +/usr/share/icons/hicolor/512x512/apps/nylas-mail.png diff --git a/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/Info.plist b/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/Info.plist new file mode 100644 index 0000000000..a08b20c1a1 --- /dev/null +++ b/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/Info.plist @@ -0,0 +1,61 @@ + + + + + BuildMachineOSBuild + 15E65 + CFBundleDevelopmentRegion + en + CFBundleExecutable + Nylas Calendar + CFBundleIconFile + AppIcon + CFBundleIdentifier + com.nylas.Nylas-Calendar + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Nylas Calendar + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleURLTypes + + + CFBundleURLName + nylas-calendar-shim + + + CFBundleVersion + 1 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 7D1014 + DTPlatformVersion + GM + DTSDKBuild + 15E60 + DTSDKName + macosx10.11 + DTXcode + 0731 + DTXcodeBuild + 7D1014 + LSMinimumSystemVersion + 10.11 + NSHumanReadableCopyright + Copyright © 2016 Ben Gotow. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/MacOS/Nylas Calendar b/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/MacOS/Nylas Calendar new file mode 100755 index 0000000000..59d982c3a7 Binary files /dev/null and b/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/MacOS/Nylas Calendar differ diff --git a/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/PkgInfo b/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/PkgInfo new file mode 100644 index 0000000000..bd04210fb4 --- /dev/null +++ b/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/PkgInfo @@ -0,0 +1 @@ +APPL???? \ No newline at end of file diff --git a/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/Resources/AppIcon.icns b/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/Resources/AppIcon.icns new file mode 100644 index 0000000000..bd4f7fdb21 Binary files /dev/null and b/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/Resources/AppIcon.icns differ diff --git a/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/Resources/Base.lproj/MainMenu.nib b/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/Resources/Base.lproj/MainMenu.nib new file mode 100644 index 0000000000..016f8f684c Binary files /dev/null and b/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/Resources/Base.lproj/MainMenu.nib differ diff --git a/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/_CodeSignature/CodeResources b/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/_CodeSignature/CodeResources new file mode 100644 index 0000000000..6dff66395b --- /dev/null +++ b/packages/client-app/build/resources/mac/Nylas Calendar.app/Contents/_CodeSignature/CodeResources @@ -0,0 +1,144 @@ + + + + + files + + Resources/AppIcon.icns + + 5v+SAI+weVs8VSrknYJvITKSEws= + + Resources/Base.lproj/MainMenu.nib + + hash + + 3K0ICUQtN1fqxnQ0VFIIBLmDg00= + + optional + + + + files2 + + Resources/AppIcon.icns + + hash + + 5v+SAI+weVs8VSrknYJvITKSEws= + + hash2 + + cDmR/kt31C8E2IvUvGZRLOTDSOnZAtRnMPhGIJs6k00= + + + Resources/Base.lproj/MainMenu.nib + + hash + + 3K0ICUQtN1fqxnQ0VFIIBLmDg00= + + hash2 + + v9hB8Bi1UaRdestGh/P5TKwS9ntchvYPnE+TZp/y76w= + + optional + + + + rules + + ^Resources/ + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ + + nested + + weight + 10 + + ^.* + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^Resources/ + + weight + 20 + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^[^/]+$ + + nested + + weight + 10 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/packages/client-app/build/resources/mac/Nylas-Mail-DMG-Background.png b/packages/client-app/build/resources/mac/Nylas-Mail-DMG-Background.png new file mode 100644 index 0000000000..1def5c06f1 Binary files /dev/null and b/packages/client-app/build/resources/mac/Nylas-Mail-DMG-Background.png differ diff --git a/packages/client-app/build/resources/mac/file.icns b/packages/client-app/build/resources/mac/file.icns new file mode 100644 index 0000000000..6a8cd5d8ed Binary files /dev/null and b/packages/client-app/build/resources/mac/file.icns differ diff --git a/packages/client-app/build/resources/mac/nylas-Info.plist b/packages/client-app/build/resources/mac/nylas-Info.plist new file mode 100644 index 0000000000..84e3b27fa5 --- /dev/null +++ b/packages/client-app/build/resources/mac/nylas-Info.plist @@ -0,0 +1,23 @@ + + + + + NSPrincipalClass + AtomApplication + CFBundleDocumentTypes + + + LSHandlerRank + Alternate + CFBundleTypeRole + Viewer + CFBundleTypeName + File + CFBundleTypeExtensions + + * + + + + + diff --git a/packages/client-app/build/resources/mac/nylas.icns b/packages/client-app/build/resources/mac/nylas.icns new file mode 100644 index 0000000000..5c02d4f59c Binary files /dev/null and b/packages/client-app/build/resources/mac/nylas.icns differ diff --git a/packages/client-app/build/resources/nylas.png b/packages/client-app/build/resources/nylas.png new file mode 100644 index 0000000000..a69dbaaff4 Binary files /dev/null and b/packages/client-app/build/resources/nylas.png differ diff --git a/packages/client-app/build/resources/ssh/nylas-n1-ci-ssh-secure-file.enc b/packages/client-app/build/resources/ssh/nylas-n1-ci-ssh-secure-file.enc new file mode 100755 index 0000000000..679920ac84 Binary files /dev/null and b/packages/client-app/build/resources/ssh/nylas-n1-ci-ssh-secure-file.enc differ diff --git a/packages/client-app/build/resources/ssh/nylas-n1-ci-ssh.openssl.enc b/packages/client-app/build/resources/ssh/nylas-n1-ci-ssh.openssl.enc new file mode 100644 index 0000000000..935f01e4a6 Binary files /dev/null and b/packages/client-app/build/resources/ssh/nylas-n1-ci-ssh.openssl.enc differ diff --git a/packages/client-app/build/resources/win/elevate.cmd b/packages/client-app/build/resources/win/elevate.cmd new file mode 100644 index 0000000000..8f72b356f0 --- /dev/null +++ b/packages/client-app/build/resources/win/elevate.cmd @@ -0,0 +1,5 @@ +@setlocal +@echo off +set CMD=%* +set APP=%1 +start wscript //nologo "%~dpn0.vbs" %* \ No newline at end of file diff --git a/packages/client-app/build/resources/win/elevate.vbs b/packages/client-app/build/resources/win/elevate.vbs new file mode 100644 index 0000000000..1304e85514 --- /dev/null +++ b/packages/client-app/build/resources/win/elevate.vbs @@ -0,0 +1,13 @@ +Set Shell = CreateObject("Shell.Application") +Set WShell = WScript.CreateObject("WScript.Shell") +Set ProcEnv = WShell.Environment("PROCESS") + +cmd = ProcEnv("CMD") +app = ProcEnv("APP") +args= Right(cmd,(Len(cmd)-Len(app))) + +If (WScript.Arguments.Count >= 1) Then + Shell.ShellExecute app, args, "", "runas", 0 +Else + WScript.Quit +End If \ No newline at end of file diff --git a/packages/client-app/build/resources/win/loading.gif b/packages/client-app/build/resources/win/loading.gif new file mode 100644 index 0000000000..ae7f841410 Binary files /dev/null and b/packages/client-app/build/resources/win/loading.gif differ diff --git a/packages/client-app/build/resources/win/loading.png b/packages/client-app/build/resources/win/loading.png new file mode 100644 index 0000000000..fabddab3f8 Binary files /dev/null and b/packages/client-app/build/resources/win/loading.png differ diff --git a/packages/client-app/build/resources/win/nylas-mailto-default.reg b/packages/client-app/build/resources/win/nylas-mailto-default.reg new file mode 100755 index 0000000000..a1721f8d1d Binary files /dev/null and b/packages/client-app/build/resources/win/nylas-mailto-default.reg differ diff --git a/packages/client-app/build/resources/win/nylas-mailto-registration.reg b/packages/client-app/build/resources/win/nylas-mailto-registration.reg new file mode 100755 index 0000000000..64a437f731 --- /dev/null +++ b/packages/client-app/build/resources/win/nylas-mailto-registration.reg @@ -0,0 +1,60 @@ +Windows Registry Editor Version 5.00 + +[{{HKEY_ROOT}}\SOFTWARE\Classes\Nylas.Url.mailto] +"FriendlyTypeName"="Nylas Url" + +[{{HKEY_ROOT}}\SOFTWARE\Classes\Nylas.Url.mailto\shell] + +[{{HKEY_ROOT}}\SOFTWARE\Classes\Nylas.Url.mailto\shell\open] + +[{{HKEY_ROOT}}\SOFTWARE\Classes\Nylas.Url.mailto\shell\open\command] +@="\"{{PATH_TO_ROOT_FOLDER}}\\Update.exe\" --processStart nylas.exe --process-start-args %1" + + +[{{HKEY_ROOT}}\SOFTWARE\Clients\Mail\Nylas] +@="Nylas" +"LocalizedString"="@{{PATH_TO_ROOT_FOLDER}}\\Update.exe,-123" + +[{{HKEY_ROOT}}\SOFTWARE\Clients\Mail\Nylas\DefaultIcon] +@="{{PATH_TO_APP_FOLDER}}\\nylas.exe,1" + +[{{HKEY_ROOT}}\SOFTWARE\Clients\Mail\Nylas\Capabilities] +"ApplicationName"="Nylas Mail" +"ApplicationDescription"="A fast, modern mail client designed to help you boost your productivity." + +[{{HKEY_ROOT}}\SOFTWARE\Clients\Mail\Nylas\Capabilities\StartMenu] +"Mail"="Nylas" + +[{{HKEY_ROOT}}\SOFTWARE\Clients\Mail\Nylas\Capabilities\URLAssociations] +"mailto"="Nylas.Url.mailto" + +[{{HKEY_ROOT}}\SOFTWARE\Clients\Mail\Nylas\Protocols] + +[{{HKEY_ROOT}}\SOFTWARE\Clients\Mail\Nylas\Protocols\mailto] +@="URL:MailTo Protocol" + +[{{HKEY_ROOT}}\SOFTWARE\Clients\Mail\Nylas\Protocols\mailto\DefaultIcon] +@="{{PATH_TO_APP_FOLDER}}\\nylas.exe,1" + +[{{HKEY_ROOT}}\SOFTWARE\Clients\Mail\Nylas\Protocols\mailto\shell] + +[{{HKEY_ROOT}}\SOFTWARE\Clients\Mail\Nylas\Protocols\mailto\shell\open] + +[{{HKEY_ROOT}}\SOFTWARE\Clients\Mail\Nylas\Protocols\mailto\shell\open\command] +@="\"{{PATH_TO_ROOT_FOLDER}}\\Update.exe\" --processStart nylas.exe --process-start-args %1" + +[{{HKEY_ROOT}}\SOFTWARE\Clients\Mail\Nylas\shell] + +[{{HKEY_ROOT}}\SOFTWARE\Clients\Mail\Nylas\shell\open] + +[{{HKEY_ROOT}}\SOFTWARE\Clients\Mail\Nylas\shell\open\command] +@="\"{{PATH_TO_ROOT_FOLDER}}\\Update.exe\" --processStart nylas.exe" + +[{{HKEY_ROOT}}\SOFTWARE\RegisteredApplications] +"Nylas"="Software\\Clients\\Mail\\Nylas\\Capabilities" + + +[HKEY_CURRENT_USER\SOFTWARE\Classes\Local Settings\Software\Microsoft\Windows\Shell\MuiCache] +"{{PATH_TO_ROOT_FOLDER}}\\Update.exe"="Nylas Mail" +"{{PATH_TO_ROOT_FOLDER}}\\Update.exe.FriendlyAppName"="Nylas Mail" +"{{PATH_TO_ROOT_FOLDER}}\\Update.exe.ApplicationCompany"="Nylas" diff --git a/packages/client-app/build/resources/win/nylas.ico b/packages/client-app/build/resources/win/nylas.ico new file mode 100644 index 0000000000..0aa35cde63 Binary files /dev/null and b/packages/client-app/build/resources/win/nylas.ico differ diff --git a/packages/client-app/build/tasks/coffeelint-task.js b/packages/client-app/build/tasks/coffeelint-task.js new file mode 100644 index 0000000000..4579d47256 --- /dev/null +++ b/packages/client-app/build/tasks/coffeelint-task.js @@ -0,0 +1,25 @@ +module.exports = (grunt) => { + grunt.config.merge({ + coffeelint: { + 'options': { + configFile: 'build/config/coffeelint.json', + }, + 'src': grunt.config('source:coffeescript'), + 'build': [ + 'build/tasks/**/*.coffee', + ], + 'test': [ + 'spec/**/*.cjsx', + 'spec/**/*.coffee', + ], + 'static': [ + 'static/**/*.coffee', + 'static/**/*.cjsx', + ], + 'target': (grunt.option("target") ? grunt.option("target").split(" ") : []), + }, + }); + + grunt.loadNpmTasks('grunt-contrib-coffee'); + grunt.loadNpmTasks('grunt-coffeelint-cjsx'); +} diff --git a/packages/client-app/build/tasks/create-mac-dmg.js b/packages/client-app/build/tasks/create-mac-dmg.js new file mode 100644 index 0000000000..81639e08d3 --- /dev/null +++ b/packages/client-app/build/tasks/create-mac-dmg.js @@ -0,0 +1,25 @@ +const path = require('path'); +const createDMG = require('electron-installer-dmg') + +module.exports = (grunt) => { + grunt.registerTask('create-mac-dmg', 'Create DMG for Nylas Mail', function pack() { + const done = this.async(); + const dmgPath = path.join(grunt.config('outputDir'), "NylasMail.dmg"); + createDMG({ + appPath: path.join(grunt.config('outputDir'), "Nylas Mail-darwin-x64", "Nylas Mail.app"), + name: "NylasMail", + background: path.resolve(grunt.config('appDir'), 'build', 'resources', 'mac', 'Nylas-Mail-DMG-background.png'), + icon: path.resolve(grunt.config('appDir'), 'build', 'resources', 'mac', 'nylas.icns'), + overwrite: true, + out: grunt.config('outputDir'), + }, (err) => { + if (err) { + done(err); + return + } + + grunt.log.writeln(`>> Created ${dmgPath}`); + done(null); + }) + }); +}; diff --git a/packages/client-app/build/tasks/create-mac-zip.js b/packages/client-app/build/tasks/create-mac-zip.js new file mode 100644 index 0000000000..5e1c92192c --- /dev/null +++ b/packages/client-app/build/tasks/create-mac-zip.js @@ -0,0 +1,35 @@ +/* eslint prefer-template: 0 */ +/* eslint global-require: 0 */ +/* eslint quote-props: 0 */ +const path = require('path'); + +module.exports = (grunt) => { + const {spawn} = grunt.config('taskHelpers') + + grunt.registerTask('create-mac-zip', 'Zip up Nylas Mail', function pack() { + const done = this.async(); + const zipPath = path.join(grunt.config('outputDir'), 'NylasMail.zip'); + + if (grunt.file.exists(zipPath)) { + grunt.file.delete(zipPath, {force: true}); + } + + const orig = process.cwd(); + process.chdir(path.join(grunt.config('outputDir'), 'Nylas Mail-darwin-x64')); + + spawn({ + cmd: "zip", + args: ["-9", "-y", "-r", "-9", "-X", zipPath, 'Nylas Mail.app'], + }, (error) => { + process.chdir(orig); + + if (error) { + done(error); + return; + } + + grunt.log.writeln(`>> Created ${zipPath}`); + done(null); + }); + }); +}; diff --git a/packages/client-app/build/tasks/csslint-task.js b/packages/client-app/build/tasks/csslint-task.js new file mode 100644 index 0000000000..f00c927445 --- /dev/null +++ b/packages/client-app/build/tasks/csslint-task.js @@ -0,0 +1,33 @@ +module.exports = (grunt) => { + grunt.config.merge({ + csslint: { + options: { + 'adjoining-classes': false, + 'duplicate-background-images': false, + 'box-model': false, + 'box-sizing': false, + 'bulletproof-font-face': false, + 'compatible-vendor-prefixes': false, + 'display-property-grouping': false, + 'fallback-colors': false, + 'font-sizes': false, + 'gradients': false, + 'ids': false, + 'important': false, + 'known-properties': false, + 'outline-none': false, + 'overqualified-elements': false, + 'qualified-headings': false, + 'unique-headings': false, + 'universal-selector': false, + 'vendor-prefix': false, + 'duplicate-properties': false, // doesn't place nice with mixins + }, + src: [ + 'static/**/*.css', + ], + }, + }); + + grunt.loadNpmTasks('grunt-contrib-csslint'); +} diff --git a/packages/client-app/build/tasks/docs-build-task.js b/packages/client-app/build/tasks/docs-build-task.js new file mode 100644 index 0000000000..36c0811390 --- /dev/null +++ b/packages/client-app/build/tasks/docs-build-task.js @@ -0,0 +1,204 @@ +const path = require('path'); +const cjsxtransform = require('coffee-react-transform'); +const rimraf = require('rimraf'); + +const fs = require('fs-plus'); +var fs_extra = require('fs-extra'); + +const _ = require('underscore'); + +const donna = require('donna'); +const joanna = require('joanna'); +const tello = require('tello'); + +module.exports = function(grunt) { + + let {cp, mkdir, rm} = grunt.config('taskHelpers'); + + let getClassesToInclude = function() { + let modulesPath = path.resolve(__dirname, '..', '..', 'internal_packages'); + let classes = {}; + fs.traverseTreeSync(modulesPath, function(modulePath) { + // Don't traverse inside dependencies + if (modulePath.match(/node_modules/g)) { return false; } + + // Don't traverse blacklisted packages (that have docs, but we don't want to include) + if (path.basename(modulePath) !== 'package.json') { return true; } + if (!fs.isFileSync(modulePath)) { return true; } + + let apiPath = path.join(path.dirname(modulePath), 'api.json'); + if (fs.isFileSync(apiPath)) { + _.extend(classes, grunt.file.readJSON(apiPath).classes); + } + return true; + }); + return classes; + }; + + let sortClasses = function(classes) { + let sortedClasses = {}; + for (let className of Array.from(Object.keys(classes).sort())) { + sortedClasses[className] = classes[className]; + } + return sortedClasses; + }; + + return grunt.registerTask('docs-build', 'Builds the API docs in src', function() { + + grunt.log.writeln("Time to build the docs!") + + let done = this.async(); + + + let classDocsOutputDir = grunt.config.get('classDocsOutputDir'); + + let cjsxOutputDir = path.join(classDocsOutputDir, 'temp-cjsx'); + + return rimraf(cjsxOutputDir, function() { + let api; + fs.mkdir(cjsxOutputDir); + + let srcPath = path.resolve(__dirname, '..', '..', 'src'); + + const blacklist = ['/K2/', + 'legacy-edgehill-api', + 'edgehill-api']; + + let in_blacklist = function(file) { + for (var i = 0; i < blacklist.length; i++) { + if (file.indexOf(blacklist[i]) >= 0) { + return true; + } + } + return false; + }; + + fs.traverseTreeSync(srcPath, function(file) { + + if (in_blacklist(file)) { + console.log("Skipping " + file); + // Skip K2 + } + + // Convert CJSX into coffeescript that can be read by Donna + else if (path.extname(file) === '.cjsx') { + let transformed = cjsxtransform(grunt.file.read(file)); + + // Only attempt to parse this file as documentation if it contains + // real Coffeescript classes. + if (transformed.indexOf('\nclass ') > 0) { + + grunt.log.writeln("Found class in file: " + file) + + grunt.file.write(path.join(cjsxOutputDir, path.basename(file).slice(0, -5 + 1 || undefined)+'coffee'), transformed); + } + } + else if (path.extname(file) === '.jsx') { + console.log('Transforming ' + file) + + let fileStr = grunt.file.read(file); + + let transformed = require("babel-core").transform(fileStr, { + plugins: ["transform-react-jsx", + "transform-class-properties"], + presets: ['react', 'stage-2'] + }); + + grunt.file.write(path.join(cjsxOutputDir, path.basename(file).slice(0, -3 || undefined)+'js'), transformed.code); + } + else if (path.extname(file) == '.es6') { + console.log(file); + + let fileStr = grunt.file.read(file); + + let transformed = require("babel-core").transform(fileStr, { + plugins: ["transform-class-properties", + "transform-function-bind"], + presets: ['react', 'stage-2'] + + }); + + if (transformed.code.indexOf('class ') > 0) { + grunt.log.writeln("Found class in file: " + file) + + grunt.file.write(path.join(cjsxOutputDir, path.basename(file).slice(0, -3 || undefined)+'js'), transformed.code); + } + } + else if (path.extname(file) == '.coffee' || + path.extname(file) == '.js') { + let dest_path = path.join(cjsxOutputDir, path.basename(file)); + console.log("Copying " + file + " to " + dest_path); + fs_extra.copySync(file, dest_path); + } + return true; + }); + + grunt.log.ok('Done transforming, starting donna extraction') + grunt.log.writeln('cjsxOutputDir: ' + cjsxOutputDir) + + // Process coffeescript source + let metadata = donna.generateMetadata([cjsxOutputDir]); + grunt.log.ok('---- Done with Donna (cjsx metadata)----'); + + + // DEBUG + // Use to check individual files + var js_files = [] + fs.traverseTreeSync(cjsxOutputDir, function(file) { + if (path.extname(file) === '.js') { + console.log('testing joanna on ' + file) + let meta = joanna([file]) + console.log('testing tello on ' + file) + tello.digest(meta) + console.log('passed') + } + }); + + var js_files = [] + fs.traverseTreeSync(cjsxOutputDir, function(file) { + if (path.extname(file) === '.js') { + js_files.push(file.toString()) + } + }); + + console.log(js_files); + grunt.log.ok('---- Starting Jonna (jsx metadata)----'); + let jsx_metadata = joanna(js_files); + grunt.log.ok('---- Done with Joanna (jsx metadata)----'); + + + Object.assign(metadata[0].files, jsx_metadata.files); + console.log(metadata[0]); + + grunt.file.write('/tmp/metadata.json', JSON.stringify(metadata, null, 2)); + + + + try { + api = tello.digest(metadata); + } catch (e) { + console.log(e) + console.log(e.stack); + + console.log(metadata) + return; + } + + console.log('---- Done with Tello ----'); + _.extend(api.classes, getClassesToInclude()); + + console.log(api.classes) + + + api.classes = sortClasses(api.classes); + console.log(api.classes) + + let apiJson = JSON.stringify(api, null, 2); + let apiJsonPath = path.join(classDocsOutputDir, 'api.json'); + grunt.file.write(apiJsonPath, apiJson); + return done(); + }); + }); + + +}; diff --git a/packages/client-app/build/tasks/docs-render-task.js b/packages/client-app/build/tasks/docs-render-task.js new file mode 100644 index 0000000000..96701d0ccd --- /dev/null +++ b/packages/client-app/build/tasks/docs-render-task.js @@ -0,0 +1,232 @@ +const path = require('path'); +const Handlebars = require('handlebars'); +const marked = require('meta-marked'); +const fs = require('fs-plus'); +const _ = require('underscore'); + +marked.setOptions({ + highlight(code) { + return require('highlight.js').highlightAuto(code).value; + } +}); + +let standardClassURLRoot = 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/'; + +let standardClasses = [ + 'string', + 'object', + 'array', + 'function', + 'number', + 'date', + 'error', + 'boolean', + 'null', + 'undefined', + 'json', + 'set', + 'map', + 'typeerror', + 'syntaxerror', + 'referenceerror', + 'rangeerror' +]; + +let thirdPartyClasses = { + 'react.component': 'https://facebook.github.io/react/docs/component-api.html', + 'promise': 'https://github.com/petkaantonov/bluebird/blob/master/API.md', + 'range': 'https://developer.mozilla.org/en-US/docs/Web/API/Range', + 'selection': 'https://developer.mozilla.org/en-US/docs/Web/API/Selection', + 'node': 'https://developer.mozilla.org/en-US/docs/Web/API/Node', +}; + +module.exports = function(grunt) { + + let {cp, mkdir, rm} = grunt.config('taskHelpers'); + + let relativePathForClass = classname => classname+'.html'; + + let outputPathFor = function(relativePath) { + let classDocsOutputDir = grunt.config.get('classDocsOutputDir'); + return path.join(classDocsOutputDir, relativePath); + }; + + var processFields = function(json, fields, tasks) { + let val; + if (fields == null) { fields = []; } + if (tasks == null) { tasks = []; } + if (json instanceof Array) { + return (() => { + let result = []; + for (val of Array.from(json)) { + result.push(processFields(val, fields, tasks)); + } + return result; + })(); + } else { + return (() => { + let result1 = []; + for (let key in json) { + val = json[key]; + let item; + if (Array.from(fields).includes(key)) { + for (let task of Array.from(tasks)) { + val = task(val); + } + json[key] = val; + } + if (_.isObject(val)) { + item = processFields(val, fields, tasks); + } + result1.push(item); + } + return result1; + })(); + } + }; + + return grunt.registerTask('docs-render', 'Builds html from the API docs', function() { + + let documentation, filename, html, match, meta, name, result, section, val; + let classDocsOutputDir = grunt.config.get('classDocsOutputDir'); + + // Parse API reference Markdown + + let classes = []; + let apiJsonPath = path.join(classDocsOutputDir, 'api.json'); + let apiJSON = JSON.parse(grunt.file.read(apiJsonPath)); + + + for (var classname in apiJSON.classes) { + // Parse a "@Section" out of the description if one is present + let contents = apiJSON.classes[classname]; + let sectionRegex = /Section: ?([\w ]*)(?:$|\n)/; + section = 'General'; + + match = sectionRegex.exec(contents.description); + if (match) { + contents.description = contents.description.replace(match[0], ''); + section = match[1].trim(); + } + + // Replace superClass "React" with "React.Component". The Coffeescript Lexer + // is so bad. + if (contents.superClass === "React") { + contents.superClass = "React.Component"; + } + + classes.push({ + name: classname, + documentation: contents, + section + }); + } + + + // Build Sidebar metadata we can hand off to each of the templates to + // generate the sidebar + let sidebar = {}; + for (var i = 0; i < classes.length; i++) { + var current_class = classes[i]; + console.log(current_class.name + ' ' + current_class.section) + + if (!(current_class.section in sidebar)) { + sidebar[current_class.section] = [] + } + sidebar[current_class.section].push(current_class.name) + } + + + // Prepare to render by loading handlebars partials + let templatesPath = path.resolve(grunt.config('buildDir'), 'docs_templates'); + grunt.file.recurse(templatesPath, function(abspath, root, subdir, filename) { + if ((filename[0] === '_') && (path.extname(filename) === '.html')) { + return Handlebars.registerPartial(filename, grunt.file.read(abspath)); + } + }); + + // Render Helpers + + let knownClassnames = {}; + for (classname in apiJSON.classes) { + val = apiJSON.classes[classname]; + knownClassnames[classname.toLowerCase()] = val; + } + + + let expandTypeReferences = function(val) { + let refRegex = /{([\w.]*)}/g; + while ((match = refRegex.exec(val)) !== null) { + let term = match[1].toLowerCase(); + let label = match[1]; + let url = false; + if (Array.from(standardClasses).includes(term)) { + url = standardClassURLRoot+term; + } else if (thirdPartyClasses[term]) { + url = thirdPartyClasses[term]; + } else if (knownClassnames[term]) { + url = relativePathForClass(knownClassnames[term].name); + grunt.log.ok("Found: " + term) + } else { + console.warn(`Cannot find class named ${term}`); + } + + if (url) { + val = val.replace(match[0], `${label}`); + } + } + return val; + }; + + let expandFuncReferences = function(val) { + let refRegex = /{([\w]*)?::([\w]*)}/g; + while ((match = refRegex.exec(val)) !== null) { + var label; + let [text, a, b] = Array.from(match); + let url = false; + if (a && b) { + url = `${relativePathForClass(a)}#${b}`; + label = `${a}::${b}`; + } else { + url = `#${b}`; + label = `${b}`; + } + if (url) { + val = val.replace(text, `${label}`); + } + } + return val; + }; + + // DEBUG Render sidebar json + // grunt.file.write(outputPathFor('sidebar.json'), JSON.stringify(sidebar, null, 2)); + + // Render Class Pages + let classTemplatePath = path.join(templatesPath, 'class.md'); + let classTemplate = Handlebars.compile(grunt.file.read(classTemplatePath)); + + for ({name, documentation, section} of Array.from(classes)) { + // Recursively process `description` and `type` fields to process markdown, + // expand references to types, functions and other files. + processFields(documentation, ['description'], [expandFuncReferences]); + processFields(documentation, ['type'], [expandTypeReferences]); + + result = classTemplate({name, documentation, section}); + grunt.file.write(outputPathFor(name + '.md'), result); + } + + let sidebarTemplatePath = path.join(templatesPath, 'sidebar.md'); + let sidebarTemplate = Handlebars.compile(grunt.file.read(sidebarTemplatePath)); + + grunt.file.write(outputPathFor('Sidebar.md'), + sidebarTemplate({sidebar})); + + + // Remove temp cjsx output + return fs.removeSync(outputPathFor("temp-cjsx")); + }); +}; + +function __guard__(value, transform) { + return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; +} diff --git a/packages/client-app/build/tasks/docs-task.js b/packages/client-app/build/tasks/docs-task.js new file mode 100644 index 0000000000..3d91a5924c --- /dev/null +++ b/packages/client-app/build/tasks/docs-task.js @@ -0,0 +1,177 @@ +const path = require('path'); +const cjsxtransform = require('coffee-react-transform'); +const rimraf = require('rimraf'); + +const fs = require('fs-plus'); +const _ = require('underscore'); + +const donna = require('donna'); +const joanna = require('joanna'); +const tello = require('tello'); + +module.exports = function(grunt) { + + let {cp, mkdir, rm} = grunt.config('taskHelpers'); + + let getClassesToInclude = function() { + let modulesPath = path.join(grunt.config('appDir'), 'internal_packages'); + let classes = {}; + fs.traverseTreeSync(modulesPath, function(modulePath) { + // Don't traverse inside dependencies + if (modulePath.match(/node_modules/g)) { return false; } + + // Don't traverse blacklisted packages (that have docs, but we don't want to include) + if (path.basename(modulePath) !== 'package.json') { return true; } + if (!fs.isFileSync(modulePath)) { return true; } + + let apiPath = path.join(path.dirname(modulePath), 'api.json'); + if (fs.isFileSync(apiPath)) { + _.extend(classes, grunt.file.readJSON(apiPath).classes); + } + return true; + }); + return classes; + }; + + let sortClasses = function(classes) { + let sortedClasses = {}; + for (let className of Array.from(Object.keys(classes).sort())) { + sortedClasses[className] = classes[className]; + } + return sortedClasses; + }; + + var processFields = function(json, fields, tasks) { + let val; + if (fields == null) { fields = []; } + if (tasks == null) { tasks = []; } + if (json instanceof Array) { + return (() => { + let result = []; + for (val of Array.from(json)) { + result.push(processFields(val, fields, tasks)); + } + return result; + })(); + } else { + return (() => { + let result1 = []; + for (let key in json) { + val = json[key]; + let item; + if (Array.from(fields).includes(key)) { + for (let task of Array.from(tasks)) { + val = task(val); + } + json[key] = val; + } + if (_.isObject(val)) { + item = processFields(val, fields, tasks); + } + result1.push(item); + } + return result1; + })(); + } + }; + + return grunt.registerTask('docs', 'Builds the API docs in src', function() { + + grunt.log.writeln("Time to build the docs!") + + let done = this.async(); + + // Convert CJSX into coffeescript that can be read by Donna + + // let classDocsOutputDir = grunt.config.get('classDocsOutputDir'); + + let classDocsOutputDir = '~/Desktop/Nylas Mail Docs/' + let cjsxOutputDir = path.join(classDocsOutputDir, 'temp-cjsx'); + + return rimraf(cjsxOutputDir, function() { + let api; + fs.mkdir(cjsxOutputDir); + + let srcPath = path.resolve(grunt.config('appDir'), 'src'); + + fs.traverseTreeSync(srcPath, function(file) { + + if (file.indexOf('/K2/') > 0) { + // Skip K2 + } + else if (path.extname(file) === '.cjsx') { // Should also look for jsx and es6 + let transformed = cjsxtransform(grunt.file.read(file)); + + // Only attempt to parse this file as documentation if it contains + // real Coffeescript classes. + if (transformed.indexOf('\nclass ') > 0) { + + grunt.log.writeln("Found class in file: " + file) + + grunt.file.write(path.join(cjsxOutputDir, path.basename(file).slice(0, -5 + 1 || undefined)+'coffee'), transformed); + } + } + else if (path.extname(file) === '.jsx') { + console.log('Transforming ' + file) + + let fileStr = grunt.file.read(file); + + let transformed = require("babel-core").transform(fileStr, { + plugins: ["transform-react-jsx", + "transform-class-properties"], + presets: ['react', 'stage-2'] + }); + + + if (transformed.code.indexOf('class ') > 0) { + grunt.log.writeln("Found class in file: " + file) + + grunt.file.write(path.join(cjsxOutputDir, path.basename(file).slice(0, -3 || undefined)+'js'), transformed.code); + } + } + return true; + }); + + grunt.log.ok('Done transforming, starting donna extraction') + grunt.log.writeln('cjsxOutputDir: ' + cjsxOutputDir) + + // Process coffeescript source + let metadata = donna.generateMetadata([cjsxOutputDir]); + grunt.log.ok('---- Done with Donna (cjsx metadata)----'); + + console.log(js_files); + + var js_files = [] + fs.traverseTreeSync(cjsxOutputDir, function(file) { + if (path.extname(file) === '.js') { + js_files.push(file.toString()) + } + }); + + console.log(js_files); + let jsx_metadata = joanna(js_files); + grunt.log.ok('---- Done with Joanna (jsx metadata)----'); + + Object.assign(metadata, jsx_metadata); + + console.log(metadata); + + try { + api = tello.digest(metadata); + } catch (e) { + console.log(e.stack); + } + + console.log('---- Done with Tello ----'); + _.extend(api.classes, getClassesToInclude()); + api.classes = sortClasses(api.classes); + + let apiJson = JSON.stringify(api, null, 2); + let apiJsonPath = path.join(classDocsOutputDir, 'api.json'); + grunt.file.write(apiJsonPath, apiJson); + return done(); + }); + }); + + +}; diff --git a/packages/client-app/build/tasks/eslint-task.js b/packages/client-app/build/tasks/eslint-task.js new file mode 100644 index 0000000000..d8417f12a4 --- /dev/null +++ b/packages/client-app/build/tasks/eslint-task.js @@ -0,0 +1,72 @@ +const chalk = require('chalk'); +const eslint = require('eslint'); + +module.exports = (grunt) => { + grunt.config.merge({ + eslint: { + options: { + ignore: false, + configFile: 'build/config/eslint.json', + }, + target: grunt.config('source:es6'), + }, + + eslintFixer: { + src: grunt.config('source:es6'), + }, + }); + + grunt.registerMultiTask('eslint', 'Validate files with ESLint', function task() { + const opts = this.options({ + outputFile: false, + quiet: false, + maxWarnings: -1, + }); + + if (this.filesSrc.length === 0) { + grunt.log.writeln(chalk.magenta('Could not find any files to validate.')); + return true; + } + + const formatter = eslint.CLIEngine.getFormatter(opts.format); + + if (!formatter) { + grunt.warn(`Could not find formatter ${opts.format}.`); + return false; + } + + const engine = new eslint.CLIEngine(opts); + + let report = null; + try { + report = engine.executeOnFiles(this.filesSrc); + } catch (err) { + grunt.warn(err); + return false; + } + + if (opts.fix) { + eslint.CLIEngine.outputFixes(report); + } + + let results = report.results; + if (opts.quiet) { + results = eslint.CLIEngine.getErrorResults(results); + } + + const output = formatter(results); + + if (opts.outputFile) { + grunt.file.write(opts.outputFile, output); + } else if (output) { + console.log(output); + } + + const tooManyWarnings = opts.maxWarnings >= 0 && report.warningCount > opts.maxWarnings; + if (report.errorCount === 0 && tooManyWarnings) { + grunt.warn(`ESLint found too many warnings (maximum:${opts.maxWarnings})`); + } + + return report.errorCount === 0; + }); +}; diff --git a/packages/client-app/build/tasks/installer-linux-task.js b/packages/client-app/build/tasks/installer-linux-task.js new file mode 100644 index 0000000000..499c792978 --- /dev/null +++ b/packages/client-app/build/tasks/installer-linux-task.js @@ -0,0 +1,116 @@ +/* eslint global-require:0 */ +const fs = require('fs'); +const path = require('path'); +const _ = require('underscore'); + +module.exports = (grunt) => { + const {spawn} = grunt.config('taskHelpers'); + + const outputDir = grunt.config.get('outputDir'); + const contentsDir = path.join(grunt.config('outputDir'), `nylas-linux-${process.arch}`); + const linuxAssetsDir = path.resolve(path.join(grunt.config('buildDir'), 'resources', 'linux')); + const arch = { + ia32: 'i386', + x64: 'amd64', + }[process.arch]; + + // a few helpers + + const writeFromTemplate = (filePath, data) => { + const template = _.template(String(fs.readFileSync(filePath))) + const finishedPath = path.join(outputDir, path.basename(filePath).replace('.in', '')); + grunt.file.write(finishedPath, template(data)); + return finishedPath; + } + + const getInstalledSize = (dir, callback) => { + const cmd = 'du'; + const args = ['-sk', dir]; + spawn({cmd, args}, (error, {stdout}) => { + const installedSize = stdout.split(/\s+/).shift() || '200000'; // default to 200MB + callback(null, installedSize); + }); + } + + grunt.registerTask('create-rpm-installer', 'Create rpm package', function mkrpmf() { + const done = this.async() + if (!arch) { + done(new Error(`Unsupported arch ${process.arch}`)); + return; + } + + const rpmDir = path.join(grunt.config('outputDir'), 'rpm'); + if (grunt.file.exists(rpmDir)) { + grunt.file.delete(rpmDir, {force: true}); + } + + const templateData = { + name: grunt.config('appJSON').name, + version: grunt.config('appJSON').version, + description: grunt.config('appJSON').description, + productName: grunt.config('appJSON').productName, + linuxShareDir: '/usr/local/share/nylas', + linuxAssetsDir: linuxAssetsDir, + contentsDir: contentsDir, + } + + // This populates nylas.spec + const specInFilePath = path.join(linuxAssetsDir, 'redhat', 'nylas.spec.in') + writeFromTemplate(specInFilePath, templateData) + + // This populates nylas.desktop + const desktopInFilePath = path.join(linuxAssetsDir, 'nylas-mail.desktop.in') + writeFromTemplate(desktopInFilePath, templateData) + + const cmd = path.join(grunt.config('appDir'), 'script', 'mkrpm') + const args = [outputDir, contentsDir, linuxAssetsDir] + spawn({cmd, args}, (error) => { + if (error) { + return done(error); + } + grunt.log.ok(`Created rpm package in ${rpmDir}`); + return done(); + }); + }); + + grunt.registerTask('create-deb-installer', 'Create debian package', function mkdebf() { + const done = this.async() + if (!arch) { + done(`Unsupported arch ${process.arch}`); + return; + } + + getInstalledSize(contentsDir, (error, installedSize) => { + if (error) { + done(error); + return; + } + + const version = grunt.config('appJSON').version; + const data = { + version: version, + name: grunt.config('appJSON').name, + description: grunt.config('appJSON').description, + productName: grunt.config('appJSON').productName, + linuxShareDir: '/usr/share/nylas-mail', + arch: arch, + section: 'devel', + maintainer: 'Nylas Team ', + installedSize: installedSize, + } + writeFromTemplate(path.join(linuxAssetsDir, 'debian', 'control.in'), data) + writeFromTemplate(path.join(linuxAssetsDir, 'nylas-mail.desktop.in'), data) + + const icon = path.join(grunt.config('appDir'), 'build', 'resources', 'nylas.png') + const cmd = path.join(grunt.config('appDir'), 'script', 'mkdeb'); + const args = [version, arch, icon, linuxAssetsDir, contentsDir, outputDir]; + spawn({cmd, args}, (spawnError) => { + if (spawnError) { + return done(spawnError); + } + grunt.log.ok(`Created ${outputDir}/nylas-${version}-${arch}.deb`); + return done() + }); + }); + }); +} diff --git a/packages/client-app/build/tasks/lesslint-task.js b/packages/client-app/build/tasks/lesslint-task.js new file mode 100644 index 0000000000..26aae5ae71 --- /dev/null +++ b/packages/client-app/build/tasks/lesslint-task.js @@ -0,0 +1,20 @@ +module.exports = (grunt) => { + grunt.config.merge({ + lesslint: { + src: [ + 'internal_packages/**/*.less', + 'dot-nylas/**/*.less', + 'static/**/*.less', + ], + options: { + less: { + paths: ['static', 'static/variables/'], + }, + imports: ['static/variables/*.less'], + }, + }, + }); + + grunt.loadNpmTasks('grunt-contrib-less'); + grunt.loadNpmTasks('grunt-lesslint'); +} diff --git a/packages/client-app/build/tasks/nylaslint-task.js b/packages/client-app/build/tasks/nylaslint-task.js new file mode 100644 index 0000000000..ad68088909 --- /dev/null +++ b/packages/client-app/build/tasks/nylaslint-task.js @@ -0,0 +1,189 @@ +/* eslint no-cond-assign: 0 */ +const path = require('path'); +const fs = require('fs-plus'); + +function normalizeRequirePath(requirePath, fPath) { + if (requirePath[0] === ".") { + return path.normalize(path.join(path.dirname(fPath), requirePath)); + } + return requirePath; +} + + +module.exports = (grunt) => { + grunt.config.merge({ + nylaslint: { + src: grunt.config('source:coffeescript').concat(grunt.config('source:es6')), + }, + }); + + grunt.registerMultiTask('nylaslint', 'Check requires for file extensions compiled away', function nylaslint() { + const done = this.async(); + + // Enable once path errors are fixed. + if (process.platform === 'win32') { + done(); + return; + } + + const extensionRegex = /require ['"].*\.(coffee|cjsx|jsx|es6|es)['"]/i; + + for (const fileset of this.files) { + grunt.log.writeln(`Nylinting ${fileset.src.length} files.`); + + const esExtensions = { + ".es6": true, + ".es": true, + ".jsx": true, + }; + + const errors = []; + + const esExport = {}; + const esNoExport = {}; + const esExportDefault = {}; + + // Temp TODO. Fix spec files + for (const f of fileset.src) { + if (!esExtensions[path.extname(f)]) { continue; } + if (!/-spec/.test(f)) { continue; } + + const content = fs.readFileSync(f, {encoding: 'utf8'}); + + // https://regex101.com/r/rQ3eD0/1 + // Matches only the first describe block + const describeRe = /[\n]describe\(['"](.*?)['"], ?\(\) ?=> ?/m; + if (describeRe.test(content)) { + errors.push(`${f}: Spec has to start with function`); + } + } + + // NOTE: Comment me in if you want to fix these files. + // _str = require('underscore.string') + // replacer = (match, describeName) -> + // fnName = _str.camelize(describeName, true) + // return "\ndescribe('#{describeName}', function #{fnName}() " + // newContent = content.replace(describeRe, replacer) + // fs.writeFileSync(f, newContent, encoding:'utf8') + + // Build the list of ES6 files that export things and categorize + for (const f of fileset.src) { + if (!esExtensions[path.extname(f)]) { continue; } + const lookupPath = `${path.dirname(f)}/${path.basename(f, path.extname(f))}`; + const content = fs.readFileSync(f, {encoding: 'utf8'}); + + if (/module.exports\s?=\s?.+/gmi.test(content)) { + if (!f.endsWith('nylas-exports.es6')) { + errors.push(`${f}: Don't use module.exports in ES6`); + } + } + + if (/^export/gmi.test(content)) { + if (/^export default/gmi.test(content)) { + esExportDefault[lookupPath] = true; + } else { + esExport[lookupPath] = true; + } + } else { + esNoExport[lookupPath] = true; + } + } + + // Now look again through all ES6 files, this time to check imports + // instead of exports. + for (const f of fileset.src) { + let result = null; + if (!esExtensions[path.extname(f)]) { + continue; + } + const content = fs.readFileSync(f, {encoding: 'utf8'}); + const importRe = /import \{.*\} from ['"](.*?)['"]/gmi; + + while (result = importRe.exec(content)) { + for (const requirePath of result.slice(1)) { + const lookupPath = normalizeRequirePath(requirePath, f); + if (esExportDefault[lookupPath] || esNoExport[lookupPath]) { + errors.push(`${f}: Don't destructure default export ${requirePath}`); + } + } + } + } + + // Now look through all coffeescript files + // If they require things from ES6 files, ensure they're using the + // proper syntax. + for (const f of fileset.src) { + let result = null; + if (esExtensions[path.extname(f)]) { + continue; + } + const content = fs.readFileSync(f, {encoding: 'utf8'}); + if (extensionRegex.test(content)) { + errors.push(`${f}: Remove extensions when requiring files`); + } + + const requireRe = /require[ (]['"]([\w_./-]*?)['"]/gmi; + + while (result = requireRe.exec(content)) { + for (const requirePath of result.slice(1)) { + const lookupPath = normalizeRequirePath(requirePath, f); + + const baseRequirePath = path.basename(requirePath); + + const plainRequireRe = new RegExp(`require[ (]['"].*${baseRequirePath}['"]\\)?$`, "gm"); + const defaultRequireRe = new RegExp(`require\\(['"].*${baseRequirePath}['"]\\)\\.default`, "gm"); + + if (esExport[lookupPath]) { + if (!plainRequireRe.test(content)) { + errors.push(`${f}: No \`default\` exported ${requirePath}`); + } + } else if (esNoExport[lookupPath]) { + errors.push(`${f}: Nothing exported from ${requirePath}`); + } else if (esExportDefault[lookupPath]) { + if (!defaultRequireRe.test(content)) { + errors.push(`${f}: Add \`default\` to require ${requirePath}`); + } + } else { + // must be a coffeescript or core file + if (defaultRequireRe.test(content)) { + errors.push(`${f}: Don't ask for \`default\` from ${requirePath}`); + } + } + } + } + } + + if (errors.length > 0) { + for (const err of errors) { grunt.log.error(err); } + const error = ` + Please fix the #{errors.length} linter errors above. These are the issues we're looking for: + + ISSUES WITH COFFEESCRIPT FILES: + + 1. Remove extensions when requiring files: + Since we compile files in production to plain ".js" files it's very important you do NOT include the file extension when "require"ing a file. + + 2. Add "default" to require: + As of Babel 6, "require" no longer returns whatever the "default" value is. If you are "require"ing an es6 file from a coffeescript file, you must explicitly request the "default" property. For example: do "require('./my-es6-file').default" + + 3. Don't ask for "default": + If you're requiring a coffeescript file from a coffeescript file, you will almost never need to load a "default" object. This is likely an indication you incorrectly thought you were importing an ES6 file. + + ISSUES WITH ES6 FILES: + + 4. Don't use module.exports in ES6: + You sholudn't manually assign module.exports anymore. Use proper ES6 module syntax like "export default" or "export const FOO". + + 5. Don't destructure default export: + If you're using "import {FOO} from './bar'" in ES6 files, it's important that "./bar" does NOT export a "default". Instead, in './bar', do "export const FOO = 'foo'" + + 6. Spec has to start with function + Top-level "describe" blocks can no longer use the "() => {}" function syntax. This will incorrectly bind "this" to the "window" object instead of the jasmine object. The top-level "describe" block must use the "function describeName() {}" syntax + `; + done(new Error(error)); + } + } + + done(null); + }); +} diff --git a/packages/client-app/build/tasks/package-task.js b/packages/client-app/build/tasks/package-task.js new file mode 100644 index 0000000000..779a8b0844 --- /dev/null +++ b/packages/client-app/build/tasks/package-task.js @@ -0,0 +1,296 @@ +/* eslint global-require: 0 *//* eslint prefer-template: 0 */ +/* eslint quote-props: 0 */ +const packager = require('electron-packager'); +const path = require('path'); +const util = require('util'); +const tmpdir = path.resolve(require('os').tmpdir(), 'nylas-build'); +const fs = require('fs-plus'); +const coffeereact = require('coffee-react'); +const glob = require('glob'); +const babel = require('babel-core'); +const symlinkedPackages = [] + +module.exports = (grunt) => { + const packageJSON = grunt.config('appJSON'); + const babelPath = path.join(grunt.config('rootDir'), '.babelrc') + const babelOptions = JSON.parse(fs.readFileSync(babelPath)) + + function runCopyAPM(buildPath, electronVersion, platform, arch, callback) { + // Move APM up out of the /app folder which will be inside the ASAR + const apmTargetDir = path.resolve(buildPath, '..', 'apm'); + fs.moveSync(path.join(buildPath, 'apm'), apmTargetDir) + + // Move /apm/node_modules/atom-package-manager up a level. We're + // essentially pulling the atom-package-manager module up outside of + // the node_modules folder, which is necessary because npmV3 installs + // nested dependencies in the same dir. + const apmPackageDir = path.join(apmTargetDir, 'node_modules', 'atom-package-manager') + for (const name of fs.readdirSync(apmPackageDir)) { + fs.renameSync(path.join(apmPackageDir, name), path.join(apmTargetDir, name)); + } + + const apmSymlink = path.join(apmTargetDir, 'node_modules', '.bin', 'apm'); + if (fs.existsSync(apmSymlink)) { + fs.unlinkSync(apmSymlink); + } + fs.rmdirSync(apmPackageDir); + callback(); + } + + function runCopyPlatformSpecificResources(buildPath, electronVersion, platform, arch, callback) { + // these files (like nylas-mailto-default.reg) go alongside the ASAR, + // not inside it, so we need to move out of the `app` directory. + const resourcesDir = path.resolve(buildPath, '..'); + if (platform === 'win32') { + fs.copySync(path.resolve(grunt.config('appDir'), 'build', 'resources', 'win'), resourcesDir); + } + callback(); + } + + /** + * We have to resolve the symlink paths (and cache the results) before + * copying over the files since some symlinks may be relative paths (like + * those created by lerna). We'll keep absolute references of those paths + * for the symlink copy function to use after the packaging is complete. + */ + function resolveRealSymlinkPaths(appDir) { + console.log("---> Resolving symlinks"); + const dirs = [ + 'internal_packages', + 'src', + 'spec', + 'node_modules', + ]; + + dirs.forEach((dir) => { + const absoluteDir = path.join(appDir, dir); + fs.readdirSync(absoluteDir).forEach((packageName) => { + const relativePackageDir = path.join(dir, packageName) + const absolutePackageDir = path.join(absoluteDir, packageName) + const realPackagePath = fs.realpathSync(absolutePackageDir).replace('/private/', '/') + if (realPackagePath !== absolutePackageDir) { + console.log(` ---> Resolving '${relativePackageDir}' to '${realPackagePath}'`) + symlinkedPackages.push({realPackagePath, relativePackageDir}) + } + }); + }); + } + + function runCopySymlinkedPackages(buildPath, electronVersion, platform, arch, callback) { + console.log("---> Moving symlinked node modules / internal packages into build folder.") + + symlinkedPackages.forEach(({realPackagePath, relativePackageDir}) => { + const packagePath = path.join(buildPath, relativePackageDir) + console.log(` ---> Copying ${realPackagePath} to ${packagePath}`); + fs.removeSync(packagePath); + fs.copySync(realPackagePath, packagePath); + }); + + callback(); + } + + /** + * We don't need the K2 folder anymore since the previous step hard + * copied the client-sync package (and its isomorphic-core dependency) + * into /internal_packages. The remains of the folder are N1-Cloud + * pieces that aren't necessary + */ + function removeUnnecessaryFiles(buildPath, electronVersion, platform, arch, callback) { + fs.removeSync(path.join(buildPath, 'src', 'K2')) + callback(); + } + + function runTranspilers(buildPath, electronVersion, platform, arch, callback) { + console.log("---> Running babel and coffeescript transpilers") + + grunt.config('source:coffeescript').forEach(pattern => { + glob.sync(pattern, {cwd: buildPath}).forEach((relPath) => { + const coffeepath = path.join(buildPath, relPath) + if (/(node_modules|\.js$)/.test(coffeepath)) return + console.log(` ---> Compiling ${coffeepath.slice(coffeepath.indexOf("/app") + 4)}`) + const outPath = coffeepath.replace(path.extname(coffeepath), '.js'); + const res = coffeereact.compile(grunt.file.read(coffeepath), { + bare: false, + join: false, + separator: grunt.util.normalizelf(grunt.util.linefeed), + + sourceMap: true, + sourceRoot: '/', + generatedFile: path.basename(outPath), + sourceFiles: [path.relative(buildPath, coffeepath)], + }); + grunt.file.write(outPath, `${res.js}\n//# sourceMappingURL=${path.basename(outPath)}.map\n`); + grunt.file.write(`${outPath}.map`, res.v3SourceMap); + fs.unlinkSync(coffeepath); + }); + }); + + grunt.config('source:es6').forEach(pattern => { + glob.sync(pattern, {cwd: buildPath}).forEach((relPath) => { + const es6Path = path.join(buildPath, relPath) + if (/(node_modules|\.js$)/.test(es6Path)) return + const outPath = es6Path.replace(path.extname(es6Path), '.js'); + console.log(` ---> Compiling ${es6Path.slice(es6Path.indexOf("/app") + 4)}`) + const res = babel.transformFileSync(es6Path, Object.assign(babelOptions, { + sourceMaps: true, + sourceRoot: '/', + sourceMapTarget: path.relative(buildPath, outPath), + sourceFileName: path.relative(buildPath, es6Path), + })); + grunt.file.write(outPath, `${res.code}\n//# sourceMappingURL=${path.basename(outPath)}.map\n`); + grunt.file.write(`${outPath}.map`, JSON.stringify(res.map)); + fs.unlinkSync(es6Path); + }); + }); + + callback(); + } + + const platform = grunt.option('platform'); + + // See: https://github.com/electron-userland/electron-packager/blob/master/usage.txt + grunt.config.merge({ + 'packager': { + 'app-version': packageJSON.version, + 'platform': platform, + 'protocols': [{ + name: "Nylas Protocol", + schemes: ["nylas"], + }, { + name: "Mailto Protocol", + schemes: ["mailto"], + }], + 'dir': grunt.config('appDir'), + 'app-category-type': "public.app-category.business", + 'tmpdir': tmpdir, + 'arch': { + 'win32': 'ia32', + }[platform], + 'icon': { + darwin: path.resolve(grunt.config('appDir'), 'build', 'resources', 'mac', 'nylas.icns'), + win32: path.resolve(grunt.config('appDir'), 'build', 'resources', 'win', 'nylas.ico'), + linux: undefined, + }[platform], + 'name': { + darwin: 'Nylas Mail', + win32: 'nylas', + linux: 'nylas', + }[platform], + 'app-copyright': `Copyright (C) 2014-${new Date().getFullYear()} Nylas, Inc. All rights reserved.`, + 'derefSymlinks': false, + 'asar': { + 'unpack': "{" + [ + '*.node', + '**/vendor/**', + 'examples/**', + '**/src/tasks/**', + '**/node_modules/spellchecker/**', + '**/node_modules/windows-shortcuts/**', + ].join(',') + "}", + }, + "ignore": [ // These are all relative to client-app + /.*\.watchmanconfig.*/, + + // top level dirs we never want + /^\/build.*/, + /^\/dist.*/, + /^\/docs.*/, + /^\/docs_src.*/, + /^\/script.*/, + /^\/spec.*/, + + // general dirs we never want + /[/]+gh-pages$/, + /[/]+docs$/, + /[/]+obj[/]+gen/, + /[/]+\.deps$/, + + // File types we know we never want in the prod build + /\.md$/i, + /\.log$/i, + /\.yml$/i, + /\.gz/i, + /\.zip/i, + /\.pdb$/, + /\.h$/, + /\.cc$/, + /\.ts$/, + /\.flow$/, + /\.gyp/, + /\.mk/, + /\.dYSM$/, + + // specific (large) module bits we know we don't need + /node_modules[/]+less[/]+dist$/, + /node_modules[/]+react[/]+dist$/, + /node_modules[/].*[/]tests?$/, + /node_modules[/].*[/]coverage$/, + /node_modules[/].*[/]benchmark$/, + /@paulbetts[/]+cld[/]+deps[/]+cld/, + ], + 'out': grunt.config('outputDir'), + 'overwrite': true, + 'prune': true, + /** + * This will automatically look for the identity in the keychain. It + * runs the `security find-identity` command. Note that + * setup-mac-keychain-task needs to be run first + */ + 'osx-sign': !!process.env.SIGN_BUILD, + 'win32metadata': { + CompanyName: 'Nylas, Inc.', + FileDescription: 'Nylas Mail', + LegalCopyright: `Copyright (C) 2014-${new Date().getFullYear()} Nylas, Inc. All rights reserved.`, + ProductName: 'Nylas Mail', + }, + // NOTE: The following plist keys can NOT be set in the + // nylas-Info.plist since they are manually overridden by + // electron-packager based on this config file: + // + // CFBundleDisplayName: 'name', + // CFBundleExecutable: 'name', + // CFBundleIdentifier: 'app-bundle-id', + // CFBundleName: 'name' + // + // See https://github.com/electron-userland/electron-packager/blob/master/mac.js#L50 + // + // Our own nylas-Info.plist gets extended on top of the + // Electron.app/Contents/Info.plist. A majority of the defaults are + // left in the Electron Info.plist file + 'extend-info': path.resolve(grunt.config('appDir'), 'build', 'resources', 'mac', 'nylas-Info.plist'), + 'app-bundle-id': "com.nylas.nylas-mail", + 'afterCopy': [ + runCopyPlatformSpecificResources, + runCopyAPM, + runCopySymlinkedPackages, + removeUnnecessaryFiles, + runTranspilers, + ], + }, + }) + + grunt.registerTask('package', 'Package Nylas Mail', function pack() { + const done = this.async(); + const start = Date.now(); + + console.log('---> Running packager with options:'); + console.log(util.inspect(grunt.config.get('packager'), true, 7, true)); + + const ongoing = setInterval(() => { + const elapsed = Math.round((Date.now() - start) / 1000.0) + console.log(`---> Packaging for ${elapsed}s`); + }, 1000) + + resolveRealSymlinkPaths(grunt.config('appDir')) + + packager(grunt.config.get('packager'), (err, appPaths) => { + clearInterval(ongoing) + if (err) { + grunt.fail.fatal(err); + return done(err); + } + console.log(`---> Done Successfully. Built into: ${appPaths}`); + return done(); + }); + }); +}; diff --git a/packages/client-app/build/tasks/setup-mac-keychain-task.js b/packages/client-app/build/tasks/setup-mac-keychain-task.js new file mode 100644 index 0000000000..1e1f6529bd --- /dev/null +++ b/packages/client-app/build/tasks/setup-mac-keychain-task.js @@ -0,0 +1,113 @@ +/* eslint global-require: 0 */ +const path = require('path'); +const fs = require('fs-plus'); + +// Codesigning is a Mac-only process that requires a valid Apple +// certificate, the private key, and access to the Mac keychain. +// +// We can only codesign from keys in the keychain. At the end of the day +// we need the certificate and private key to exist in the keychain +// +// In the case of Travis, we need to setup a temp keychain from encrypted +// files in the repository. # We'll decrypt and import our certificates, +// put them in a temporary keychain, and use that. +// +// If you want to verify the app was signed you can run the commands: +// +// spctl -a -t exec -vv /path/to/N1.app +// +// Which should return "satisfies its Designated Requirement" +// +// And: +// +// codesign --verify --deep --verbose=2 /path/to/N1.app +// +// Which should return "accepted" +module.exports = (grunt) => { + let getCertData; + const {spawnP} = grunt.config('taskHelpers') + const tmpKeychain = "n1-build.keychain"; + + const unlockKeychain = (keychain, keychainPass) => { + const args = ['unlock-keychain', '-p', keychainPass, keychain]; + return spawnP({cmd: "security", args}); + }; + + const cleanupKeychain = () => { + if (fs.existsSync(path.join(process.env.HOME, "Library", "Keychains", tmpKeychain))) { + return spawnP({cmd: "security", args: ["delete-keychain", tmpKeychain]}); + } + return Promise.resolve() + }; + + const buildMacKeychain = () => { + const crypto = require('crypto'); + const tmpPass = crypto.randomBytes(32).toString('hex'); + const {appleCert, nylasCert, nylasPrivateKey, keyPass} = getCertData(); + const codesignBin = path.join("/", "usr", "bin", "codesign"); + + // Create a custom, temporary keychain + return cleanupKeychain() + .then(() => spawnP({cmd: "security", args: ["create-keychain", '-p', tmpPass, tmpKeychain]})) + + // Due to a bug in OSX, you must list-keychain with -s in order for it + // to actually add it to the list of keychains. See http://stackoverflow.com/questions/20391911/os-x-keychain-not-visible-to-keychain-access-app-in-mavericks + .then(() => spawnP({cmd: "security", args: ["list-keychains", "-s", tmpKeychain]})) + + // Make the custom keychain default, so xcodebuild will use it for signing + .then(() => spawnP({cmd: "security", args: ["default-keychain", "-s", tmpKeychain]})) + + // Unlock the keychain + .then(() => unlockKeychain(tmpKeychain, tmpPass)) + + // Set keychain timeout to 1 hour for long builds + .then(() => spawnP({cmd: "security", args: ["set-keychain-settings", "-t", "3600", "-l", tmpKeychain]})) + + // Add certificates to keychain and allow codesign to access them + .then(() => spawnP({cmd: "security", args: ["import", appleCert, "-k", tmpKeychain, "-T", codesignBin]})) + + .then(() => spawnP({cmd: "security", args: ["import", nylasCert, "-k", tmpKeychain, "-T", codesignBin]})) + + // Load the password for the private key from environment variables + .then(() => spawnP({cmd: "security", args: ["import", nylasPrivateKey, "-k", tmpKeychain, "-P", keyPass, "-T", codesignBin]})); + }; + + getCertData = () => { + const certs = path.resolve(path.join(grunt.config('buildDir'), 'resources', 'certs', 'mac')); + const appleCert = path.join(certs, 'AppleWWDRCA.cer'); + const nylasCert = path.join(certs, 'mac-nylas-n1.cer'); + const nylasPrivateKey = path.join(certs, 'mac-nylas-n1.p12'); + + const keyPass = process.env.APPLE_CODESIGN_KEY_PASSWORD; + + if (!keyPass) { + throw new Error("APPLE_CODESIGN_KEY_PASSWORD must be set"); + } + if (!fs.existsSync(appleCert)) { + throw new Error(`${appleCert} doesn't exist`); + } + if (!fs.existsSync(nylasCert)) { + throw new Error(`${nylasCert} doesn't exist`); + } + if (!fs.existsSync(nylasPrivateKey)) { + throw new Error(`${nylasPrivateKey} doesn't exist`); + } + + return {appleCert, nylasCert, nylasPrivateKey, keyPass}; + }; + + const shouldRun = () => { + if (process.platform !== 'darwin') { + grunt.log.writeln(`Skipping keychain setup since ${process.platform} is not darwin`); + return false + } + return !!process.env.SIGN_BUILD + } + + grunt.registerTask('setup-mac-keychain', 'Setup Mac Keychain to sign the app', function setupMacKeychain() { + const done = this.async(); + if (!shouldRun()) return done(); + + return buildMacKeychain().then(done).catch(grunt.fail.fatal); + }); +} diff --git a/packages/client-app/build/tasks/task-helpers.js b/packages/client-app/build/tasks/task-helpers.js new file mode 100644 index 0000000000..12c8de989f --- /dev/null +++ b/packages/client-app/build/tasks/task-helpers.js @@ -0,0 +1,32 @@ +const childProcess = require('child_process'); + +module.exports = (grunt) => { + function spawn(options, callback) { + const stdout = []; + const stderr = []; + let error = null; + const proc = childProcess.spawn(options.cmd, options.args, options.opts); + proc.stdout.on('data', data => stdout.push(data.toString())); + proc.stderr.on('data', data => stderr.push(data.toString())); + proc.on('error', (processError) => { + return error != null ? error : (error = processError) + }); + proc.on('close', (exitCode, signal) => { + if (exitCode !== 0) { if (typeof error === 'undefined' || error === null) { error = new Error(signal); } } + const results = {stderr: stderr.join(''), stdout: stdout.join(''), code: exitCode}; + if (exitCode !== 0) { grunt.log.error(results.stderr); } + return callback(error, results, exitCode); + }); + } + + function spawnP(options) { + return new Promise((resolve, reject) => { + spawn(options, (error) => { + if (error) return reject(error); + return resolve() + }) + }) + } + + return {spawn, spawnP}; +} diff --git a/packages/client-app/build/tasks/upload-task.js b/packages/client-app/build/tasks/upload-task.js new file mode 100644 index 0000000000..4baf58f0b6 --- /dev/null +++ b/packages/client-app/build/tasks/upload-task.js @@ -0,0 +1,199 @@ +/* eslint global-require: 0 */ +/* eslint import/no-dynamic-require: 0 */ +const s3 = require('s3'); +const request = require('request'); +const Promise = require('bluebird'); +const path = require('path'); +const fs = require('fs-plus'); + + +let s3Client = null; +let packageVersion = null; +let fullVersion = null; + +module.exports = (grunt) => { + const {spawn} = grunt.config('taskHelpers'); + + function populateVersion() { + return new Promise((resolve, reject) => { + const json = grunt.config.get('appJSON') + const cmd = 'git'; + const args = ['rev-parse', '--short', 'HEAD']; + spawn({cmd, args}, (error, {stdout} = {}) => { + if (error) { + return reject(); + } + const commitHash = (stdout ? stdout.trim() : "").slice(0, 7); + packageVersion = json.version; + if (packageVersion.indexOf('-') > 0) { + fullVersion = packageVersion; + } else { + fullVersion = `${packageVersion}-${commitHash}`; + } + return resolve(); + }); + }); + } + + function postToSlack(msg) { + if (!process.env.NYLAS_INTERNAL_HOOK_URL) { + return Promise.resolve(); + } + return new Promise((resolve, reject) => + request.post({ + url: process.env.NYLAS_INTERNAL_HOOK_URL, + json: { + username: "Edgehill Builds", + text: msg, + }, + } + , (error) => { + return error ? reject(error) : resolve(); + }) + ); + } + + function put(localSource, destName, options = {}) { + grunt.log.writeln(`>> Uploading ${localSource} to S3…`); + + const write = grunt.log.writeln; + let lastPc = 0; + + const params = { + Key: destName, + ACL: "public-read", + Bucket: "edgehill", + }; + Object.assign(params, options); + + return new Promise((resolve, reject) => { + const uploader = s3Client.uploadFile({ + localFile: localSource, + s3Params: params, + }); + uploader.on("error", err => reject(err)); + uploader.on("progress", () => { + const pc = Math.round((uploader.progressAmount / uploader.progressTotal) * 100.0); + if (pc !== lastPc) { + lastPc = pc; + write(`>> Uploading ${destName} ${pc}%`); + return; + } + }); + uploader.on("end", data => resolve(data)); + }); + } + + function uploadToS3(filepath, key) { + grunt.log.writeln(`>> Uploading ${filepath} to ${key}…`); + return put(filepath, key).then((data) => { + const msg = `Nylas Mail release asset uploaded: <${data.Location}|${key}>`; + return postToSlack(msg).then(() => Promise.resolve(data)); + }); + } + + grunt.registerTask("upload", "Upload Nylas build", function upload() { + const done = this.async(); + + populateVersion() + .then(() => { + // find files to upload + const outputDir = grunt.config.get('outputDir'); + const uploads = []; + + // We increment the version so we have an autoupdate target to test + const [version, hash] = fullVersion.split('-'); + const versionParts = version.split('.') + versionParts[2] = +versionParts[2] + 1 + const nextVersion = `${versionParts.join('.')}-${hash}` + + const s3PathCurrentVersion = `${fullVersion}/${process.platform}/${process.arch}` + const s3PathNextVersion = `${nextVersion}/${process.platform}/${process.arch}` + + if (process.platform === 'darwin') { + uploads.push({ + source: `${outputDir}/NylasMail.zip`, + key: `${s3PathCurrentVersion}/NylasMail.zip`, + }); + uploads.push({ + source: `${outputDir}/NylasMail.zip`, + key: `${s3PathNextVersion}/NylasMail.zip`, + }); + uploads.push({ + source: `${outputDir}/NylasMail.dmg`, + key: `${s3PathCurrentVersion}/NylasMail.dmg`, + }); + } else if (process.platform === 'win32') { + uploads.push({ + source: path.join(outputDir, "RELEASES"), + key: `${s3PathCurrentVersion}/RELEASES`, + }); + uploads.push({ + source: path.join(outputDir, "NylasMailSetup.exe"), + key: `${s3PathCurrentVersion}/NylasMailSetup.exe`, + }); + uploads.push({ + source: path.join(outputDir, `NylasMail-${packageVersion}-full.nupkg`), + key: `${s3PathCurrentVersion}/nylasmail-${packageVersion}-full.nupkg`, + }); + uploads.push({ + source: path.join(outputDir, "RELEASES"), + key: `${s3PathNextVersion}/RELEASES`, + }); + uploads.push({ + source: path.join(outputDir, "NylasMailSetup.exe"), + key: `${s3PathNextVersion}/NylasMailSetup.exe`, + }); + uploads.push({ + source: path.join(outputDir, `NylasMail-${packageVersion}-full.nupkg`), + key: `${s3PathNextVersion}/nylasmail-${packageVersion}-full.nupkg`, + }); + } else if (process.platform === 'linux') { + const files = fs.readdirSync(outputDir); + for (const file of files) { + if (path.extname(file) === '.deb') { + uploads.push({ + source: `${outputDir}/${file}`, + key: `${fullVersion}/${process.platform}-deb/${process.arch}/NylasMail.deb`, + options: {ContentType: "application/x-deb"}, + }); + } + if (path.extname(file) === '.rpm') { + uploads.push({ + source: `${outputDir}/${file}`, + key: `${fullVersion}/${process.platform}-rpm/${process.arch}/NylasMail.rpm`, + options: {ContentType: "application/x-rpm"}, + }); + } + } + } else { + grunt.fail.fatal(`Unsupported platform: '${process.platform}'`); + } + + const awsKey = process.env.AWS_ACCESS_KEY_ID != null ? process.env.AWS_ACCESS_KEY_ID : ""; + const awsSecret = process.env.AWS_SECRET_ACCESS_KEY != null ? process.env.AWS_SECRET_ACCESS_KEY : ""; + + if (awsKey.length === 0) { + grunt.fail.fatal("Please set the AWS_ACCESS_KEY_ID environment variable"); + } + if (awsSecret.length === 0) { + grunt.fail.fatal("Please set the AWS_SECRET_ACCESS_KEY environment variable"); + } + + s3Client = s3.createClient({ + s3Options: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + scretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + }, + }); + + return Promise.all(uploads.map(({source, key, options}) => + uploadToS3(source, key, options)) + ) + .then(done) + }) + .catch((err) => { + grunt.fail.fatal(err) + }); + }); +} diff --git a/packages/client-app/dot-nylas/README.md b/packages/client-app/dot-nylas/README.md new file mode 100644 index 0000000000..d2174a44a9 --- /dev/null +++ b/packages/client-app/dot-nylas/README.md @@ -0,0 +1,4 @@ +# Default Config + +These are the default Nylas configs. This folder on setup is copied to +`~/.nylas-mail` on unix machines. diff --git a/packages/client-app/dot-nylas/config.json b/packages/client-app/dot-nylas/config.json new file mode 100644 index 0000000000..5a125d974b --- /dev/null +++ b/packages/client-app/dot-nylas/config.json @@ -0,0 +1,24 @@ +{ + "*": { + "env": "production", + "core": { + "themes": [ + "ui-light" + ], + "disabledPackages": [ + "message-view-on-github", + "personal-level-indicators", + "phishing-detection", + "nylas-private-salesforce", + "github-contact-card", + "keybase", + "thread-sharing", + "composer-markdown", + "composer-scheduler", + "composer-mail-merge", + "send-and-archive", + "main-calendar" + ] + } + } +} diff --git a/packages/client-app/dot-nylas/keymap.json b/packages/client-app/dot-nylas/keymap.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/client-app/dot-nylas/packages/README.md b/packages/client-app/dot-nylas/packages/README.md new file mode 100644 index 0000000000..540b6949c9 --- /dev/null +++ b/packages/client-app/dot-nylas/packages/README.md @@ -0,0 +1 @@ +All packages in this directory will be automatically loaded diff --git a/packages/client-app/internal_packages/account-sidebar/README.md b/packages/client-app/internal_packages/account-sidebar/README.md new file mode 100755 index 0000000000..a781a1fc64 --- /dev/null +++ b/packages/client-app/internal_packages/account-sidebar/README.md @@ -0,0 +1 @@ +# React version of thread list diff --git a/packages/client-app/internal_packages/account-sidebar/assets/icon-sidebar-addcategory@2x.png b/packages/client-app/internal_packages/account-sidebar/assets/icon-sidebar-addcategory@2x.png new file mode 100644 index 0000000000..a151a89241 Binary files /dev/null and b/packages/client-app/internal_packages/account-sidebar/assets/icon-sidebar-addcategory@2x.png differ diff --git a/packages/client-app/internal_packages/account-sidebar/lib/account-commands.coffee b/packages/client-app/internal_packages/account-sidebar/lib/account-commands.coffee new file mode 100644 index 0000000000..b5624308be --- /dev/null +++ b/packages/client-app/internal_packages/account-sidebar/lib/account-commands.coffee @@ -0,0 +1,88 @@ +_ = require 'underscore' +{Actions, MenuHelpers} = require 'nylas-exports' + + +class AccountCommands + + @_focusAccounts: (accounts) -> + Actions.focusDefaultMailboxPerspectiveForAccounts(accounts) + NylasEnv.show() unless NylasEnv.isVisible() + + @_isSelected: (account, sidebarAccountIds) => + if sidebarAccountIds.length > 1 + return account instanceof Array + else if sidebarAccountIds.length is 1 + return account?.id is sidebarAccountIds[0] + else + return false + + @registerCommands: (accounts) -> + @_commandsDisposable?.dispose() + commands = {} + + allKey = "window:select-account-0" + commands[allKey] = @_focusAccounts.bind(@, accounts) + + [1..8].forEach (index) => + account = accounts[index - 1] + return unless account + key = "window:select-account-#{index}" + commands[key] = @_focusAccounts.bind(@, [account]) + + @_commandsDisposable = NylasEnv.commands.add(document.body, commands) + + @registerMenuItems: (accounts, sidebarAccountIds) -> + windowMenu = _.find NylasEnv.menu.template, ({label}) -> + MenuHelpers.normalizeLabel(label) is 'Window' + return unless windowMenu + + submenu = _.reject windowMenu.submenu, (item) -> item.account + return unless submenu + + idx = _.findIndex submenu, ({type}) -> type is 'separator' + return unless idx > 0 + + template = @menuTemplate(accounts, sidebarAccountIds) + submenu.splice(idx + 1, 0, template...) + windowMenu.submenu = submenu + NylasEnv.menu.update() + + @menuItem: (account, idx, {isSelected, clickHandlers} = {}) => + item = { + label: account.label ? "All Accounts", + command: "window:select-account-#{idx}", + account: true + } + if isSelected + item.type = 'checkbox' + item.checked = true + if clickHandlers + accounts = if account instanceof Array then account else [account] + item.click = @_focusAccounts.bind(@, accounts) + item.accelerator = "CmdOrCtrl+#{idx + 1}" + return item + + @menuTemplate: (accounts, sidebarAccountIds, {clickHandlers} = {}) => + template = [] + multiAccount = accounts.length > 1 + + if multiAccount + isSelected = @_isSelected(accounts, sidebarAccountIds) + template = [ + @menuItem(accounts, 0, {isSelected, clickHandlers}) + ] + + template = template.concat accounts.map((account, idx) => + # If there's only one account, it should be mapped to command+1, not command+2 + accIdx = if multiAccount then idx + 1 else idx + isSelected = @_isSelected(account, sidebarAccountIds) + return @menuItem(account, accIdx, {isSelected, clickHandlers}) + ) + return template + + @register: (accounts, sidebarAccountIds) -> + @registerCommands(accounts) + @registerMenuItems(accounts, sidebarAccountIds) + + +module.exports = AccountCommands diff --git a/packages/client-app/internal_packages/account-sidebar/lib/components/account-sidebar.cjsx b/packages/client-app/internal_packages/account-sidebar/lib/components/account-sidebar.cjsx new file mode 100644 index 0000000000..a5fd417f07 --- /dev/null +++ b/packages/client-app/internal_packages/account-sidebar/lib/components/account-sidebar.cjsx @@ -0,0 +1,59 @@ +_ = require 'underscore' +React = require 'react' +{Utils, AccountStore} = require 'nylas-exports' +{OutlineView, ScrollRegion, Flexbox} = require 'nylas-component-kit' +AccountSwitcher = require './account-switcher' +SidebarStore = require '../sidebar-store' + + +class AccountSidebar extends React.Component + @displayName: 'AccountSidebar' + + @containerRequired: false + @containerStyles: + minWidth: 165 + maxWidth: 250 + + constructor: (@props) -> + @state = @_getStateFromStores() + + componentDidMount: => + @unsubscribers = [] + @unsubscribers.push SidebarStore.listen @_onStoreChange + @unsubscribers.push AccountStore.listen @_onStoreChange + + shouldComponentUpdate: (nextProps, nextState) => + not Utils.isEqualReact(nextProps, @props) or + not Utils.isEqualReact(nextState, @state) + + componentWillUnmount: => + unsubscribe() for unsubscribe in @unsubscribers + + _onStoreChange: => + @setState @_getStateFromStores() + + _getStateFromStores: => + accounts: AccountStore.accounts() + sidebarAccountIds: SidebarStore.sidebarAccountIds() + userSections: SidebarStore.userSections() + standardSection: SidebarStore.standardSection() + + _renderUserSections: (sections) => + sections.map (section) => + + + render: => + {accounts, sidebarAccountIds, userSections, standardSection} = @state + + + + +
+ + {@_renderUserSections(userSections)} +
+
+
+ + +module.exports = AccountSidebar diff --git a/packages/client-app/internal_packages/account-sidebar/lib/components/account-switcher.cjsx b/packages/client-app/internal_packages/account-sidebar/lib/components/account-switcher.cjsx new file mode 100644 index 0000000000..7c5dd4cac9 --- /dev/null +++ b/packages/client-app/internal_packages/account-sidebar/lib/components/account-switcher.cjsx @@ -0,0 +1,53 @@ +React = require 'react' +{Actions} = require 'nylas-exports' +{RetinaImg} = require 'nylas-component-kit' +AccountCommands = require '../account-commands' + + +class AccountSwitcher extends React.Component + @displayName: 'AccountSwitcher' + + @propTypes: + accounts: React.PropTypes.array.isRequired + sidebarAccountIds: React.PropTypes.array.isRequired + + + _makeMenuTemplate: => + template = AccountCommands.menuTemplate( + @props.accounts, + @props.sidebarAccountIds, + clickHandlers: true + ) + template = template.concat [ + {type: 'separator'} + {label: 'Add Account...', click: @_onAddAccount} + {label: 'Manage Accounts...', click: @_onManageAccounts} + ] + return template + + # Handlers + + _onAddAccount: => + ipc = require('electron').ipcRenderer + ipc.send('command', 'application:add-account', {source: 'Sidebar'}) + + _onManageAccounts: => + Actions.switchPreferencesTab('Accounts') + Actions.openPreferences() + + _onShowMenu: => + remote = require('electron').remote + Menu = remote.Menu + menu = Menu.buildFromTemplate(@_makeMenuTemplate()) + menu.popup() + + render: => +
+ +
+ + +module.exports = AccountSwitcher diff --git a/packages/client-app/internal_packages/account-sidebar/lib/main.coffee b/packages/client-app/internal_packages/account-sidebar/lib/main.coffee new file mode 100644 index 0000000000..a2f73ea122 --- /dev/null +++ b/packages/client-app/internal_packages/account-sidebar/lib/main.coffee @@ -0,0 +1,13 @@ +React = require "react" +AccountSidebar = require "./components/account-sidebar" +{ComponentRegistry, WorkspaceStore} = require "nylas-exports" + +module.exports = + item: null # The DOM item the main React component renders into + + activate: (@state) -> + ComponentRegistry.register AccountSidebar, + location: WorkspaceStore.Location.RootSidebar + + deactivate: (@state) -> + ComponentRegistry.unregister(AccountSidebar) diff --git a/packages/client-app/internal_packages/account-sidebar/lib/sidebar-actions.coffee b/packages/client-app/internal_packages/account-sidebar/lib/sidebar-actions.coffee new file mode 100644 index 0000000000..8aaa180476 --- /dev/null +++ b/packages/client-app/internal_packages/account-sidebar/lib/sidebar-actions.coffee @@ -0,0 +1,12 @@ +Reflux = require 'reflux' + +Actions = [ + 'focusAccounts', + 'setKeyCollapsed', +] + +for idx in Actions + Actions[idx] = Reflux.createAction(Actions[idx]) + Actions[idx].sync = true + +module.exports = Actions diff --git a/packages/client-app/internal_packages/account-sidebar/lib/sidebar-item.coffee b/packages/client-app/internal_packages/account-sidebar/lib/sidebar-item.coffee new file mode 100644 index 0000000000..82324c9cb6 --- /dev/null +++ b/packages/client-app/internal_packages/account-sidebar/lib/sidebar-item.coffee @@ -0,0 +1,172 @@ +_ = require 'underscore' +_str = require 'underscore.string' +{WorkspaceStore, + MailboxPerspective, + FocusedPerspectiveStore, + SyncbackCategoryTask, + DestroyCategoryTask, + CategoryStore, + Actions, + Utils, + RegExpUtils} = require 'nylas-exports' +{OutlineViewItem} = require 'nylas-component-kit' + +SidebarActions = require './sidebar-actions' + +idForCategories = (categories) -> + _.pluck(categories, 'id').join('-') + +countForItem = (perspective) -> + unreadCountEnabled = NylasEnv.config.get('core.workspace.showUnreadForAllCategories') + if perspective.isInbox() or unreadCountEnabled + return perspective.unreadCount() + return 0 + +isItemSelected = (perspective) -> + (WorkspaceStore.rootSheet() in [WorkspaceStore.Sheet.Threads, WorkspaceStore.Sheet.Drafts] and + FocusedPerspectiveStore.current().isEqual(perspective)) + +isItemCollapsed = (id) -> + if NylasEnv.savedState.sidebarKeysCollapsed[id] isnt undefined + NylasEnv.savedState.sidebarKeysCollapsed[id] + else + true + +toggleItemCollapsed = (item) -> + return unless item.children.length > 0 + SidebarActions.setKeyCollapsed(item.id, not isItemCollapsed(item.id)) + +onDeleteItem = (item) -> + # TODO Delete multiple categories at once + return if item.deleted is true + category = item.perspective.category() + return unless category + + Actions.queueTask(new DestroyCategoryTask({category})) + +onEditItem = (item, value) -> + return unless value + return if item.deleted is true + category = item.perspective.category() + return unless category + re = RegExpUtils.subcategorySplitRegex() + match = re.exec(category.displayName) + lastMatch = match + while match + lastMatch = match + match = re.exec(category.displayName) + if lastMatch + newDisplayName = category.displayName.slice(0, lastMatch.index + 1) + value + else + newDisplayName = value + if newDisplayName is category.displayName + return + Actions.queueTask(new SyncbackCategoryTask({category, displayName: newDisplayName})) + + +class SidebarItem + + @forPerspective: (id, perspective, opts = {}) -> + counterStyle = OutlineViewItem.CounterStyles.Alt if perspective.isInbox() + + return _.extend({ + id: id + name: perspective.name + contextMenuLabel: perspective.name + count: countForItem(perspective) + iconName: perspective.iconName + children: [] + perspective: perspective + selected: isItemSelected(perspective) + collapsed: isItemCollapsed(id) ? true + counterStyle: counterStyle + onDelete: if opts.deletable then onDeleteItem else undefined + onEdited: if opts.editable then onEditItem else undefined + onCollapseToggled: toggleItemCollapsed + + onDrop: (item, event) -> + jsonString = event.dataTransfer.getData('nylas-threads-data') + jsonData = null + try + jsonData = JSON.parse(jsonString) + catch err + console.error("JSON parse error: #{err}") + return unless jsonData + Actions.moveThreadsToPerspective({ + targetPerspective: item.perspective, + threadIds: jsonData.threadIds, + accountIds: jsonData.accountIds, + }) + + shouldAcceptDrop: (item, event) -> + target = item.perspective + current = FocusedPerspectiveStore.current() + return false unless event.dataTransfer.types.includes('nylas-threads-data') + return false if target.isEqual(current) + + # We can't inspect the drag payload until drop, so we use a dataTransfer + # type to encode the account IDs of threads currently being dragged. + accountsType = event.dataTransfer.types.find((t) => t.startsWith('nylas-accounts=')) + accountIds = (accountsType || "").replace('nylas-accounts=', '').split(',') + return target.canReceiveThreadsFromAccountIds(accountIds) + + onSelect: (item) -> + Actions.focusMailboxPerspective(item.perspective) + }, opts) + + + @forCategories: (categories = [], opts = {}) -> + id = idForCategories(categories) + contextMenuLabel = _str.capitalize(categories[0]?.displayType()) + perspective = MailboxPerspective.forCategories(categories) + + opts.deletable ?= true + opts.editable ?= true + opts.contextMenuLabel = contextMenuLabel + @forPerspective(id, perspective, opts) + + @forSnoozed: (accountIds, opts = {}) -> + # TODO This constant should be available elsewhere + constants = require('../../thread-snooze/lib/snooze-constants') + displayName = constants.SNOOZE_CATEGORY_NAME + id = displayName + id += "-#{opts.name}" if opts.name + opts.name = "Snoozed" unless opts.name + opts.iconName= 'snooze.png' + + categories = accountIds.map (accId) => + _.findWhere CategoryStore.categories(accId), {displayName} + categories = _.compact(categories) + + perspective = MailboxPerspective.forCategories(categories) + perspective.name = id unless perspective.name + @forPerspective(id, perspective, opts) + + @forStarred: (accountIds, opts = {}) -> + perspective = MailboxPerspective.forStarred(accountIds) + id = 'Starred' + id += "-#{opts.name}" if opts.name + @forPerspective(id, perspective, opts) + + @forUnread: (accountIds, opts = {}) -> + categories = accountIds.map (accId) => + CategoryStore.getStandardCategory(accId, 'inbox') + + # NOTE: It's possible for an account to not yet have an `inbox` + # category. Since the `SidebarStore` triggers on `AccountStore` + # changes, it'll trigger the exact moment an account is added to the + # config. However, the API has not yet come back with the list of + # `categories` for that account. + categories = _.compact(categories) + + perspective = MailboxPerspective.forUnread(categories) + id = 'Unread' + id += "-#{opts.name}" if opts.name + @forPerspective(id, perspective, opts) + + @forDrafts: (accountIds, opts = {}) -> + perspective = MailboxPerspective.forDrafts(accountIds) + id = "Drafts-#{opts.name}" + @forPerspective(id, perspective, opts) + +module.exports = SidebarItem diff --git a/packages/client-app/internal_packages/account-sidebar/lib/sidebar-section.coffee b/packages/client-app/internal_packages/account-sidebar/lib/sidebar-section.coffee new file mode 100644 index 0000000000..ceff3e96b9 --- /dev/null +++ b/packages/client-app/internal_packages/account-sidebar/lib/sidebar-section.coffee @@ -0,0 +1,189 @@ +_ = require 'underscore' +{Actions, + SyncbackCategoryTask, + DestroyCategoryTask, + CategoryStore, + Category, + ExtensionRegistry, + RegExpUtils} = require 'nylas-exports' +SidebarItem = require './sidebar-item' +SidebarActions = require './sidebar-actions' + +isSectionCollapsed = (title) -> + if NylasEnv.savedState.sidebarKeysCollapsed[title] isnt undefined + NylasEnv.savedState.sidebarKeysCollapsed[title] + else + false + +toggleSectionCollapsed = (section) -> + return unless section + SidebarActions.setKeyCollapsed(section.title, not isSectionCollapsed(section.title)) + +class SidebarSection + + @empty: (title) -> + return { + title, + items: [] + } + + @standardSectionForAccount: (account) -> + if not account + throw new Error("standardSectionForAccount: You must pass an account.") + + cats = CategoryStore.standardCategories(account) + return @empty(account.label) if cats.length is 0 + + items = _ + .reject(cats, (cat) -> cat.name is 'drafts') + .map (cat) => SidebarItem.forCategories([cat], editable: false, deletable: false) + + unreadItem = SidebarItem.forUnread([account.id]) + starredItem = SidebarItem.forStarred([account.id]) + draftsItem = SidebarItem.forDrafts([account.id]) + snoozedItem = SidebarItem.forSnoozed([account.id]) + + extensionItems = ExtensionRegistry.AccountSidebar.extensions() + .filter((ext) => ext.sidebarItem?) + .map((ext) => ext.sidebarItem([account.id])) + .map(({id, name, iconName, perspective}) => + SidebarItem.forPerspective(id, perspective, {name, iconName}) + ) + + # Order correctly: Inbox, Unread, Starred, rest... , Drafts + items.splice(1, 0, unreadItem, starredItem, snoozedItem, extensionItems...) + items.push(draftsItem) + + return { + title: account.label + items: items + } + + @standardSectionForAccounts: (accounts) -> + return @empty('All Accounts') if not accounts or accounts.length is 0 + return @empty('All Accounts') if CategoryStore.categories().length is 0 + return @standardSectionForAccount(accounts[0]) if accounts.length is 1 + + standardNames = [ + 'inbox', + 'important' + 'sent', + ['archive', 'all'], + 'spam' + 'trash' + ] + items = [] + + for names in standardNames + names = if Array.isArray(names) then names else [names] + categories = CategoryStore.getStandardCategories(accounts, names...) + continue if categories.length is 0 + + children = [] + accounts.forEach (acc) -> + cat = _.first(_.compact( + names.map((name) -> CategoryStore.getStandardCategory(acc, name)) + )) + return unless cat + children.push(SidebarItem.forCategories([cat], name: acc.label, editable: false, deletable: false)) + + items.push SidebarItem.forCategories(categories, {children, editable: false, deletable: false}) + + accountIds = _.pluck(accounts, 'id') + + starredItem = SidebarItem.forStarred(accountIds, + children: accounts.map (acc) -> SidebarItem.forStarred([acc.id], name: acc.label) + ) + unreadItem = SidebarItem.forUnread(accountIds, + children: accounts.map (acc) -> SidebarItem.forUnread([acc.id], name: acc.label) + ) + draftsItem = SidebarItem.forDrafts(accountIds, + children: accounts.map (acc) -> SidebarItem.forDrafts([acc.id], name: acc.label) + ) + snoozedItem = SidebarItem.forSnoozed(accountIds, + children: accounts.map (acc) -> SidebarItem.forSnoozed([acc.id], name: acc.label) + ) + + extensionItems = ExtensionRegistry.AccountSidebar.extensions() + .filter((ext) => ext.sidebarItem?) + .map((ext) => + {id, name, iconName, perspective} = ext.sidebarItem(accountIds) + return SidebarItem.forPerspective(id, perspective, { + name, + iconName, + children: accounts.map((acc) => + subItem = ext.sidebarItem([acc.id]) + return SidebarItem.forPerspective( + subItem.id + "-#{acc.id}", + subItem.perspective, + {name: acc.label, iconName: subItem.iconName} + ) + ) + }) + ) + + # Order correctly: Inbox, Unread, Starred, rest... , Drafts + items.splice(1, 0, unreadItem, starredItem, snoozedItem, extensionItems...) + items.push(draftsItem) + + return { + title: 'All Accounts' + items: items + } + + + @forUserCategories: (account, {title, collapsible} = {}) -> + return unless account + # Compute hierarchy for user categories using known "path" separators + # NOTE: This code uses the fact that userCategoryItems is a sorted set, eg: + # + # Inbox + # Inbox.FolderA + # Inbox.FolderA.FolderB + # Inbox.FolderB + # + items = [] + seenItems = {} + for category in CategoryStore.userCategories(account) + # https://regex101.com/r/jK8cC2/1 + re = RegExpUtils.subcategorySplitRegex() + itemKey = category.displayName.replace(re, '/') + + parent = null + parentComponents = itemKey.split('/') + for i in [parentComponents.length..1] by -1 + parentKey = parentComponents[0...i].join('/') + parent = seenItems[parentKey] + break if parent + + if parent + itemDisplayName = category.displayName.substr(parentKey.length+1) + item = SidebarItem.forCategories([category], name: itemDisplayName) + parent.children.push(item) + else + item = SidebarItem.forCategories([category]) + items.push(item) + seenItems[itemKey] = item + + + title ?= account.categoryLabel() + collapsed = isSectionCollapsed(title) + if collapsible + onCollapseToggled = toggleSectionCollapsed + + return { + title: title + iconName: account.categoryIcon() + items: items + collapsed: collapsed + onCollapseToggled: onCollapseToggled + onItemCreated: (displayName) -> + return unless displayName + category = new Category + displayName: displayName + accountId: account.id + Actions.queueTask(new SyncbackCategoryTask({category})) + } + + +module.exports = SidebarSection diff --git a/packages/client-app/internal_packages/account-sidebar/lib/sidebar-store.coffee b/packages/client-app/internal_packages/account-sidebar/lib/sidebar-store.coffee new file mode 100644 index 0000000000..aec2375369 --- /dev/null +++ b/packages/client-app/internal_packages/account-sidebar/lib/sidebar-store.coffee @@ -0,0 +1,124 @@ +_ = require 'underscore' +NylasStore = require 'nylas-store' +{Actions, + AccountStore, + ThreadCountsStore, + WorkspaceStore, + OutboxStore, + FocusedPerspectiveStore, + CategoryStore} = require 'nylas-exports' + +SidebarSection = require './sidebar-section' +SidebarActions = require './sidebar-actions' +AccountCommands = require './account-commands' + +Sections = { + "Standard", + "User" +} + +class SidebarStore extends NylasStore + + constructor: -> + NylasEnv.savedState.sidebarKeysCollapsed ?= {} + + @_sections = {} + @_sections[Sections.Standard] = {} + @_sections[Sections.User] = [] + @_registerCommands() + @_registerMenuItems() + @_registerListeners() + @_updateSections() + + accounts: -> + AccountStore.accounts() + + sidebarAccountIds: -> + FocusedPerspectiveStore.sidebarAccountIds() + + standardSection: -> + @_sections[Sections.Standard] + + userSections: -> + @_sections[Sections.User] + + _registerListeners: -> + @listenTo Actions.setCollapsedSidebarItem, @_onSetCollapsedByName + @listenTo SidebarActions.setKeyCollapsed, @_onSetCollapsedByKey + @listenTo AccountStore, @_onAccountsChanged + @listenTo FocusedPerspectiveStore, @_onFocusedPerspectiveChanged + @listenTo WorkspaceStore, @_updateSections + @listenTo OutboxStore, @_updateSections + @listenTo ThreadCountsStore, @_updateSections + @listenTo CategoryStore, @_updateSections + + @configSubscription = NylasEnv.config.onDidChange( + 'core.workspace.showUnreadForAllCategories', + @_updateSections + ) + + return + + _onSetCollapsedByKey: (itemKey, collapsed) => + currentValue = NylasEnv.savedState.sidebarKeysCollapsed[itemKey] + if currentValue isnt collapsed + NylasEnv.savedState.sidebarKeysCollapsed[itemKey] = collapsed + @_updateSections() + + _onSetCollapsedByName: (itemName, collapsed) => + item = _.findWhere(@standardSection().items, {name: itemName}) + if not item + for section in @userSections() + item = _.findWhere(section.items, {name: itemName}) + break if item + return unless item + @_onSetCollapsedByKey(item.id, collapsed) + + _registerCommands: (accounts = AccountStore.accounts()) => + AccountCommands.registerCommands(accounts) + + _registerMenuItems: (accounts = AccountStore.accounts()) => + AccountCommands.registerMenuItems(accounts, FocusedPerspectiveStore.sidebarAccountIds()) + + # TODO Refactor this + # Listen to changes on the account store only for when the account label + # or order changes. When accounts or added or removed, those changes will + # come in through the FocusedPerspectiveStore + _onAccountsChanged: => + @_updateSections() + + # TODO Refactor this + # The FocusedPerspectiveStore tells this store the accounts that should be + # displayed in the sidebar (i.e. unified inbox vs single account) and will + # trigger whenever an account is added or removed, as well as when a + # perspective is focused. + # However, when udpating the SidebarSections, we also depend on the actual + # accounts in the AccountStore. The problem is that the FocusedPerspectiveStore + # triggers before the AccountStore is actually updated, so we need to wait for + # the AccountStore to get updated (via `defer`) before updateing our sidebar + # sections + _onFocusedPerspectiveChanged: => + _.defer => + @_registerCommands() + @_registerMenuItems() + @_updateSections() + + _updateSections: => + accounts = FocusedPerspectiveStore.sidebarAccountIds() + .map((id) => AccountStore.accountForId(id)) + .filter((a) => !!a) + + return if accounts.length is 0 + multiAccount = accounts.length > 1 + + @_sections[Sections.Standard] = SidebarSection.standardSectionForAccounts(accounts) + @_sections[Sections.User] = accounts.map (acc) -> + opts = {} + if multiAccount + opts.title = acc.label + opts.collapsible = true + SidebarSection.forUserCategories(acc, opts) + @trigger() + + +module.exports = new SidebarStore() diff --git a/packages/client-app/internal_packages/account-sidebar/package.json b/packages/client-app/internal_packages/account-sidebar/package.json new file mode 100755 index 0000000000..eabb4b992a --- /dev/null +++ b/packages/client-app/internal_packages/account-sidebar/package.json @@ -0,0 +1,11 @@ +{ + "name": "account-sidebar", + "version": "0.1.0", + "main": "./lib/main", + "description": "Sidebar view that shows your account and important tags", + "license": "GPL-3.0", + "private": true, + "engines": { + "nylas": "*" + } +} diff --git a/packages/client-app/internal_packages/account-sidebar/spec/sidebar-item-spec.es6 b/packages/client-app/internal_packages/account-sidebar/spec/sidebar-item-spec.es6 new file mode 100644 index 0000000000..3103bf16ce --- /dev/null +++ b/packages/client-app/internal_packages/account-sidebar/spec/sidebar-item-spec.es6 @@ -0,0 +1,23 @@ +import {Category, Actions} from "nylas-exports" +import SidebarItem from "../lib/sidebar-item" + +describe("sidebar-item", function sidebarItemSpec() { + it("preserves nested labels on rename", () => { + spyOn(Actions, "queueTask") + const categories = [new Category({displayName: 'a.b/c', accountId: window.TEST_ACCOUNT_ID})] + NylasEnv.savedState.sidebarKeysCollapsed = {} + const item = SidebarItem.forCategories(categories) + item.onEdited(item, 'd') + const task = Actions.queueTask.calls[0].args[0] + expect(task.displayName).toBe("a.b/d") + }) + it("preserves labels on rename", () => { + spyOn(Actions, "queueTask") + const categories = [new Category({displayName: 'a', accountId: window.TEST_ACCOUNT_ID})] + NylasEnv.savedState.sidebarKeysCollapsed = {} + const item = SidebarItem.forCategories(categories) + item.onEdited(item, 'b') + const task = Actions.queueTask.calls[0].args[0] + expect(task.displayName).toBe("b") + }) +}) diff --git a/packages/client-app/internal_packages/account-sidebar/stylesheets/account-sidebar.less b/packages/client-app/internal_packages/account-sidebar/stylesheets/account-sidebar.less new file mode 100644 index 0000000000..e50b07cedc --- /dev/null +++ b/packages/client-app/internal_packages/account-sidebar/stylesheets/account-sidebar.less @@ -0,0 +1,30 @@ +@import "ui-variables"; +@import "ui-mixins"; + +.account-sidebar { + flex: 1; + height: 100%; + background-color: @source-list-bg; + + .item.deleted { + opacity: 0.5; + } + + .nylas-outline-view:first-child { + .heading span { + margin-right: 30px; + } + } +} + +.account-switcher { + position: absolute; + top: 7px; + right: 17px; + z-index: 3; + img { + transform: rotateX(180deg); + } +} + + diff --git a/packages/client-app/internal_packages/attachments/lib/main.es6 b/packages/client-app/internal_packages/attachments/lib/main.es6 new file mode 100644 index 0000000000..d1b983ed39 --- /dev/null +++ b/packages/client-app/internal_packages/attachments/lib/main.es6 @@ -0,0 +1,10 @@ +import {ComponentRegistry} from 'nylas-exports'; +import MessageAttachments from './message-attachments' + +export function activate() { + ComponentRegistry.register(MessageAttachments, {role: 'MessageAttachments'}) +} + +export function deactivate() { + ComponentRegistry.unregister(MessageAttachments); +} diff --git a/packages/client-app/internal_packages/attachments/lib/message-attachments.jsx b/packages/client-app/internal_packages/attachments/lib/message-attachments.jsx new file mode 100644 index 0000000000..baaa4675ea --- /dev/null +++ b/packages/client-app/internal_packages/attachments/lib/message-attachments.jsx @@ -0,0 +1,92 @@ +import React, {Component, PropTypes} from 'react' +import {Actions, Utils, FileDownloadStore} from 'nylas-exports' +import {AttachmentItem, ImageAttachmentItem} from 'nylas-component-kit' + + +class MessageAttachments extends Component { + static displayName = 'MessageAttachments' + + static containerRequired = false + + static propTypes = { + files: PropTypes.array, + downloads: PropTypes.object, + messageClientId: PropTypes.string, + filePreviewPaths: PropTypes.object, + canRemoveAttachments: PropTypes.bool, + } + + static defaultProps = { + downloads: {}, + filePreviewPaths: {}, + } + + onOpenAttachment = (file) => { + Actions.fetchAndOpenFile(file) + } + + onRemoveAttachment = (file) => { + const {messageClientId} = this.props + Actions.removeFile({ + file: file, + messageClientId: messageClientId, + }) + } + + onDownloadAttachment = (file) => { + Actions.fetchAndSaveFile(file) + } + + onAbortDownload = (file) => { + Actions.abortFetchFile(file) + } + + renderAttachment(AttachmentRenderer, file) { + const {canRemoveAttachments, downloads, filePreviewPaths} = this.props + const download = downloads[file.id] + const filePath = FileDownloadStore.pathForFile(file) + const fileIconName = `file-${file.displayExtension()}.png` + const displayName = file.displayName() + const displaySize = file.displayFileSize() + const contentType = file.contentType + const displayFilePreview = NylasEnv.config.get('core.attachments.displayFilePreview') + const filePreviewPath = displayFilePreview ? filePreviewPaths[file.id] : null; + + return ( + this.onOpenAttachment(file)} + onDownloadAttachment={() => this.onDownloadAttachment(file)} + onAbortDownload={() => this.onAbortDownload(file)} + onRemoveAttachment={canRemoveAttachments ? () => this.onRemoveAttachment(file) : null} + /> + ) + } + + render() { + const {files} = this.props; + const nonImageFiles = files.filter((f) => !Utils.shouldDisplayAsImage(f)); + const imageFiles = files.filter((f) => Utils.shouldDisplayAsImage(f)); + return ( +
+ {nonImageFiles.map((file) => + this.renderAttachment(AttachmentItem, file) + )} + {imageFiles.map((file) => + this.renderAttachment(ImageAttachmentItem, file) + )} +
+ ) + } +} + +export default MessageAttachments diff --git a/packages/client-app/internal_packages/attachments/package.json b/packages/client-app/internal_packages/attachments/package.json new file mode 100644 index 0000000000..95c1d6de3e --- /dev/null +++ b/packages/client-app/internal_packages/attachments/package.json @@ -0,0 +1,17 @@ +{ + "name": "attachments", + "version": "0.1.0", + "main": "./lib/main", + "description": "View attachments on messages", + "license": "GPL-3.0", + "private": true, + "engines": { + "nylas": "*" + }, + "windowTypes": { + "default": true, + "composer": true, + "composer-preload": true, + "thread-popout": true + } +} diff --git a/packages/client-app/internal_packages/category-picker/lib/category-picker-popover.jsx b/packages/client-app/internal_packages/category-picker/lib/category-picker-popover.jsx new file mode 100644 index 0000000000..dae5a121b7 --- /dev/null +++ b/packages/client-app/internal_packages/category-picker/lib/category-picker-popover.jsx @@ -0,0 +1,360 @@ +/* eslint jsx-a11y/tabindex-no-positive: 0 */ +import _ from 'underscore' +import React, {Component, PropTypes} from 'react' +import { + Menu, + RetinaImg, + LabelColorizer, + BoldedSearchResult, +} from 'nylas-component-kit' +import { + Utils, + Actions, + TaskQueueStatusStore, + DatabaseStore, + TaskFactory, + Category, + SyncbackCategoryTask, + CategoryStore, + FocusedPerspectiveStore, +} from 'nylas-exports' +import {Categories} from 'nylas-observables' + + +export default class CategoryPickerPopover extends Component { + + static propTypes = { + threads: PropTypes.array.isRequired, + account: PropTypes.object.isRequired, + }; + + constructor(props) { + super(props) + this._categories = [] + this._standardCategories = [] + this._userCategories = [] + this.state = this._recalculateState(this.props, {searchValue: ''}) + } + + componentDidMount() { + this._registerObservables() + } + + componentWillReceiveProps(nextProps) { + this._registerObservables(nextProps) + this.setState(this._recalculateState(nextProps)) + } + + componentWillUnmount() { + this._unregisterObservables() + } + + _registerObservables = (props = this.props) => { + this._unregisterObservables() + this.disposables = [ + Categories.forAccount(props.account).sort().subscribe(this._onCategoriesChanged), + ] + }; + + _unregisterObservables = () => { + if (this.disposables) { + this.disposables.forEach(disp => disp.dispose()) + } + }; + + _isInSearch = (searchValue, category) => { + return Utils.wordSearchRegExp(searchValue).test(category.displayName) + }; + + _isUserFacing = (allInInbox, category) => { + const currentCategories = FocusedPerspectiveStore.current().categories() || [] + const currentCategoryIds = _.pluck(currentCategories, 'id') + const {account} = this.props + let hiddenCategories = [] + + if (account) { + if (account.usesLabels()) { + hiddenCategories = Category.StandardCategoryNames.concat(["starred", "N1-Snoozed"]) + if (allInInbox) { + hiddenCategories.push("inbox") + } + if (category.divider) { + return false + } + } else if (account.usesFolders()) { + hiddenCategories = ["drafts", "sent", "N1-Snoozed"] + } + } + return ( + (!hiddenCategories.includes(category.name)) && + (!hiddenCategories.includes(category.displayName)) && + (!currentCategoryIds.includes(category.id)) + ) + }; + + _itemForCategory = ({usageCount, numThreads}, category) => { + if (category.divider) { + return category + } + const item = category.toJSON() + item.category = category + item.backgroundColor = LabelColorizer.backgroundColorDark(category) + item.usage = usageCount[category.id] || 0 + item.numThreads = numThreads + return item + }; + + _allInInbox = (usageCount, numThreads) => { + const {account} = this.props + const inbox = CategoryStore.getStandardCategory(account, "inbox") + if (!inbox) return false + return usageCount[inbox.id] === numThreads + }; + + _categoryUsageCount = (props) => { + const {threads} = props + const categoryUsageCount = {} + _.flatten(_.pluck(threads, 'categories')).forEach((category) => { + categoryUsageCount[category.id] = categoryUsageCount[category.id] || 0 + categoryUsageCount[category.id] += 1 + }) + return categoryUsageCount; + }; + + _recalculateState = (props = this.props, {searchValue = (this.state.searchValue || "")} = {}) => { + const {account, threads} = props + + const numThreads = threads.length + let categories; + + if (numThreads === 0) { + return {categoryData: [], searchValue} + } + + if (account.usesLabels()) { + categories = this._categories + } else { + categories = this._standardCategories + .concat([{divider: true, id: "category-divider"}]) + .concat(this._userCategories) + } + + const usageCount = this._categoryUsageCount(props, categories) + const allInInbox = this._allInInbox(usageCount, numThreads) + const displayData = {usageCount, numThreads} + + const categoryData = _.chain(categories) + .filter(_.partial(this._isUserFacing, allInInbox)) + .filter(_.partial(this._isInSearch, searchValue)) + .map(_.partial(this._itemForCategory, displayData)) + .value() + + if (searchValue.length > 0) { + const newItemData = { + searchValue: searchValue, + newCategoryItem: true, + id: "category-create-new", + } + categoryData.push(newItemData) + } + return {categoryData, searchValue} + }; + + _onCategoriesChanged = (categories) => { + this._categories = categories + this._standardCategories = categories.filter((cat) => cat.isStandardCategory()) + this._userCategories = categories.filter((cat) => cat.isUserCategory()) + this.setState(this._recalculateState()) + }; + + _onEscape = () => { + Actions.closePopover() + }; + + _onSelectCategory = (item) => { + const {account, threads} = this.props + + if (threads.length === 0) return; + + if (item.newCategoryItem) { + const category = new Category({ + displayName: this.state.searchValue, + accountId: account.id, + }) + const syncbackTask = new SyncbackCategoryTask({category}) + + TaskQueueStatusStore.waitForPerformRemote(syncbackTask).then(() => { + DatabaseStore.findBy(category.constructor, {clientId: category.clientId}) + .then((cat) => { + if (!cat) { + const categoryType = account.usesLabels() ? "label" : "folder"; + NylasEnv.showErrorDialog({title: "Error", message: `Could not create ${categoryType}.`}) + return; + } + Actions.applyCategoryToThreads({ + source: "Category Picker: New Category", + threads: threads, + categoryToApply: cat, + }) + }) + }) + Actions.queueTask(syncbackTask) + } else if (item.usage === threads.length) { + Actions.removeCategoryFromThreads({ + source: "Category Picker: Existing Category", + threads: threads, + categoryToRemove: item.category, + }) + } else { + Actions.applyCategoryToThreads({ + source: "Category Picker: Existing Category", + threads: threads, + categoryToApply: item.category, + }) + } + if (account.usesFolders()) { + // In case we are drilled down into a message + Actions.popSheet() + } + Actions.closePopover() + }; + + _onSearchValueChange = (event) => { + this.setState( + this._recalculateState(this.props, {searchValue: event.target.value}) + ) + }; + + _renderFolderIcon = (item) => { + return ( + + ) + }; + + _renderCheckbox = (item) => { + const styles = {} + let checkStatus; + styles.backgroundColor = item.backgroundColor + + if (item.usage === 0) { + checkStatus = + } else if (item.usage < item.numThreads) { + checkStatus = ( + this._onSelectCategory(item)} + /> + ) + } else { + checkStatus = ( + this._onSelectCategory(item)} + /> + ) + } + + return ( +
+ this._onSelectCategory(item)} + /> + {checkStatus} +
+ ) + }; + + _renderCreateNewItem = ({searchValue}) => { + const {account} = this.props + let picName = '' + if (account) { + picName = account.usesLabels() ? 'tag' : 'folder' + } + + return ( +
+ +
+ “{searchValue}” (create new) +
+
+ ) + }; + + _renderItem = (item) => { + if (item.divider) { + return + } else if (item.newCategoryItem) { + return this._renderCreateNewItem(item) + } + + const {account} = this.props + let icon; + + if (account) { + icon = account.usesLabels() ? this._renderCheckbox(item) : this._renderFolderIcon(item); + } else { + return + } + + return ( +
+ {icon} +
+ +
+
+ ) + }; + + render() { + const {account} = this.props + let placeholder = '' + if (account) { + placeholder = account.usesLabels() ? 'Label as' : 'Move to folder' + } + + const headerComponents = [ + , + ] + + return ( +
+ item.id} + itemContent={this._renderItem} + onSelect={this._onSelectCategory} + onEscape={this._onEscape} + defaultSelectedIndex={this.state.searchValue === "" ? -1 : 0} + /> +
+ ) + } +} diff --git a/packages/client-app/internal_packages/category-picker/lib/category-picker.cjsx b/packages/client-app/internal_packages/category-picker/lib/category-picker.cjsx new file mode 100644 index 0000000000..b45da73b8b --- /dev/null +++ b/packages/client-app/internal_packages/category-picker/lib/category-picker.cjsx @@ -0,0 +1,86 @@ +_ = require 'underscore' +React = require 'react' +ReactDOM = require 'react-dom' + +{Actions, + AccountStore, + WorkspaceStore} = require 'nylas-exports' + +{RetinaImg, + KeyCommandsRegion} = require 'nylas-component-kit' + +CategoryPickerPopover = require('./category-picker-popover').default + + +# This changes the category on one or more threads. +class CategoryPicker extends React.Component + @displayName: "CategoryPicker" + + @containerRequired: false + + @propTypes: + items: React.PropTypes.array + + @contextTypes: + sheetDepth: React.PropTypes.number + + constructor: (@props) -> + @_account = AccountStore.accountForItems(@props.items) + + # If the threads we're picking categories for change, (like when they + # get their categories updated), we expect our parents to pass us new + # props. We don't listen to the DatabaseStore ourselves. + componentWillReceiveProps: (nextProps) -> + @_account = AccountStore.accountForItems(nextProps.items) + + _keymapHandlers: -> + "core:change-category": @_onOpenCategoryPopover + + _onOpenCategoryPopover: => + return unless @props.items.length > 0 + return unless @context.sheetDepth is WorkspaceStore.sheetStack().length - 1 + buttonRect = ReactDOM.findDOMNode(@refs.button).getBoundingClientRect() + Actions.openPopover( + , + {originRect: buttonRect, direction: 'down'} + ) + return + + render: => + return unless @_account + btnClasses = "btn btn-toolbar btn-category-picker" + img = "" + tooltip = "" + if @_account.usesLabels() + img = "toolbar-tag.png" + tooltip = "Apply Labels" + else + img = "toolbar-movetofolder.png" + tooltip = "Move to Folder" + + return ( + + + + ) + + +module.exports = CategoryPicker diff --git a/packages/client-app/internal_packages/category-picker/lib/main.cjsx b/packages/client-app/internal_packages/category-picker/lib/main.cjsx new file mode 100644 index 0000000000..bf7f186704 --- /dev/null +++ b/packages/client-app/internal_packages/category-picker/lib/main.cjsx @@ -0,0 +1,12 @@ +CategoryPicker = require "./category-picker" + +{ComponentRegistry, + WorkspaceStore} = require 'nylas-exports' + +module.exports = + activate: (@state={}) -> + ComponentRegistry.register CategoryPicker, + role: 'ThreadActionsToolbarButton' + + deactivate: -> + ComponentRegistry.unregister(CategoryPicker) diff --git a/packages/client-app/internal_packages/category-picker/package.json b/packages/client-app/internal_packages/category-picker/package.json new file mode 100755 index 0000000000..d5c57b7982 --- /dev/null +++ b/packages/client-app/internal_packages/category-picker/package.json @@ -0,0 +1,11 @@ +{ + "name": "category-picker", + "version": "0.1.0", + "main": "./lib/main", + "description": "Label & Folder Picker", + "license": "GPL-3.0", + "private": true, + "engines": { + "nylas": "*" + } +} diff --git a/packages/client-app/internal_packages/category-picker/spec/category-picker-spec.cjsx b/packages/client-app/internal_packages/category-picker/spec/category-picker-spec.cjsx new file mode 100644 index 0000000000..efea677736 --- /dev/null +++ b/packages/client-app/internal_packages/category-picker/spec/category-picker-spec.cjsx @@ -0,0 +1,205 @@ +_ = require 'underscore' +React = require "react" +ReactDOM = require 'react-dom' +ReactTestUtils = require 'react-addons-test-utils' +CategoryPickerPopover = require('../lib/category-picker-popover').default + +{Utils, + Category, + Thread, + Actions, + AccountStore, + CategoryStore, + DatabaseStore, + TaskFactory, + SyncbackCategoryTask, + FocusedPerspectiveStore, + MailboxPerspective, + NylasTestUtils, + TaskQueueStatusStore} = require 'nylas-exports' + +{Categories} = require 'nylas-observables' + +describe 'CategoryPickerPopover', -> + beforeEach -> + CategoryStore._categoryCache = {} + + afterEach -> + NylasEnv.testOrganizationUnit = null + + setupFor = (organizationUnit) -> + NylasEnv.testOrganizationUnit = organizationUnit + @account = { + id: TEST_ACCOUNT_ID + usesLabels: -> organizationUnit is "label" + usesFolders: -> organizationUnit isnt "label" + } + + @inboxCategory = new Category(id: 'id-123', name: 'inbox', displayName: "INBOX", accountId: TEST_ACCOUNT_ID) + @archiveCategory = new Category(id: 'id-456', name: 'archive', displayName: "ArCHIVe", accountId: TEST_ACCOUNT_ID) + @userCategory = new Category(id: 'id-789', name: null, displayName: "MyCategory", accountId: TEST_ACCOUNT_ID) + + observable = NylasTestUtils.mockObservable([@inboxCategory, @archiveCategory, @userCategory]) + observable.sort = => observable + + spyOn(Categories, "forAccount").andReturn observable + spyOn(CategoryStore, "getStandardCategory").andReturn @inboxCategory + spyOn(AccountStore, "accountForItems").andReturn @account + spyOn(Actions, "closePopover") + + # By default we're going to set to "inbox". This has implications for + # what categories get filtered out of the list. + spyOn(FocusedPerspectiveStore, 'current').andCallFake => + MailboxPerspective.forCategory(@inboxCategory) + + setupForCreateNew = (orgUnit = "folder") -> + setupFor.call(@, orgUnit) + + @testThread = new Thread(id: 't1', subject: "fake", accountId: TEST_ACCOUNT_ID, categories: []) + @picker = ReactTestUtils.renderIntoDocument( + + ) + + describe 'when using labels', -> + beforeEach -> + setupFor.call(@, "label") + + describe 'when using folders', -> + beforeEach -> + setupFor.call(@, "folder") + + @testThread = new Thread(id: 't1', subject: "fake", accountId: TEST_ACCOUNT_ID, categories: []) + @picker = ReactTestUtils.renderIntoDocument( + + ) + + it 'lists the desired categories', -> + data = @picker.state.categoryData + # NOTE: The inbox category is not included here because it's the + # currently focused category, which gets filtered out of the list. + expect(data.length).toBe 3 + + expect(data[0].id).toBe "id-456" + expect(data[0].name).toBe "archive" + expect(data[0].category).toBe @archiveCategory + + expect(data[1].divider).toBe true + expect(data[1].id).toBe "category-divider" + + expect(data[2].id).toBe "id-789" + expect(data[2].name).toBeUndefined() + expect(data[2].category).toBe @userCategory + + describe "'create new' item", -> + beforeEach -> + setupForCreateNew.call @ + + afterEach -> NylasEnv.testOrganizationUnit = null + + it "is not visible when the search box is empty", -> + count = ReactTestUtils.scryRenderedDOMComponentsWithClass(@picker, 'category-create-new').length + expect(count).toBe 0 + + it "is visible when the search box has text", -> + inputNode = ReactDOM.findDOMNode(ReactTestUtils.scryRenderedDOMComponentsWithTag(@picker, "input")[0]) + ReactTestUtils.Simulate.change inputNode, target: { value: "calendar" } + count = ReactTestUtils.scryRenderedDOMComponentsWithClass(@picker, 'category-create-new').length + expect(count).toBe 1 + + it "shows folder icon if we're using exchange", -> + inputNode = ReactDOM.findDOMNode(ReactTestUtils.scryRenderedDOMComponentsWithTag(@picker, "input")[0]) + ReactTestUtils.Simulate.change inputNode, target: { value: "calendar" } + count = ReactTestUtils.scryRenderedDOMComponentsWithClass(@picker, 'category-create-new-folder').length + expect(count).toBe 1 + + describe "'create new' item with labels", -> + beforeEach -> + setupForCreateNew.call @, "label" + + it "shows label icon if we're using gmail", -> + inputNode = ReactDOM.findDOMNode(ReactTestUtils.scryRenderedDOMComponentsWithTag(@picker, "input")[0]) + ReactTestUtils.Simulate.change inputNode, target: { value: "calendar" } + count = ReactTestUtils.scryRenderedDOMComponentsWithClass(@picker, 'category-create-new-tag').length + expect(count).toBe 1 + + describe "_onSelectCategory", -> + beforeEach -> + setupForCreateNew.call @, "folder" + spyOn(Actions, "applyCategoryToThreads") + spyOn(Actions, "removeCategoryFromThreads") + spyOn(Actions, "queueTask") + spyOn(Actions, "queueTasks") + + it "closes the popover", -> + @picker._onSelectCategory { usage: 0, category: "asdf" } + expect(Actions.closePopover).toHaveBeenCalled() + + describe "when selecting a category currently on all the selected items", -> + it "fires a task to remove the category", -> + input = + category: "asdf" + usage: 1 + + @picker._onSelectCategory(input) + expect(Actions.removeCategoryFromThreads).toHaveBeenCalledWith + threads: [@testThread] + source: 'Category Picker: Existing Category' + categoryToRemove: "asdf" + + describe "when selecting a category not on all the selected items", -> + it "fires a task to add the category", -> + input = + category: "asdf" + usage: 0 + + @picker._onSelectCategory(input) + expect(Actions.applyCategoryToThreads).toHaveBeenCalledWith + source: 'Category Picker: Existing Category' + threads: [@testThread] + categoryToApply: "asdf" + + describe "when selecting a new category", -> + beforeEach -> + @input = + newCategoryItem: true + @picker.setState(searchValue: "teSTing!") + + it "queues a new syncback task for creating a category", -> + @picker._onSelectCategory(@input) + expect(Actions.queueTask).toHaveBeenCalled() + syncbackTask = Actions.queueTask.calls[0].args[0] + newCategory = syncbackTask.category + expect(newCategory instanceof Category).toBe(true) + expect(newCategory.displayName).toBe "teSTing!" + expect(newCategory.accountId).toBe TEST_ACCOUNT_ID + + it "queues a task for applying the category after it has saved", -> + category = false + resolveSave = false + spyOn(TaskQueueStatusStore, "waitForPerformRemote").andCallFake (task) -> + expect(task instanceof SyncbackCategoryTask).toBe true + new Promise (resolve, reject) -> + resolveSave = resolve + + spyOn(DatabaseStore, "findBy").andCallFake (klass, {clientId}) -> + expect(klass).toBe(Category) + expect(typeof clientId).toBe("string") + Promise.resolve(category) + + @picker._onSelectCategory(@input) + + waitsFor -> + Actions.queueTask.callCount > 0 + + runs -> + category = Actions.queueTask.calls[0].args[0].category + resolveSave() + + waitsFor -> + Actions.applyCategoryToThreads.calls.length is 1 + + runs -> + expect(Actions.applyCategoryToThreads).toHaveBeenCalledWith + source: 'Category Picker: New Category' + threads: [@testThread] + categoryToApply: category diff --git a/packages/client-app/internal_packages/category-picker/stylesheets/category-picker.less b/packages/client-app/internal_packages/category-picker/stylesheets/category-picker.less new file mode 100644 index 0000000000..2b862f5759 --- /dev/null +++ b/packages/client-app/internal_packages/category-picker/stylesheets/category-picker.less @@ -0,0 +1,82 @@ +@import "ui-variables"; + +@popover-width: 250px; + +body.platform-win32 { + .category-picker-popover { + margin-left: 0; + } +} + +.sheet-toolbar .btn-category-picker:only-of-type { + margin-right: 0; +} + +.category-picker-popover { + .menu { + background: @background-secondary; + width: @popover-width; + max-height: 400px; + + .header-container { + border-bottom: 0; + } + + .item.divider { + background-color: #e0e0e0; + margin: 4px 0; + height: 2px; + padding: 0; + } + + } + + .btn.btn-toolbar { + margin-left: 0; + margin-right: 0; + } + + .check-wrap { + width: 14px; + height: 14px; + border-radius: 2px; + display: inline-block; + position: relative; + flex-shrink: 0; + top: 2px; + } + + .item { + img.content-mask { + position: relative; + top:3px; + background-color: @text-color-subtle; + } + } + .item.selected, .item.active { + img.content-mask { + background-color: @text-color-inverse; + } + } + + img.check-img { + position: absolute; + } + + .category-item { + font-size: 14px; + display: flex; + } + + .category-create-new-tag { + flex-shrink: 0; + } + + .category-display-name { + display: inline-block; + margin-left: 10px; + margin-right: 5px; + word-break: break-word; + flex: 1; + } +} diff --git a/packages/client-app/internal_packages/composer-emoji/assets/icon.png b/packages/client-app/internal_packages/composer-emoji/assets/icon.png new file mode 100644 index 0000000000..d8c359fe83 Binary files /dev/null and b/packages/client-app/internal_packages/composer-emoji/assets/icon.png differ diff --git a/packages/client-app/internal_packages/composer-emoji/lib/categorized-emoji.es6 b/packages/client-app/internal_packages/composer-emoji/lib/categorized-emoji.es6 new file mode 100644 index 0000000000..54159aa80a --- /dev/null +++ b/packages/client-app/internal_packages/composer-emoji/lib/categorized-emoji.es6 @@ -0,0 +1,1311 @@ +const categorizedEmojiList = { + 'People': [ + 'grinning', + 'grimacing', + 'grin', + 'joy', + 'smiley', + 'smile', + 'sweat_smile', + 'laughing', + 'innocent', + 'wink', + 'blush', + 'slightly_smiling_face', + 'upside_down_face', + 'relaxed', + 'yum', + 'relieved', + 'heart_eyes', + 'kissing_heart', + 'kissing', + 'kissing_smiling_eyes', + 'kissing_closed_eyes', + 'stuck_out_tongue_winking_eye', + 'stuck_out_tongue_closed_eyes', + 'stuck_out_tongue', + 'money_mouth_face', + 'nerd_face', + 'sunglasses', + 'hugging_face', + 'smirk', + 'no_mouth', + 'neutral_face', + 'expressionless', + 'unamused', + 'face_with_rolling_eyes', + 'thinking_face', + 'flushed', + 'disappointed', + 'worried', + 'angry', + 'rage', + 'pensive', + 'confused', + 'slightly_frowning_face', + 'white_frowning_face', + 'persevere', + 'confounded', + 'tired_face', + 'weary', + 'triumph', + 'open_mouth', + 'scream', + 'fearful', + 'cold_sweat', + 'hushed', + 'frowning', + 'anguished', + 'cry', + 'disappointed_relieved', + 'sleepy', + 'sweat', + 'sob', + 'dizzy_face', + 'astonished', + 'zipper_mouth_face', + 'mask', + 'face_with_thermometer', + 'face_with_head_bandage', + 'sleeping', + 'zzz', + 'poop', + 'smiling_imp', + 'imp', + 'japanese_ogre', + 'japanese_goblin', + 'skull', + 'ghost', + 'alien', + 'robot_face', + 'smiley_cat', + 'smile_cat', + 'joy_cat', + 'heart_eyes_cat', + 'smirk_cat', + 'kissing_cat', + 'scream_cat', + 'crying_cat_face', + 'pouting_cat', + 'raised_hands', + 'clap', + 'wave', + 'thumbsup', + 'thumbsdown', + 'punch', + 'fist', + 'v', + 'ok_hand', + 'hand', + 'open_hands', + 'muscle', + 'pray', + 'point_up', + 'point_up_2', + 'point_down', + 'point_left', + 'point_right', + 'middle_finger', + 'raised_hand_with_fingers_splayed', + 'the_horns', + 'spock-hand', + 'writing_hand', + 'nail_care', + 'lips', + 'tongue', + 'ear', + 'nose', + 'eye', + 'eyes', + 'bust_in_silhouette', + 'busts_in_silhouette', + 'speaking_head_in_silhouette', + 'baby', + 'boy', + 'girl', + 'man', + 'woman', + 'person_with_blond_hair', + 'older_man', + 'older_woman', + 'man_with_gua_pi_mao', + 'man_with_turban', + 'cop', + 'construction_worker', + 'guardsman', + 'sleuth_or_spy', + 'santa', + 'angel', + 'princess', + 'bride_with_veil', + 'walking', + 'running', + 'dancer', + 'dancers', + 'couple', + 'two_men_holding_hands', + 'two_women_holding_hands', + 'bow', + 'information_desk_person', + 'no_good', + 'ok_woman', + 'raising_hand', + 'person_with_pouting_face', + 'person_frowning', + 'haircut', + 'massage', + 'couple_with_heart', + 'woman-heart-woman', + 'man-heart-man', + 'couplekiss', + 'woman-kiss-woman', + 'man-kiss-man', + 'family', + 'man-woman-girl', + 'man-woman-girl-boy', + 'man-woman-boy-boy', + 'man-woman-girl-girl', + 'woman-woman-boy', + 'woman-woman-girl', + 'woman-woman-girl-boy', + 'woman-woman-boy-boy', + 'woman-woman-girl-girl', + 'man-man-boy', + 'man-man-girl', + 'man-man-girl-boy', + 'man-man-boy-boy', + 'man-man-girl-girl', + 'womans_clothes', + 'shirt', + 'jeans', + 'necktie', + 'dress', + 'bikini', + 'kimono', + 'lipstick', + 'kiss', + 'footprints', + 'high_heel', + 'sandal', + 'boot', + 'mans_shoe', + 'athletic_shoe', + 'womans_hat', + 'tophat', + 'helmet_with_white_cross', + 'mortar_board', + 'crown', + 'school_satchel', + 'pouch', + 'purse', + 'handbag', + 'briefcase', + 'eyeglasses', + 'dark_sunglasses', + 'ring', + 'closed_umbrella', + ], + 'Nature': [ + 'dog', + 'cat', + 'mouse', + 'hamster', + 'rabbit', + 'bear', + 'panda_face', + 'koala', + 'tiger', + 'lion_face', + 'cow', + 'pig', + 'pig_nose', + 'frog', + 'octopus', + 'monkey_face', + 'see_no_evil', + 'hear_no_evil', + 'speak_no_evil', + 'monkey', + 'chicken', + 'penguin', + 'bird', + 'baby_chick', + 'hatching_chick', + 'hatched_chick', + 'wolf', + 'boar', + 'horse', + 'unicorn_face', + 'bee', + 'bug', + 'snail', + 'beetle', + 'ant', + 'spider', + 'scorpion', + 'crab', + 'snake', + 'turtle', + 'tropical_fish', + 'fish', + 'blowfish', + 'dolphin', + 'whale', + 'whale2', + 'crocodile', + 'leopard', + 'tiger2', + 'water_buffalo', + 'ox', + 'cow2', + 'dromedary_camel', + 'camel', + 'elephant', + 'goat', + 'ram', + 'sheep', + 'racehorse', + 'pig2', + 'rat', + 'mouse2', + 'rooster', + 'turkey', + 'dove_of_peace', + 'dog2', + 'poodle', + 'cat2', + 'rabbit2', + 'chipmunk', + 'paw_prints', + 'dragon', + 'dragon_face', + 'cactus', + 'christmas_tree', + 'evergreen_tree', + 'deciduous_tree', + 'palm_tree', + 'seedling', + 'herb', + 'shamrock', + 'four_leaf_clover', + 'bamboo', + 'tanabata_tree', + 'leaves', + 'fallen_leaf', + 'maple_leaf', + 'ear_of_rice', + 'hibiscus', + 'sunflower', + 'rose', + 'tulip', + 'blossom', + 'cherry_blossom', + 'bouquet', + 'mushroom', + 'chestnut', + 'jack_o_lantern', + 'shell', + 'spider_web', + 'earth_americas', + 'earth_africa', + 'earth_asia', + 'full_moon', + 'waning_gibbous_moon', + 'last_quarter_moon', + 'waning_crescent_moon', + 'new_moon', + 'waxing_crescent_moon', + 'first_quarter_moon', + 'moon', + 'new_moon_with_face', + 'full_moon_with_face', + 'first_quarter_moon_with_face', + 'last_quarter_moon_with_face', + 'sun_with_face', + 'crescent_moon', + 'star', + 'star2', + 'dizzy', + 'sparkles', + 'comet', + 'sunny', + 'mostly_sunny', + 'partly_sunny', + 'barely_sunny', + 'partly_sunny_rain', + 'cloud', + 'rain_cloud', + 'thunder_cloud_and_rain', + 'lightning', + 'zap', + 'fire', + 'boom', + 'snowflake', + 'snow_cloud', + 'showman', + 'snowman', + 'wind_blowing_face', + 'dash', + 'tornado', + 'fog', + 'umbrella', + 'droplet', + 'sweat_drops', + 'ocean', + ], + 'Food and Drink': [ + 'green_apple', + 'apple', + 'pear', + 'tangerine', + 'lemon', + 'banana', + 'watermelon', + 'grapes', + 'strawberry', + 'melon', + 'cherries', + 'peach', + 'pineapple', + 'tomato', + 'eggplant', + 'hot_pepper', + 'corn', + 'sweet_potato', + 'honey_pot', + 'bread', + 'cheese_wedge', + 'poultry_leg', + 'meat_on_bone', + 'fried_shrimp', + 'egg', + 'hamburger', + 'fries', + 'hotdog', + 'pizza', + 'spaghetti', + 'taco', + 'burrito', + 'ramen', + 'stew', + 'fish_cake', + 'sushi', + 'bento', + 'curry', + 'rice_ball', + 'rice', + 'rice_cracker', + 'oden', + 'dango', + 'shaved_ice', + 'ice_cream', + 'icecream', + 'cake', + 'birthday', + 'custard', + 'candy', + 'lollipop', + 'chocolate_bar', + 'popcorn', + 'doughnut', + 'cookie', + 'beer', + 'beers', + 'wine_glass', + 'cocktail', + 'tropical_drink', + 'champagne', + 'sake', + 'tea', + 'coffee', + 'baby_bottle', + 'fork_and_knife', + 'knife_fork_plate', + ], + 'Activity': [ + 'soccer', + 'basketball', + 'football', + 'baseball', + 'tennis', + 'volleyball', + 'rugby_football', + '8ball', + 'golf', + 'golfer', + 'table_tennis_paddle_and_ball', + 'badminton_racquet_and_shuttlecock', + 'ice_hockey_stick_and_puck', + 'field_hockey_stick_and_ball', + 'cricket_bat_and_ball', + 'ski', + 'skier', + 'snowboarder', + 'ice_skate', + 'bow_and_arrow', + 'fishing_pole_and_fish', + 'rowboat', + 'swimmer', + 'surfer', + 'bath', + 'person_with_ball', + 'weight_lifter', + 'bicyclist', + 'mountain_bicyclist', + 'horse_racing', + 'man_in_business_suit_levitating', + 'trophy', + 'running_shirt_with_sash', + 'sports_medal', + 'medal', + 'reminder_ribbon', + 'rosette', + 'ticket', + 'admission_tickets', + 'performing_arts', + 'art', + 'circus_tent', + 'microphone', + 'headphones', + 'musical_score', + 'musical_keyboard', + 'saxophone', + 'trumpet', + 'guitar', + 'violin', + 'clapper', + 'video_game', + 'space_invader', + 'dart', + 'game_die', + 'slot_machine', + 'bowling', + ], + 'Travel and Places': [ + 'car', + 'taxi', + 'blue_car', + 'bus', + 'trolleybus', + 'racing_car', + 'police_car', + 'ambulance', + 'fire_engine', + 'minibus', + 'truck', + 'articulated_lorry', + 'tractor', + 'racing_motorcycle', + 'bike', + 'rotating_light', + 'oncoming_police_car', + 'oncoming_bus', + 'oncoming_automobile', + 'oncoming_taxi', + 'aerial_tramway', + 'mountain_cableway', + 'suspension_railway', + 'railway_car', + 'train', + 'monorail', + 'bullettrain_side', + 'bullettrain_front', + 'light_rail', + 'mountain_railway', + 'steam_locomotive', + 'train2', + 'metro', + 'tram', + 'station', + 'helicopter', + 'small_airplane', + 'airplane', + 'airplane_departure', + 'airplane_arriving', + 'boat', + 'motor_boat', + 'speedboat', + 'ferry', + 'passenger_ship', + 'rocket', + 'satellite', + 'seat', + 'anchor', + 'construction', + 'fuelpump', + 'busstop', + 'vertical_traffic_light', + 'traffic_light', + 'checkered_flag', + 'ship', + 'ferris_wheel', + 'roller_coaster', + 'carousel_horse', + 'building_construction', + 'foggy', + 'tokyo_tower', + 'factory', + 'fountain', + 'rice_scene', + 'mountain', + 'snow_capped_mountain', + 'mount_fuji', + 'volcano', + 'japan', + 'camping', + 'tent', + 'national_park', + 'motorway', + 'railway_track', + 'sunrise', + 'sunrise_over_mountains', + 'desert', + 'beach_with_umbrella', + 'desert_island', + 'city_sunrise', + 'city_sunset', + 'cityscape', + 'night_with_stars', + 'bridge_at_night', + 'milky_way', + 'stars', + 'sparkler', + 'fireworks', + 'rainbow', + 'house_buildings', + 'european_castle', + 'japanese_castle', + 'stadium', + 'statue_of_liberty', + 'house', + 'house_with_garden', + 'derelict_house_building', + 'office', + 'department_store', + 'post_office', + 'european_post_office', + 'hospital', + 'bank', + 'hotel', + 'convenience_store', + 'school', + 'love_hotel', + 'wedding', + 'classical_building', + 'church', + 'mosque', + 'synagogue', + 'kaaba', + 'shinto_shrine', + ], + 'Objects': [ + 'watch', + 'iphone', + 'calling', + 'computer', + 'keyboard', + 'desktop_computer', + 'printer', + 'three_button_mouse', + 'trackball', + 'joystick', + 'compression', + 'minidisc', + 'floppy_disk', + 'cd', + 'dvd', + 'vhs', + 'camera', + 'camera_with_flash', + 'video_camera', + 'movie_camera', + 'film_projector', + 'film_frames', + 'telephone_receiver', + 'phone', + 'pager', + 'fax', + 'tv', + 'radio', + 'studio_microphone', + 'level_slider', + 'control_knobs', + 'stopwatch', + 'timer_clock', + 'alarm_clock', + 'mantelpiece_clock', + 'hourglass_flowing_sand', + 'hourglass', + 'battery', + 'electric_plug', + 'bulb', + 'flashlight', + 'candle', + 'wastebasket', + 'oil_drum', + 'money_with_wings', + 'dollar', + 'yen', + 'euro', + 'pound', + 'moneybag', + 'credit_card', + 'gem', + 'scales', + 'wrench', + 'hammer', + 'hammer_and_pick', + 'hammer_and_wrench', + 'pick', + 'nut_and_bolt', + 'gear', + 'chains', + 'gun', + 'bomb', + 'knife', + 'dagger_knife', + 'crossed_swords', + 'shield', + 'smoking', + 'skull_and_crossbones', + 'coffin', + 'funeral_urn', + 'amphora', + 'crystal_ball', + 'prayer_beads', + 'barber', + 'alembic', + 'telescope', + 'microscope', + 'hole', + 'pill', + 'syringe', + 'thermometer', + 'label', + 'bookmark', + 'toilet', + 'shower', + 'bathtub', + 'key', + 'old_key', + 'couch_and_lamp', + 'sleeping_accommodation', + 'bed', + 'door', + 'bellhop_bell', + 'frame_with_picture', + 'world_map', + 'umbrella_on_ground', + 'moyai', + 'shopping_bags', + 'balloon', + 'flags', + 'ribbon', + 'gift', + 'confetti_ball', + 'tada', + 'dolls', + 'wind_chime', + 'crossed_flags', + 'izakaya_lantern', + 'envelope', + 'envelope_with_arrow', + 'incoming_envelope', + 'e-mail', + 'love_letter', + 'postbox', + 'mailbox_closed', + 'mailbox', + 'mailbox_with_mail', + 'mailbox_with_no_mail', + 'package', + 'postal_horn', + 'inbox_tray', + 'outbox_tray', + 'scroll', + 'page_with_curl', + 'bookmark_tabs', + 'bar_chart', + 'chart_with_upwards_trend', + 'chart_with_downwards_trend', + 'page_facing_up', + 'date', + 'calendar', + 'spiral_calendar_pad', + 'card_index', + 'card_file_box', + 'ballot_box_with_ballot', + 'file_cabinet', + 'clipboard', + 'spiral_note_pad', + 'file_folder', + 'open_file_folder', + 'card_index_dividers', + 'rolled_up_newspaper', + 'newspaper', + 'notebook', + 'closed_book', + 'green_book', + 'blue_book', + 'orange_book', + 'notebook_with_decorative_cover', + 'ledger', + 'books', + 'book', + 'link', + 'paperclip', + 'linked_paperclips', + 'scissors', + 'triangular_ruler', + 'straight_ruler', + 'pushpin', + 'round_pushpin', + 'triangular_flag_on_post', + 'waving_white_flag', + 'waving_black_flag', + 'closed_lock_with_key', + 'lock', + 'unlock', + 'lock_with_ink_pen', + 'lower_left_ballpoint_pen', + 'lower_left_fountain_pen', + 'black_nib', + 'memo', + 'pencil2', + 'lower_left_crayon', + 'lower_left_paintbrush', + 'mag', + 'mag_right', + ], + 'Symbols': [ + 'heart', + 'yellow_heart', + 'green_heart', + 'blue_heart', + 'purple_heart', + 'broken_heart', + 'heavy_heart_exclamation_mark_ornament', + 'two_hearts', + 'revolving_hearts', + 'heartbeat', + 'heartpulse', + 'sparkling_heart', + 'cupid', + 'gift_heart', + 'heart_decoration', + 'peace_symbol', + 'latin_cross', + 'star_and_crescent', + 'om_symbol', + 'wheel_of_dharma', + 'star_of_david', + 'six_pointed_star', + 'menorah_with_nine_branches', + 'yin_yang', + 'orthodox_cross', + 'place_of_worship', + 'ophiuchus', + 'aries', + 'taurus', + 'gemini', + 'cancer', + 'leo', + 'virgo', + 'libra', + 'scorpius', + 'sagittarius', + 'capricorn', + 'aquarius', + 'pisces', + 'id', + 'atom_symbol', + 'u7a7a', + 'u5272', + 'radioactive_sign', + 'biohazard_sign', + 'mobile_phone_off', + 'vibration_mode', + 'u6709', + 'u7121', + 'u7533', + 'u55b6', + 'u6708', + 'eight_pointed_black_star', + 'vs', + 'accept', + 'white_flower', + 'ideograph_advantage', + 'secret', + 'congratulations', + 'u5408', + 'u6e80', + 'u7981', + 'a', + 'b', + 'ab', + 'cl', + 'o2', + 'sos', + 'no_entry', + 'name_badge', + 'no_entry_sign', + 'x', + 'o', + 'anger', + 'hotsprings', + 'no_pedestrians', + 'do_not_litter', + 'no_bicycles', + 'non-potable_water', + 'underage', + 'no_mobile_phones', + 'exclamation', + 'grey_exclamation', + 'question', + 'grey_question', + 'bangbang', + 'interrobang', + '100', + 'low_brightness', + 'high_brightness', + 'trident', + 'fleur_de_lis', + 'part_alternation_mark', + 'warning', + 'children_crossing', + 'beginner', + 'recycle', + 'u6307', + 'chart', + 'sparkle', + 'eight_spoked_asterisk', + 'negative_squared_cross_mark', + 'white_check_mark', + 'diamond_shape_with_a_dot_inside', + 'cyclone', + 'loop', + 'globe_with_meridians', + 'm', + 'atm', + 'sa', + 'passport_control', + 'customs', + 'baggage_claim', + 'left_luggage', + 'wheelchair', + 'no_smoking', + 'wc', + 'parking', + 'potable_water', + 'mens', + 'womens', + 'baby_symbol', + 'restroom', + 'put_litter_in_its_place', + 'cinema', + 'signal_strength', + 'koko', + 'ng', + 'ok', + 'up', + 'cool', + 'new', + 'free', + 'zero', + 'one', + 'two', + 'three', + 'four', + 'five', + 'six', + 'seven', + 'eight', + 'nine', + 'keycap_ten', + 'keycap_star', + '1234', + 'arrow_forward', + 'double_vertical_bar', + 'black_right_pointing_triangle_with_double_vertical_bar', + 'black_square_for_stop', + 'black_circle_for_record', + 'black_right_pointing_double_triangle_with_vertical_bar', + 'black_left_pointing_double_triangle_with_vertical_bar', + 'fast_forward', + 'rewind', + 'twisted_rightwards_arrows', + 'repeat', + 'repeat_one', + 'arrow_backward', + 'arrow_up_small', + 'arrow_down_small', + 'arrow_double_up', + 'arrow_double_down', + 'arrow_right', + 'arrow_left', + 'arrow_up', + 'arrow_down', + 'arrow_upper_right', + 'arrow_lower_right', + 'arrow_lower_left', + 'arrow_upper_left', + 'arrow_up_down', + 'left_right_arrow', + 'arrows_counterclockwise', + 'arrow_right_hook', + 'leftwards_arrow_with_hook', + 'arrow_heading_up', + 'arrow_heading_down', + 'hash', + 'information_source', + 'abc', + 'abcd', + 'capital_abcd', + 'symbols', + 'musical_note', + 'notes', + 'wavy_dash', + 'curly_loop', + 'heavy_check_mark', + 'arrows_clockwise', + 'heavy_plus_sign', + 'heavy_minus_sign', + 'heavy_division_sign', + 'heavy_multiplication_x', + 'heavy_dollar_sign', + 'currency_exchange', + 'copyright', + 'registered', + 'tm', + 'end', + 'back', + 'on', + 'top', + 'soon', + 'ballot_box_with_check', + 'radio_button', + 'white_circle', + 'black_circle', + 'red_circle', + 'large_blue_circle', + 'small_orange_diamond', + 'small_blue_diamond', + 'large_orange_diamond', + 'large_blue_diamond', + 'small_red_triangle', + 'black_small_square', + 'white_small_square', + 'black_large_square', + 'white_large_square', + 'small_red_triangle_down', + 'black_medium_square', + 'white_medium_square', + 'black_medium_small_square', + 'white_medium_small_square', + 'black_square_button', + 'white_square_button', + 'speaker', + 'sound', + 'loud_sound', + 'mute', + 'mega', + 'loudspeaker', + 'bell', + 'no_bell', + 'black_joker', + 'mahjong', + 'spades', + 'clubs', + 'hearts', + 'diamonds', + 'flower_playing_cards', + 'thought_balloon', + 'right_anger_bubble', + 'speech_balloon', + 'left_speech_bubble', + 'clock1', + 'clock2', + 'clock3', + 'clock4', + 'clock5', + 'clock6', + 'clock7', + 'clock8', + 'clock9', + 'clock10', + 'clock11', + 'clock12', + 'clock130', + 'clock230', + 'clock330', + 'clock430', + 'clock530', + 'clock630', + 'clock730', + 'clock830', + 'clock930', + 'clock1030', + 'clock1130', + 'clock1230', + ], + 'Flags': [ + 'flag-ac', + 'flag-ad', + 'flag-ae', + 'flag-af', + 'flag-ag', + 'flag-ai', + 'flag-al', + 'flag-am', + 'flag-ao', + 'flag-aq', + 'flag-ar', + 'flag-as', + 'flag-at', + 'flag-au', + 'flag-aw', + 'flag-ax', + 'flag-az', + 'flag-ba', + 'flag-bb', + 'flag-bd', + 'flag-be', + 'flag-bf', + 'flag-bg', + 'flag-bh', + 'flag-bi', + 'flag-bj', + 'flag-bl', + 'flag-bm', + 'flag-bn', + 'flag-bo', + 'flag-bq', + 'flag-br', + 'flag-bs', + 'flag-bt', + 'flag-bv', + 'flag-bw', + 'flag-by', + 'flag-bz', + 'flag-ca', + 'flag-cc', + 'flag-cd', + 'flag-cf', + 'flag-cg', + 'flag-ch', + 'flag-ci', + 'flag-ck', + 'flag-cl', + 'flag-cm', + 'flag-cn', + 'flag-co', + 'flag-cp', + 'flag-cr', + 'flag-cu', + 'flag-cv', + 'flag-cw', + 'flag-cx', + 'flag-cy', + 'flag-cz', + 'flag-de', + 'flag-dg', + 'flag-dj', + 'flag-dk', + 'flag-dm', + 'flag-do', + 'flag-dz', + 'flag-ea', + 'flag-ec', + 'flag-ee', + 'flag-eg', + 'flag-eh', + 'flag-er', + 'flag-es', + 'flag-et', + 'flag-eu', + 'flag-fi', + 'flag-fj', + 'flag-fk', + 'flag-fm', + 'flag-fo', + 'flag-fr', + 'flag-ga', + 'flag-gb', + 'flag-gd', + 'flag-ge', + 'flag-gf', + 'flag-gg', + 'flag-gh', + 'flag-gi', + 'flag-gl', + 'flag-gm', + 'flag-gn', + 'flag-gp', + 'flag-gq', + 'flag-gr', + 'flag-gs', + 'flag-gt', + 'flag-gu', + 'flag-gw', + 'flag-gy', + 'flag-hk', + 'flag-hm', + 'flag-hn', + 'flag-hr', + 'flag-ht', + 'flag-hu', + 'flag-ic', + 'flag-id', + 'flag-ie', + 'flag-il', + 'flag-im', + 'flag-in', + 'flag-io', + 'flag-iq', + 'flag-ir', + 'flag-is', + 'flag-it', + 'flag-je', + 'flag-jm', + 'flag-jo', + 'flag-jp', + 'flag-ke', + 'flag-kg', + 'flag-kh', + 'flag-ki', + 'flag-km', + 'flag-kn', + 'flag-kp', + 'flag-kr', + 'flag-kw', + 'flag-ky', + 'flag-kz', + 'flag-la', + 'flag-lb', + 'flag-lc', + 'flag-li', + 'flag-lk', + 'flag-lr', + 'flag-ls', + 'flag-lt', + 'flag-lu', + 'flag-lv', + 'flag-ly', + 'flag-ma', + 'flag-mc', + 'flag-md', + 'flag-me', + 'flag-mf', + 'flag-mg', + 'flag-mh', + 'flag-mk', + 'flag-ml', + 'flag-mm', + 'flag-mn', + 'flag-mo', + 'flag-mp', + 'flag-mq', + 'flag-mr', + 'flag-ms', + 'flag-mt', + 'flag-mu', + 'flag-mv', + 'flag-mw', + 'flag-mx', + 'flag-my', + 'flag-mz', + 'flag-na', + 'flag-nc', + 'flag-ne', + 'flag-nf', + 'flag-ng', + 'flag-ni', + 'flag-nl', + 'flag-no', + 'flag-np', + 'flag-nr', + 'flag-nu', + 'flag-nz', + 'flag-om', + 'flag-pa', + 'flag-pe', + 'flag-pf', + 'flag-pg', + 'flag-ph', + 'flag-pk', + 'flag-pl', + 'flag-pm', + 'flag-pn', + 'flag-pr', + 'flag-ps', + 'flag-pt', + 'flag-pw', + 'flag-py', + 'flag-qa', + 'flag-re', + 'flag-ro', + 'flag-rs', + 'flag-ru', + 'flag-rw', + 'flag-sa', + 'flag-sb', + 'flag-sc', + 'flag-sd', + 'flag-se', + 'flag-sg', + 'flag-sh', + 'flag-si', + 'flag-sj', + 'flag-sk', + 'flag-sl', + 'flag-sm', + 'flag-sn', + 'flag-so', + 'flag-sr', + 'flag-ss', + 'flag-st', + 'flag-sv', + 'flag-sx', + 'flag-sy', + 'flag-sz', + 'flag-ta', + 'flag-tc', + 'flag-td', + 'flag-tf', + 'flag-tg', + 'flag-th', + 'flag-tj', + 'flag-tk', + 'flag-tl', + 'flag-tm', + 'flag-tn', + 'flag-to', + 'flag-tr', + 'flag-tt', + 'flag-tv', + 'flag-tw', + 'flag-tz', + 'flag-ua', + 'flag-ug', + 'flag-um', + 'flag-us', + 'flag-uy', + 'flag-uz', + 'flag-va', + 'flag-vc', + 'flag-ve', + 'flag-vg', + 'flag-vi', + 'flag-vn', + 'flag-vu', + 'flag-wf', + 'flag-ws', + 'flag-xk', + 'flag-ye', + 'flag-yt', + 'flag-za', + 'flag-zm', + 'flag-zw', + ], +} +export default categorizedEmojiList diff --git a/packages/client-app/internal_packages/composer-emoji/lib/emoji-actions.es6 b/packages/client-app/internal_packages/composer-emoji/lib/emoji-actions.es6 new file mode 100644 index 0000000000..ea0150df0f --- /dev/null +++ b/packages/client-app/internal_packages/composer-emoji/lib/emoji-actions.es6 @@ -0,0 +1,12 @@ +import Reflux from 'reflux'; + +const EmojiActions = Reflux.createActions([ + "selectEmoji", + "useEmoji", +]); + +for (const key of Object.keys(EmojiActions)) { + EmojiActions[key].sync = true; +} + +export default EmojiActions; diff --git a/packages/client-app/internal_packages/composer-emoji/lib/emoji-button-popover.jsx b/packages/client-app/internal_packages/composer-emoji/lib/emoji-button-popover.jsx new file mode 100644 index 0000000000..ade3c1151f --- /dev/null +++ b/packages/client-app/internal_packages/composer-emoji/lib/emoji-button-popover.jsx @@ -0,0 +1,330 @@ +import React from 'react'; +import {findDOMNode} from 'react-dom'; +import {Actions} from 'nylas-exports'; +import {RetinaImg, ScrollRegion} from 'nylas-component-kit'; + +import EmojiStore from './emoji-store'; +import EmojiActions from './emoji-actions'; +import categorizedEmojiList from './categorized-emoji'; + +class EmojiButtonPopover extends React.Component { + static displayName = 'EmojiButtonPopover'; + + constructor() { + super(); + const {categoryNames, + categorizedEmoji, + categoryPositions} = this.getStateFromStore(); + this.state = { + emojiName: "Emoji Picker", + categoryNames: categoryNames, + categorizedEmoji: categorizedEmoji, + categoryPositions: categoryPositions, + searchValue: "", + activeTab: Object.keys(categorizedEmoji)[0], + }; + } + + componentDidMount() { + this._mounted = true; + this._emojiPreloadImage = new Image(); + this.renderCanvas(); + } + + componentWillUnmount() { + this._emojiPreloadImage.onload = null; + this._emojiPreloadImage = null; + this._mounted = false; + } + + onMouseDown = (event) => { + const emojiName = this.calcEmojiByPosition(this.calcPosition(event)); + if (!emojiName) return null; + EmojiActions.selectEmoji({emojiName: emojiName, replaceSelection: false}); + Actions.closePopover(); + return null + } + + onScroll = () => { + const emojiContainer = document.querySelector(".emoji-finder-container .scroll-region-content"); + const tabContainer = document.querySelector(".emoji-tabs"); + tabContainer.className = emojiContainer.scrollTop ? "emoji-tabs shadow" : "emoji-tabs"; + if (emojiContainer.scrollTop === 0) { + this.setState({activeTab: Object.keys(this.state.categorizedEmoji)[0]}); + } else { + for (const category of Object.keys(this.state.categoryPositions)) { + if (emojiContainer.scrollTop >= this.state.categoryPositions[category].top && + emojiContainer.scrollTop <= this.state.categoryPositions[category].bottom) { + this.setState({activeTab: category}); + } + } + } + } + + onHover = (event) => { + const emojiName = this.calcEmojiByPosition(this.calcPosition(event)); + if (emojiName) { + this.setState({emojiName: emojiName}); + } else { + this.setState({emojiName: "Emoji Picker"}); + } + } + + onMouseOut = () => { + this.setState({emojiName: "Emoji Picker"}); + } + + onChange = (event) => { + const searchValue = event.target.value; + if (searchValue.length > 0) { + const searchMatches = this.findSearchMatches(searchValue); + this.setState({ + categorizedEmoji: { + 'Search Results': searchMatches, + }, + categoryPositions: { + 'Search Results': { + top: 25, + bottom: 25 + Math.ceil(searchMatches.length / 8) * 24, + }, + }, + searchValue: searchValue, + activeTab: null, + }, this.renderCanvas); + } else { + this.setState(this.getStateFromStore, () => { + this.setState({ + searchValue: searchValue, + activeTab: Object.keys(this.state.categorizedEmoji)[0], + }, this.renderCanvas); + }); + } + } + + getStateFromStore = () => { + let categorizedEmoji = categorizedEmojiList; + const categoryPositions = {}; + let categoryNames = [ + 'People', + 'Nature', + 'Food and Drink', + 'Activity', + 'Travel and Places', + 'Objects', + 'Symbols', + 'Flags', + ]; + const frequentlyUsedEmoji = EmojiStore.frequentlyUsedEmoji(); + if (frequentlyUsedEmoji.length > 0) { + categorizedEmoji = {'Frequently Used': frequentlyUsedEmoji}; + for (const category of Object.keys(categorizedEmojiList)) { + categorizedEmoji[category] = categorizedEmojiList[category]; + } + categoryNames = ["Frequently Used"].concat(categoryNames); + } + // Calculates where each category should be (variable because Frequently + // Used may or may not be present) + for (const name of categoryNames) { + categoryPositions[name] = {top: 0, bottom: 0}; + } + let verticalPos = 25; + for (const category of Object.keys(categoryPositions)) { + const height = Math.ceil(categorizedEmoji[category].length / 8) * 24; + categoryPositions[category].top = verticalPos; + verticalPos += height; + categoryPositions[category].bottom = verticalPos; + verticalPos += 24; + } + return { + categoryNames: categoryNames, + categorizedEmoji: categorizedEmoji, + categoryPositions: categoryPositions, + }; + } + + scrollToCategory(category) { + const container = document.querySelector(".emoji-finder-container .scroll-region-content"); + if (this.state.searchValue.length > 0) { + this.setState({searchValue: ""}); + this.setState(this.getStateFromStore, () => { + this.renderCanvas(); + container.scrollTop = this.state.categoryPositions[category].top + 16; + }); + } else { + container.scrollTop = this.state.categoryPositions[category].top + 16; + } + this.setState({activeTab: category}) + } + + findSearchMatches(searchValue) { + // TODO: Find matches for aliases, too. + const searchMatches = []; + for (const category of Object.keys(categorizedEmojiList)) { + categorizedEmojiList[category].forEach((emojiName) => { + if (emojiName.indexOf(searchValue) !== -1) { + searchMatches.push(emojiName); + } + }); + } + return searchMatches; + } + + calcPosition(event) { + const rect = event.target.getBoundingClientRect(); + const position = { + x: event.pageX - rect.left / 2, + y: event.pageY - rect.top / 2, + }; + return position; + } + + calcEmojiByPosition = (position) => { + for (const category of Object.keys(this.state.categoryPositions)) { + const LEFT_BOUNDARY = 8; + const RIGHT_BOUNDARY = 204; + const EMOJI_WIDTH = 24.5; + const EMOJI_HEIGHT = 24; + const EMOJI_PER_ROW = 8; + if (position.x >= LEFT_BOUNDARY && + position.x <= RIGHT_BOUNDARY && + position.y >= this.state.categoryPositions[category].top && + position.y <= this.state.categoryPositions[category].bottom) { + const x = Math.round((position.x + 5) / EMOJI_WIDTH); + const y = Math.round((position.y - this.state.categoryPositions[category].top + 10) / EMOJI_HEIGHT); + const index = x + (y - 1) * EMOJI_PER_ROW - 1; + return this.state.categorizedEmoji[category][index]; + } + } + return null; + } + + renderTabs() { + const tabs = []; + this.state.categoryNames.forEach((category) => { + let className = `emoji-tab ${(category.replace(/ /g, '-')).toLowerCase()}` + if (category === this.state.activeTab) { + className += " active"; + } + tabs.push( +
+ this.scrollToCategory(category)} + /> +
+ ); + }); + return tabs; + } + + renderCanvas() { + const canvas = findDOMNode(this.refs.emojiCanvas); + const keys = Object.keys(this.state.categoryPositions); + canvas.height = this.state.categoryPositions[keys[keys.length - 1]].bottom * 2; + const ctx = canvas.getContext("2d"); + ctx.font = "24px Nylas-Pro"; + ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; + ctx.clearRect(0, 0, canvas.width, canvas.height); + const position = { + x: 15, + y: 45, + } + + let idx = 0; + const categoryNames = Object.keys(this.state.categorizedEmoji); + const renderNextCategory = () => { + if (!categoryNames[idx]) return; + if (!this._mounted) return; + this.renderCategory(categoryNames[idx], idx, ctx, position, renderNextCategory); + idx += 1; + } + renderNextCategory(); + } + + renderCategory(category, i, ctx, pos, callback) { + const position = pos + if (i > 0) { + position.x = 18; + position.y += 48; + } + ctx.fillText(category, position.x, position.y); + position.x = 18; + position.y += 48; + + const emojiNames = this.state.categorizedEmoji[category]; + if (!emojiNames || emojiNames.length === 0) return; + + const emojiToDraw = emojiNames.map((emojiName, j) => { + const x = position.x; + const y = position.y; + const src = EmojiStore.getImagePath(emojiName); + + if (position.x > 325 && j < this.state.categorizedEmoji[category].length - 1) { + position.x = 18; + position.y += 48; + } else { + position.x += 50; + } + + return {src, x, y}; + }); + + const drawEmojiAt = ({src, x, y} = {}) => { + if (!src) { + return; + } + this._emojiPreloadImage.onload = () => { + this._emojiPreloadImage.onload = null; + ctx.drawImage(this._emojiPreloadImage, x, y - 30, 32, 32); + if (emojiToDraw.length === 0) { + callback(); + } else { + drawEmojiAt(emojiToDraw.shift()); + } + } + this._emojiPreloadImage.src = src; + } + + drawEmojiAt(emojiToDraw.shift()); + } + + render() { + return ( +
+
+ {this.renderTabs()} +
+ +
+ +
+ +
+
+ {this.state.emojiName} +
+
+ ); + } +} + +export default EmojiButtonPopover; diff --git a/packages/client-app/internal_packages/composer-emoji/lib/emoji-button.jsx b/packages/client-app/internal_packages/composer-emoji/lib/emoji-button.jsx new file mode 100644 index 0000000000..59eb56b652 --- /dev/null +++ b/packages/client-app/internal_packages/composer-emoji/lib/emoji-button.jsx @@ -0,0 +1,31 @@ +import {Actions, React, ReactDOM} from 'nylas-exports'; +import {RetinaImg} from 'nylas-component-kit'; + +import EmojiButtonPopover from './emoji-button-popover'; + + +class EmojiButton extends React.Component { + static displayName = 'EmojiButton'; + + onClick = () => { + const buttonRect = ReactDOM.findDOMNode(this).getBoundingClientRect(); + Actions.openPopover( + , + {originRect: buttonRect, direction: 'up'} + ) + } + + render() { + return ( + + ); + } +} + +EmojiButton.containerStyles = { + order: 2, +}; + +export default EmojiButton; diff --git a/packages/client-app/internal_packages/composer-emoji/lib/emoji-composer-extension.jsx b/packages/client-app/internal_packages/composer-emoji/lib/emoji-composer-extension.jsx new file mode 100644 index 0000000000..f70a27f0eb --- /dev/null +++ b/packages/client-app/internal_packages/composer-emoji/lib/emoji-composer-extension.jsx @@ -0,0 +1,292 @@ +import {DOMUtils, ComposerExtension, RegExpUtils} from 'nylas-exports'; +import emoji from 'node-emoji'; + +import EmojiStore from './emoji-store'; +import EmojiActions from './emoji-actions'; +import EmojiPicker from './emoji-picker'; + + +class EmojiComposerExtension extends ComposerExtension { + + static selState = null; + + static onContentChanged = ({editor}) => { + const sel = editor.currentSelection() + const {emojiOptions, triggerWord} = EmojiComposerExtension._findEmojiOptions(sel); + if (sel.anchorNode && sel.isCollapsed) { + if (emojiOptions.length > 0) { + const offset = sel.anchorOffset; + if (!DOMUtils.closest(sel.anchorNode, "n1-emoji-autocomplete")) { + const anchorOffset = Math.max(sel.anchorOffset - triggerWord.length - 1, 0); + editor.select(sel.anchorNode, + anchorOffset, + sel.focusNode, + sel.focusOffset) + editor.wrapSelection("n1-emoji-autocomplete"); + editor.select(sel.anchorNode, + offset, + sel.anchorNode, + offset); + } + } else { + if (DOMUtils.closest(sel.anchorNode, "n1-emoji-autocomplete")) { + editor.unwrapNodeAndSelectAll(DOMUtils.closest(sel.anchorNode, "n1-emoji-autocomplete")); + editor.select(sel.anchorNode, + sel.anchorOffset + triggerWord.length + 1, + sel.focusNode, + sel.focusOffset + triggerWord.length + 1); + } + } + } else { + if (DOMUtils.closest(sel.anchorNode, "n1-emoji-autocomplete")) { + editor.unwrapNodeAndSelectAll(DOMUtils.closest(sel.anchorNode, "n1-emoji-autocomplete")); + editor.select(sel.anchorNode, + sel.anchorOffset + triggerWord.length, + sel.focusNode, + sel.focusOffset + triggerWord.length); + } + } + }; + + static onBlur = ({editor}) => { + EmojiComposerExtension.selState = editor.currentSelection().exportSelection(); + }; + + static onFocus = ({editor}) => { + if (EmojiComposerExtension.selState) { + editor.select(EmojiComposerExtension.selState); + EmojiComposerExtension.selState = null; + } + }; + + static toolbarComponentConfig = ({toolbarState}) => { + const sel = toolbarState.selectionSnapshot; + if (sel) { + const {emojiOptions} = EmojiComposerExtension._findEmojiOptions(sel); + if (emojiOptions.length > 0 && !toolbarState.dragging && !toolbarState.doubleDown) { + const locationRefNode = DOMUtils.closest(sel.anchorNode, + "n1-emoji-autocomplete"); + if (!locationRefNode) return null; + const selectedEmoji = locationRefNode.getAttribute("selectedEmoji"); + return { + component: EmojiPicker, + props: {emojiOptions, + selectedEmoji}, + locationRefNode: locationRefNode, + width: EmojiComposerExtension._emojiPickerWidth(emojiOptions), + height: EmojiComposerExtension._emojiPickerHeight(emojiOptions), + hidePointer: true, + } + } + } + return null; + }; + + static editingActions = () => { + return [{ + action: EmojiActions.selectEmoji, + callback: EmojiComposerExtension._onSelectEmoji, + }] + }; + + static onKeyDown = ({editor, event}) => { + const sel = editor.currentSelection() + const {emojiOptions} = EmojiComposerExtension._findEmojiOptions(sel); + if (emojiOptions.length > 0) { + if (event.key === "ArrowDown" || event.key === "ArrowRight" || + event.key === "ArrowUp" || event.key === "ArrowLeft") { + event.preventDefault(); + const moveToNext = (event.key === "ArrowDown" || event.key === "ArrowRight"); + const emojiNameNode = DOMUtils.closest(sel.anchorNode, "n1-emoji-autocomplete"); + if (!emojiNameNode) return null; + const selectedEmoji = emojiNameNode.getAttribute("selectedEmoji"); + if (selectedEmoji) { + const emojiIndex = emojiOptions.indexOf(selectedEmoji); + if (emojiIndex < emojiOptions.length - 1 && moveToNext) { + emojiNameNode.setAttribute("selectedEmoji", emojiOptions[emojiIndex + 1]); + } else if (emojiIndex > 0 && !moveToNext) { + emojiNameNode.setAttribute("selectedEmoji", emojiOptions[emojiIndex - 1]); + } else { + const index = moveToNext ? 0 : emojiOptions.length - 1; + emojiNameNode.setAttribute("selectedEmoji", emojiOptions[index]); + } + } else { + const index = moveToNext ? 1 : emojiOptions.length - 1; + emojiNameNode.setAttribute("selectedEmoji", emojiOptions[index]); + } + } else if (event.key === "Enter" || event.key === "Tab") { + event.preventDefault(); + const emojiNameNode = DOMUtils.closest(sel.anchorNode, "n1-emoji-autocomplete"); + if (!emojiNameNode) return null; + let selectedEmoji = emojiNameNode.getAttribute("selectedEmoji"); + if (!selectedEmoji) selectedEmoji = emojiOptions[0]; + const args = { + editor: editor, + actionArg: { + emojiName: selectedEmoji, + replaceSelection: true, + }, + }; + EmojiComposerExtension._onSelectEmoji(args); + } + } + return null; + }; + + static applyTransformsForSending = ({draftBodyRootNode}) => { + const imgs = draftBodyRootNode.querySelectorAll('img') + for (const imgEl of Array.from(imgs)) { + const names = imgEl.className.split(' '); + if (names[0] === 'emoji') { + const emojiChar = emoji.get(names[1]); + if (emojiChar) { + imgEl.parentNode.replaceChild(document.createTextNode(emojiChar), imgEl); + } + } + } + } + + static unapplyTransformsForSending = ({draftBodyRootNode}) => { + const treeWalker = document.createTreeWalker(draftBodyRootNode, NodeFilter.SHOW_TEXT); + while (treeWalker.nextNode()) { + const textNode = treeWalker.currentNode; + const match = RegExpUtils.emojiRegex().exec(textNode.textContent); + if (match) { + const emojiPlusTrailingEl = textNode.splitText(match.index); + emojiPlusTrailingEl.splitText(match.length); + const emojiEl = emojiPlusTrailingEl; + const imgEl = document.createElement('img'); + const emojiName = emoji.which(match[0]) + imgEl.className = `emoji ${emojiName}`; + imgEl.src = EmojiStore.getImagePath(emojiName); + imgEl.width = '14'; + imgEl.height = '14'; + imgEl.style.marginTop = '-5px'; + emojiEl.parentNode.replaceChild(imgEl, emojiEl); + } + } + } + + static _findEmojiOptions(sel) { + if (sel.anchorNode && + sel.anchorNode.nodeValue && + sel.anchorNode.nodeValue.length > 0 && + sel.isCollapsed) { + const words = sel.anchorNode.nodeValue.substring(0, sel.anchorOffset); + let index = words.lastIndexOf(":"); + let lastWord = ""; + if (index !== -1 && words.lastIndexOf(" ") < index) { + lastWord = words.substring(index + 1, sel.anchorOffset); + } else { + const {text} = EmojiComposerExtension._getTextUntilSpace(sel.anchorNode, sel.anchorOffset); + index = text.lastIndexOf(":"); + if (index !== -1 && text.lastIndexOf(" ") < index) { + lastWord = text.substring(index + 1); + } else { + return {triggerWord: "", emojiOptions: []}; + } + } + if (lastWord.length > 0) { + return {triggerWord: lastWord, emojiOptions: EmojiComposerExtension._findMatches(lastWord)}; + } + return {triggerWord: lastWord, emojiOptions: []}; + } + return {triggerWord: "", emojiOptions: []}; + } + + static _onSelectEmoji = ({editor, actionArg}) => { + const {emojiName, replaceSelection} = actionArg; + if (!emojiName) return null; + if (replaceSelection) { + const sel = editor.currentSelection(); + if (sel.anchorNode && + sel.anchorNode.nodeValue && + sel.anchorNode.nodeValue.length > 0 && + sel.isCollapsed) { + const words = sel.anchorNode.nodeValue.substring(0, sel.anchorOffset); + let index = words.lastIndexOf(":"); + let lastWord = words.substring(index + 1, sel.anchorOffset); + if (index !== -1 && words.lastIndexOf(" ") < index) { + editor.select(sel.anchorNode, + sel.anchorOffset - lastWord.length - 1, + sel.focusNode, + sel.focusOffset); + } else { + const {text, textNode} = EmojiComposerExtension._getTextUntilSpace(sel.anchorNode, sel.anchorOffset); + index = text.lastIndexOf(":"); + lastWord = text.substring(index + 1); + const offset = textNode.nodeValue.lastIndexOf(":"); + editor.select(textNode, + offset, + sel.focusNode, + sel.focusOffset); + editor.delete(); + } + } + } + const emojiChar = emoji.get(emojiName); + const html = ``; + editor.insertHTML(html, {selectInsertion: false}); + EmojiActions.useEmoji({emojiName: emojiName, emojiChar: emojiChar}); + return null; + }; + + static _emojiPickerWidth(emojiOptions) { + let maxLength = 0; + for (const emojiOption of emojiOptions) { + if (emojiOption.length > maxLength) { + maxLength = emojiOption.length; + } + } + // TODO: Calculate width of words more accurately for a closer fit. + const WIDTH_PER_CHAR = 8; + return (maxLength + 10) * WIDTH_PER_CHAR; + } + + static _emojiPickerHeight(emojiOptions) { + const HEIGHT_PER_EMOJI = 25; + if (emojiOptions.length < 5) { + return emojiOptions.length * HEIGHT_PER_EMOJI + 20; + } + return 5 * HEIGHT_PER_EMOJI + 23; + } + + static _getTextUntilSpace(node, offset) { + let text = node.nodeValue.substring(0, offset); + let prevTextNode = DOMUtils.previousTextNode(node); + if (!prevTextNode) return {text: text, textNode: node}; + while (prevTextNode) { + if (prevTextNode.nodeValue.indexOf(" ") === -1 && + prevTextNode.nodeValue.indexOf(":") === -1) { + text = prevTextNode.nodeValue + text; + prevTextNode = DOMUtils.previousTextNode(prevTextNode); + } else if (prevTextNode.nextSibling && + prevTextNode.nextSibling.nodeName !== "DIV") { + text = prevTextNode.nodeValue.trim() + text; + break; + } else { + break; + } + } + return {text: text, textNode: prevTextNode}; + } + + static _findMatches(word) { + const emojiOptions = [] + const emojiNames = Object.keys(emoji.emoji).sort(); + for (const emojiName of emojiNames) { + if (word === emojiName.substring(0, word.length)) { + emojiOptions.push(emojiName); + } + } + return emojiOptions; + } + +} + +export default EmojiComposerExtension; diff --git a/packages/client-app/internal_packages/composer-emoji/lib/emoji-data.json b/packages/client-app/internal_packages/composer-emoji/lib/emoji-data.json new file mode 100644 index 0000000000..62be9f3212 --- /dev/null +++ b/packages/client-app/internal_packages/composer-emoji/lib/emoji-data.json @@ -0,0 +1 @@ +{"emojiData":[{"name":"COPYRIGHT SIGN","unified":"00A9","variations":["00A9-FE0F"],"docomo":"E731","au":"E558","softbank":"E24E","google":"FEB29","image":"00a9.png","sheet_x":0,"sheet_y":0,"short_name":"copyright","short_names":["copyright"],"text":null,"texts":null,"category":"Symbols","sort_order":197,"has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_emojione":true},{"name":"REGISTERED SIGN","unified":"00AE","variations":["00AE-FE0F"],"docomo":"E736","au":"E559","softbank":"E24F","google":"FEB2D","image":"00ae.png","sheet_x":0,"sheet_y":1,"short_name":"registered","short_names":["registered"],"text":null,"texts":null,"category":"Symbols","sort_order":198,"has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_emojione":true},{"name":"DOUBLE EXCLAMATION MARK","unified":"203C","variations":["203C-FE0F"],"docomo":"E704","au":"EB30","softbank":null,"google":"FEB06","image":"203c.png","sheet_x":0,"sheet_y":2,"short_name":"bangbang","short_names":["bangbang"],"text":null,"texts":null,"category":"Symbols","sort_order":86,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"EXCLAMATION QUESTION MARK","unified":"2049","variations":["2049-FE0F"],"docomo":"E703","au":"EB2F","softbank":null,"google":"FEB05","image":"2049.png","sheet_x":0,"sheet_y":3,"short_name":"interrobang","short_names":["interrobang"],"text":null,"texts":null,"category":"Symbols","sort_order":87,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TRADE MARK SIGN","unified":"2122","variations":["2122-FE0F"],"docomo":"E732","au":"E54E","softbank":"E537","google":"FEB2A","image":"2122.png","sheet_x":0,"sheet_y":4,"short_name":"tm","short_names":["tm"],"text":null,"texts":null,"category":"Symbols","sort_order":199,"has_img_apple":true,"has_img_google":true,"has_img_twitter":false,"has_img_emojione":true},{"name":"INFORMATION SOURCE","unified":"2139","variations":["2139-FE0F"],"docomo":null,"au":"E533","softbank":null,"google":"FEB47","image":"2139.png","sheet_x":0,"sheet_y":5,"short_name":"information_source","short_names":["information_source"],"text":null,"texts":null,"category":"Symbols","sort_order":180,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LEFT RIGHT ARROW","unified":"2194","variations":["2194-FE0F"],"docomo":"E73C","au":"EB7A","softbank":null,"google":"FEAF6","image":"2194.png","sheet_x":0,"sheet_y":6,"short_name":"left_right_arrow","short_names":["left_right_arrow"],"text":null,"texts":null,"category":"Symbols","sort_order":172,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"UP DOWN ARROW","unified":"2195","variations":["2195-FE0F"],"docomo":"E73D","au":"EB7B","softbank":null,"google":"FEAF7","image":"2195.png","sheet_x":0,"sheet_y":7,"short_name":"arrow_up_down","short_names":["arrow_up_down"],"text":null,"texts":null,"category":"Symbols","sort_order":171,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NORTH WEST ARROW","unified":"2196","variations":["2196-FE0F"],"docomo":"E697","au":"E54C","softbank":"E237","google":"FEAF2","image":"2196.png","sheet_x":0,"sheet_y":8,"short_name":"arrow_upper_left","short_names":["arrow_upper_left"],"text":null,"texts":null,"category":"Symbols","sort_order":170,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NORTH EAST ARROW","unified":"2197","variations":["2197-FE0F"],"docomo":"E678","au":"E555","softbank":"E236","google":"FEAF0","image":"2197.png","sheet_x":0,"sheet_y":9,"short_name":"arrow_upper_right","short_names":["arrow_upper_right"],"text":null,"texts":null,"category":"Symbols","sort_order":167,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SOUTH EAST ARROW","unified":"2198","variations":["2198-FE0F"],"docomo":"E696","au":"E54D","softbank":"E238","google":"FEAF1","image":"2198.png","sheet_x":0,"sheet_y":10,"short_name":"arrow_lower_right","short_names":["arrow_lower_right"],"text":null,"texts":null,"category":"Symbols","sort_order":168,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SOUTH WEST ARROW","unified":"2199","variations":["2199-FE0F"],"docomo":"E6A5","au":"E556","softbank":"E239","google":"FEAF3","image":"2199.png","sheet_x":0,"sheet_y":11,"short_name":"arrow_lower_left","short_names":["arrow_lower_left"],"text":null,"texts":null,"category":"Symbols","sort_order":169,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LEFTWARDS ARROW WITH HOOK","unified":"21A9","variations":["21A9-FE0F"],"docomo":"E6DA","au":"E55D","softbank":null,"google":"FEB83","image":"21a9.png","sheet_x":0,"sheet_y":12,"short_name":"leftwards_arrow_with_hook","short_names":["leftwards_arrow_with_hook"],"text":null,"texts":null,"category":"Symbols","sort_order":175,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RIGHTWARDS ARROW WITH HOOK","unified":"21AA","variations":["21AA-FE0F"],"docomo":null,"au":"E55C","softbank":null,"google":"FEB88","image":"21aa.png","sheet_x":0,"sheet_y":13,"short_name":"arrow_right_hook","short_names":["arrow_right_hook"],"text":null,"texts":null,"category":"Symbols","sort_order":174,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WATCH","unified":"231A","variations":["231A-FE0F"],"docomo":"E71F","au":"E57A","softbank":null,"google":"FE01D","image":"231a.png","sheet_x":0,"sheet_y":14,"short_name":"watch","short_names":["watch"],"text":null,"texts":null,"category":"Objects","sort_order":1,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HOURGLASS","unified":"231B","variations":["231B-FE0F"],"docomo":"E71C","au":"E57B","softbank":null,"google":"FE01C","image":"231b.png","sheet_x":0,"sheet_y":15,"short_name":"hourglass","short_names":["hourglass"],"text":null,"texts":null,"category":"Objects","sort_order":37,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KEYBOARD","unified":"2328","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2328.png","sheet_x":0,"sheet_y":16,"short_name":"keyboard","short_names":["keyboard"],"text":null,"texts":null,"category":"Objects","sort_order":5,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK RIGHT-POINTING DOUBLE TRIANGLE","unified":"23E9","variations":[],"docomo":null,"au":"E530","softbank":"E23C","google":"FEAFE","image":"23e9.png","sheet_x":0,"sheet_y":17,"short_name":"fast_forward","short_names":["fast_forward"],"text":null,"texts":null,"category":"Symbols","sort_order":153,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK LEFT-POINTING DOUBLE TRIANGLE","unified":"23EA","variations":[],"docomo":null,"au":"E52F","softbank":"E23D","google":"FEAFF","image":"23ea.png","sheet_x":0,"sheet_y":18,"short_name":"rewind","short_names":["rewind"],"text":null,"texts":null,"category":"Symbols","sort_order":154,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK UP-POINTING DOUBLE TRIANGLE","unified":"23EB","variations":[],"docomo":null,"au":"E545","softbank":null,"google":"FEB03","image":"23eb.png","sheet_x":0,"sheet_y":19,"short_name":"arrow_double_up","short_names":["arrow_double_up"],"text":null,"texts":null,"category":"Symbols","sort_order":161,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK DOWN-POINTING DOUBLE TRIANGLE","unified":"23EC","variations":[],"docomo":null,"au":"E544","softbank":null,"google":"FEB02","image":"23ec.png","sheet_x":0,"sheet_y":20,"short_name":"arrow_double_down","short_names":["arrow_double_down"],"text":null,"texts":null,"category":"Symbols","sort_order":162,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR","unified":"23ED","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"23ed.png","sheet_x":0,"sheet_y":21,"short_name":"black_right_pointing_double_triangle_with_vertical_bar","short_names":["black_right_pointing_double_triangle_with_vertical_bar"],"text":null,"texts":null,"category":"Symbols","sort_order":151,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR","unified":"23EE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"23ee.png","sheet_x":0,"sheet_y":22,"short_name":"black_left_pointing_double_triangle_with_vertical_bar","short_names":["black_left_pointing_double_triangle_with_vertical_bar"],"text":null,"texts":null,"category":"Symbols","sort_order":152,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR","unified":"23EF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"23ef.png","sheet_x":0,"sheet_y":23,"short_name":"black_right_pointing_triangle_with_double_vertical_bar","short_names":["black_right_pointing_triangle_with_double_vertical_bar"],"text":null,"texts":null,"category":"Symbols","sort_order":148,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ALARM CLOCK","unified":"23F0","variations":[],"docomo":"E6BA","au":"E594","softbank":"E02D","google":"FE02A","image":"23f0.png","sheet_x":0,"sheet_y":24,"short_name":"alarm_clock","short_names":["alarm_clock"],"text":null,"texts":null,"category":"Objects","sort_order":34,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"STOPWATCH","unified":"23F1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"23f1.png","sheet_x":0,"sheet_y":25,"short_name":"stopwatch","short_names":["stopwatch"],"text":null,"texts":null,"category":"Objects","sort_order":32,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TIMER CLOCK","unified":"23F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"23f2.png","sheet_x":0,"sheet_y":26,"short_name":"timer_clock","short_names":["timer_clock"],"text":null,"texts":null,"category":"Objects","sort_order":33,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HOURGLASS WITH FLOWING SAND","unified":"23F3","variations":[],"docomo":"E71C","au":"E47C","softbank":null,"google":"FE01B","image":"23f3.png","sheet_x":0,"sheet_y":27,"short_name":"hourglass_flowing_sand","short_names":["hourglass_flowing_sand"],"text":null,"texts":null,"category":"Objects","sort_order":36,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DOUBLE VERTICAL BAR","unified":"23F8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"23f8.png","sheet_x":0,"sheet_y":28,"short_name":"double_vertical_bar","short_names":["double_vertical_bar"],"text":null,"texts":null,"category":"Symbols","sort_order":147,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK SQUARE FOR STOP","unified":"23F9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"23f9.png","sheet_x":0,"sheet_y":29,"short_name":"black_square_for_stop","short_names":["black_square_for_stop"],"text":null,"texts":null,"category":"Symbols","sort_order":149,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK CIRCLE FOR RECORD","unified":"23FA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"23fa.png","sheet_x":0,"sheet_y":30,"short_name":"black_circle_for_record","short_names":["black_circle_for_record"],"text":null,"texts":null,"category":"Symbols","sort_order":150,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CIRCLED LATIN CAPITAL LETTER M","unified":"24C2","variations":["24C2-FE0F"],"docomo":"E65C","au":"E5BC","softbank":"E434","google":"FE7E1","image":"24c2.png","sheet_x":0,"sheet_y":31,"short_name":"m","short_names":["m"],"text":null,"texts":null,"category":"Symbols","sort_order":108,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK SMALL SQUARE","unified":"25AA","variations":["25AA-FE0F"],"docomo":null,"au":"E532","softbank":"E21A","google":"FEB6E","image":"25aa.png","sheet_x":0,"sheet_y":32,"short_name":"black_small_square","short_names":["black_small_square"],"text":null,"texts":null,"category":"Symbols","sort_order":216,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHITE SMALL SQUARE","unified":"25AB","variations":["25AB-FE0F"],"docomo":null,"au":"E531","softbank":"E21B","google":"FEB6D","image":"25ab.png","sheet_x":0,"sheet_y":33,"short_name":"white_small_square","short_names":["white_small_square"],"text":null,"texts":null,"category":"Symbols","sort_order":217,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK RIGHT-POINTING TRIANGLE","unified":"25B6","variations":["25B6-FE0F"],"docomo":null,"au":"E52E","softbank":"E23A","google":"FEAFC","image":"25b6.png","sheet_x":0,"sheet_y":34,"short_name":"arrow_forward","short_names":["arrow_forward"],"text":null,"texts":null,"category":"Symbols","sort_order":146,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK LEFT-POINTING TRIANGLE","unified":"25C0","variations":["25C0-FE0F"],"docomo":null,"au":"E52D","softbank":"E23B","google":"FEAFD","image":"25c0.png","sheet_x":0,"sheet_y":35,"short_name":"arrow_backward","short_names":["arrow_backward"],"text":null,"texts":null,"category":"Symbols","sort_order":158,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHITE MEDIUM SQUARE","unified":"25FB","variations":["25FB-FE0F"],"docomo":null,"au":"E538","softbank":"E21B","google":"FEB71","image":"25fb.png","sheet_x":0,"sheet_y":36,"short_name":"white_medium_square","short_names":["white_medium_square"],"text":null,"texts":null,"category":"Symbols","sort_order":222,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK MEDIUM SQUARE","unified":"25FC","variations":["25FC-FE0F"],"docomo":null,"au":"E539","softbank":"E21A","google":"FEB72","image":"25fc.png","sheet_x":0,"sheet_y":37,"short_name":"black_medium_square","short_names":["black_medium_square"],"text":null,"texts":null,"category":"Symbols","sort_order":221,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHITE MEDIUM SMALL SQUARE","unified":"25FD","variations":["25FD-FE0F"],"docomo":null,"au":"E534","softbank":"E21B","google":"FEB6F","image":"25fd.png","sheet_x":0,"sheet_y":38,"short_name":"white_medium_small_square","short_names":["white_medium_small_square"],"text":null,"texts":null,"category":"Symbols","sort_order":224,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK MEDIUM SMALL SQUARE","unified":"25FE","variations":["25FE-FE0F"],"docomo":null,"au":"E535","softbank":"E21A","google":"FEB70","image":"25fe.png","sheet_x":0,"sheet_y":39,"short_name":"black_medium_small_square","short_names":["black_medium_small_square"],"text":null,"texts":null,"category":"Symbols","sort_order":223,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK SUN WITH RAYS","unified":"2600","variations":["2600-FE0F"],"docomo":"E63E","au":"E488","softbank":"E04A","google":"FE000","image":"2600.png","sheet_x":0,"sheet_y":40,"short_name":"sunny","short_names":["sunny"],"text":null,"texts":null,"category":"Nature","sort_order":123,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOUD","unified":"2601","variations":["2601-FE0F"],"docomo":"E63F","au":"E48D","softbank":"E049","google":"FE001","image":"2601.png","sheet_x":1,"sheet_y":0,"short_name":"cloud","short_names":["cloud"],"text":null,"texts":null,"category":"Nature","sort_order":128,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"UMBRELLA","unified":"2602","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2602.png","sheet_x":1,"sheet_y":1,"short_name":"umbrella","short_names":["umbrella"],"text":null,"texts":null,"category":"Nature","sort_order":143,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SNOWMAN","unified":"2603","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2603.png","sheet_x":1,"sheet_y":2,"short_name":"showman","short_names":["showman"],"text":null,"texts":null,"category":"Nature","sort_order":137,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"COMET","unified":"2604","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2604.png","sheet_x":1,"sheet_y":3,"short_name":"comet","short_names":["comet"],"text":null,"texts":null,"category":"Nature","sort_order":122,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK TELEPHONE","unified":"260E","variations":["260E-FE0F"],"docomo":"E687","au":"E596","softbank":"E009","google":"FE523","image":"260e.png","sheet_x":1,"sheet_y":4,"short_name":"phone","short_names":["phone","telephone"],"text":null,"texts":null,"category":"Objects","sort_order":24,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BALLOT BOX WITH CHECK","unified":"2611","variations":["2611-FE0F"],"docomo":null,"au":"EB02","softbank":null,"google":"FEB8B","image":"2611.png","sheet_x":1,"sheet_y":5,"short_name":"ballot_box_with_check","short_names":["ballot_box_with_check"],"text":null,"texts":null,"category":"Symbols","sort_order":205,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"UMBRELLA WITH RAIN DROPS","unified":"2614","variations":["2614-FE0F"],"docomo":"E640","au":"E48C","softbank":"E04B","google":"FE002","image":"2614.png","sheet_x":1,"sheet_y":6,"short_name":"umbrella","short_names":["umbrella"],"text":null,"texts":null,"category":"Nature","sort_order":144,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HOT BEVERAGE","unified":"2615","variations":["2615-FE0F"],"docomo":"E670","au":"E597","softbank":"E045","google":"FE981","image":"2615.png","sheet_x":1,"sheet_y":7,"short_name":"coffee","short_names":["coffee"],"text":null,"texts":null,"category":"Foods","sort_order":64,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SHAMROCK","unified":"2618","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2618.png","sheet_x":1,"sheet_y":8,"short_name":"shamrock","short_names":["shamrock"],"text":null,"texts":null,"category":"Nature","sort_order":81,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHITE UP POINTING INDEX","unified":"261D","variations":["261D-FE0F"],"docomo":null,"au":"E4F6","softbank":"E00F","google":"FEB98","image":"261d.png","sheet_x":1,"sheet_y":9,"short_name":"point_up","short_names":["point_up"],"text":null,"texts":null,"category":"People","sort_order":101,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"261D-1F3FB":{"unified":"261D-1F3FB","image":"261d-1f3fb.png","sheet_x":1,"sheet_y":10,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"261D-1F3FC":{"unified":"261D-1F3FC","image":"261d-1f3fc.png","sheet_x":1,"sheet_y":11,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"261D-1F3FD":{"unified":"261D-1F3FD","image":"261d-1f3fd.png","sheet_x":1,"sheet_y":12,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"261D-1F3FE":{"unified":"261D-1F3FE","image":"261d-1f3fe.png","sheet_x":1,"sheet_y":13,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"261D-1F3FF":{"unified":"261D-1F3FF","image":"261d-1f3ff.png","sheet_x":1,"sheet_y":14,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"SKULL AND CROSSBONES","unified":"2620","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2620.png","sheet_x":1,"sheet_y":15,"short_name":"skull_and_crossbones","short_names":["skull_and_crossbones"],"text":null,"texts":null,"category":"Objects","sort_order":70,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RADIOACTIVE SIGN","unified":"2622","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2622.png","sheet_x":1,"sheet_y":16,"short_name":"radioactive_sign","short_names":["radioactive_sign"],"text":null,"texts":null,"category":"Symbols","sort_order":44,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BIOHAZARD SIGN","unified":"2623","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2623.png","sheet_x":1,"sheet_y":17,"short_name":"biohazard_sign","short_names":["biohazard_sign"],"text":null,"texts":null,"category":"Symbols","sort_order":45,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ORTHODOX CROSS","unified":"2626","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2626.png","sheet_x":1,"sheet_y":18,"short_name":"orthodox_cross","short_names":["orthodox_cross"],"text":null,"texts":null,"category":"Symbols","sort_order":25,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"STAR AND CRESCENT","unified":"262A","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"262a.png","sheet_x":1,"sheet_y":19,"short_name":"star_and_crescent","short_names":["star_and_crescent"],"text":null,"texts":null,"category":"Symbols","sort_order":18,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PEACE SYMBOL","unified":"262E","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"262e.png","sheet_x":1,"sheet_y":20,"short_name":"peace_symbol","short_names":["peace_symbol"],"text":null,"texts":null,"category":"Symbols","sort_order":16,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"YIN YANG","unified":"262F","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"262f.png","sheet_x":1,"sheet_y":21,"short_name":"yin_yang","short_names":["yin_yang"],"text":null,"texts":null,"category":"Symbols","sort_order":24,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHEEL OF DHARMA","unified":"2638","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2638.png","sheet_x":1,"sheet_y":22,"short_name":"wheel_of_dharma","short_names":["wheel_of_dharma"],"text":null,"texts":null,"category":"Symbols","sort_order":20,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHITE FROWNING FACE","unified":"2639","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2639.png","sheet_x":1,"sheet_y":23,"short_name":"white_frowning_face","short_names":["white_frowning_face"],"text":null,"texts":null,"category":"People","sort_order":44,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHITE SMILING FACE","unified":"263A","variations":["263A-FE0F"],"docomo":"E6F0","au":"E4FB","softbank":"E414","google":"FE336","image":"263a.png","sheet_x":1,"sheet_y":24,"short_name":"relaxed","short_names":["relaxed"],"text":null,"texts":null,"category":"People","sort_order":14,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ARIES","unified":"2648","variations":["2648-FE0F"],"docomo":"E646","au":"E48F","softbank":"E23F","google":"FE02B","image":"2648.png","sheet_x":1,"sheet_y":25,"short_name":"aries","short_names":["aries"],"text":null,"texts":null,"category":"Symbols","sort_order":28,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TAURUS","unified":"2649","variations":["2649-FE0F"],"docomo":"E647","au":"E490","softbank":"E240","google":"FE02C","image":"2649.png","sheet_x":1,"sheet_y":26,"short_name":"taurus","short_names":["taurus"],"text":null,"texts":null,"category":"Symbols","sort_order":29,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GEMINI","unified":"264A","variations":["264A-FE0F"],"docomo":"E648","au":"E491","softbank":"E241","google":"FE02D","image":"264a.png","sheet_x":1,"sheet_y":27,"short_name":"gemini","short_names":["gemini"],"text":null,"texts":null,"category":"Symbols","sort_order":30,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CANCER","unified":"264B","variations":["264B-FE0F"],"docomo":"E649","au":"E492","softbank":"E242","google":"FE02E","image":"264b.png","sheet_x":1,"sheet_y":28,"short_name":"cancer","short_names":["cancer"],"text":null,"texts":null,"category":"Symbols","sort_order":31,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LEO","unified":"264C","variations":["264C-FE0F"],"docomo":"E64A","au":"E493","softbank":"E243","google":"FE02F","image":"264c.png","sheet_x":1,"sheet_y":29,"short_name":"leo","short_names":["leo"],"text":null,"texts":null,"category":"Symbols","sort_order":32,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"VIRGO","unified":"264D","variations":["264D-FE0F"],"docomo":"E64B","au":"E494","softbank":"E244","google":"FE030","image":"264d.png","sheet_x":1,"sheet_y":30,"short_name":"virgo","short_names":["virgo"],"text":null,"texts":null,"category":"Symbols","sort_order":33,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LIBRA","unified":"264E","variations":["264E-FE0F"],"docomo":"E64C","au":"E495","softbank":"E245","google":"FE031","image":"264e.png","sheet_x":1,"sheet_y":31,"short_name":"libra","short_names":["libra"],"text":null,"texts":null,"category":"Symbols","sort_order":34,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SCORPIUS","unified":"264F","variations":["264F-FE0F"],"docomo":"E64D","au":"E496","softbank":"E246","google":"FE032","image":"264f.png","sheet_x":1,"sheet_y":32,"short_name":"scorpius","short_names":["scorpius"],"text":null,"texts":null,"category":"Symbols","sort_order":35,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SAGITTARIUS","unified":"2650","variations":["2650-FE0F"],"docomo":"E64E","au":"E497","softbank":"E247","google":"FE033","image":"2650.png","sheet_x":1,"sheet_y":33,"short_name":"sagittarius","short_names":["sagittarius"],"text":null,"texts":null,"category":"Symbols","sort_order":36,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CAPRICORN","unified":"2651","variations":["2651-FE0F"],"docomo":"E64F","au":"E498","softbank":"E248","google":"FE034","image":"2651.png","sheet_x":1,"sheet_y":34,"short_name":"capricorn","short_names":["capricorn"],"text":null,"texts":null,"category":"Symbols","sort_order":37,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"AQUARIUS","unified":"2652","variations":["2652-FE0F"],"docomo":"E650","au":"E499","softbank":"E249","google":"FE035","image":"2652.png","sheet_x":1,"sheet_y":35,"short_name":"aquarius","short_names":["aquarius"],"text":null,"texts":null,"category":"Symbols","sort_order":38,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PISCES","unified":"2653","variations":["2653-FE0F"],"docomo":"E651","au":"E49A","softbank":"E24A","google":"FE036","image":"2653.png","sheet_x":1,"sheet_y":36,"short_name":"pisces","short_names":["pisces"],"text":null,"texts":null,"category":"Symbols","sort_order":39,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK SPADE SUIT","unified":"2660","variations":["2660-FE0F"],"docomo":"E68E","au":"E5A1","softbank":"E20E","google":"FEB1B","image":"2660.png","sheet_x":1,"sheet_y":37,"short_name":"spades","short_names":["spades"],"text":null,"texts":null,"category":"Symbols","sort_order":237,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK CLUB SUIT","unified":"2663","variations":["2663-FE0F"],"docomo":"E690","au":"E5A3","softbank":"E20F","google":"FEB1D","image":"2663.png","sheet_x":1,"sheet_y":38,"short_name":"clubs","short_names":["clubs"],"text":null,"texts":null,"category":"Symbols","sort_order":238,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK HEART SUIT","unified":"2665","variations":["2665-FE0F"],"docomo":"E68D","au":"EAA5","softbank":"E20C","google":"FEB1A","image":"2665.png","sheet_x":1,"sheet_y":39,"short_name":"hearts","short_names":["hearts"],"text":null,"texts":null,"category":"Symbols","sort_order":239,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK DIAMOND SUIT","unified":"2666","variations":["2666-FE0F"],"docomo":"E68F","au":"E5A2","softbank":"E20D","google":"FEB1C","image":"2666.png","sheet_x":1,"sheet_y":40,"short_name":"diamonds","short_names":["diamonds"],"text":null,"texts":null,"category":"Symbols","sort_order":240,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HOT SPRINGS","unified":"2668","variations":["2668-FE0F"],"docomo":"E6F7","au":"E4BC","softbank":"E123","google":"FE7FA","image":"2668.png","sheet_x":2,"sheet_y":0,"short_name":"hotsprings","short_names":["hotsprings"],"text":null,"texts":null,"category":"Symbols","sort_order":75,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK UNIVERSAL RECYCLING SYMBOL","unified":"267B","variations":["267B-FE0F"],"docomo":"E735","au":"EB79","softbank":null,"google":"FEB2C","image":"267b.png","sheet_x":2,"sheet_y":1,"short_name":"recycle","short_names":["recycle"],"text":null,"texts":null,"category":"Symbols","sort_order":97,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHEELCHAIR SYMBOL","unified":"267F","variations":["267F-FE0F"],"docomo":"E69B","au":"E47F","softbank":"E20A","google":"FEB20","image":"267f.png","sheet_x":2,"sheet_y":2,"short_name":"wheelchair","short_names":["wheelchair"],"text":null,"texts":null,"category":"Symbols","sort_order":115,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HAMMER AND PICK","unified":"2692","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2692.png","sheet_x":2,"sheet_y":3,"short_name":"hammer_and_pick","short_names":["hammer_and_pick"],"text":null,"texts":null,"category":"Objects","sort_order":57,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ANCHOR","unified":"2693","variations":["2693-FE0F"],"docomo":"E661","au":"E4A9","softbank":"E202","google":"FE4C1","image":"2693.png","sheet_x":2,"sheet_y":4,"short_name":"anchor","short_names":["anchor"],"text":null,"texts":null,"category":"Places","sort_order":49,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CROSSED SWORDS","unified":"2694","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2694.png","sheet_x":2,"sheet_y":5,"short_name":"crossed_swords","short_names":["crossed_swords"],"text":null,"texts":null,"category":"Objects","sort_order":67,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SCALES","unified":"2696","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2696.png","sheet_x":2,"sheet_y":6,"short_name":"scales","short_names":["scales"],"text":null,"texts":null,"category":"Objects","sort_order":54,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ALEMBIC","unified":"2697","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2697.png","sheet_x":2,"sheet_y":7,"short_name":"alembic","short_names":["alembic"],"text":null,"texts":null,"category":"Objects","sort_order":77,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GEAR","unified":"2699","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2699.png","sheet_x":2,"sheet_y":8,"short_name":"gear","short_names":["gear"],"text":null,"texts":null,"category":"Objects","sort_order":61,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ATOM SYMBOL","unified":"269B","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"269b.png","sheet_x":2,"sheet_y":9,"short_name":"atom_symbol","short_names":["atom_symbol"],"text":null,"texts":null,"category":"Symbols","sort_order":41,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FLEUR-DE-LIS","unified":"269C","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"269c.png","sheet_x":2,"sheet_y":10,"short_name":"fleur_de_lis","short_names":["fleur_de_lis"],"text":null,"texts":null,"category":"Symbols","sort_order":92,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WARNING SIGN","unified":"26A0","variations":["26A0-FE0F"],"docomo":"E737","au":"E481","softbank":"E252","google":"FEB23","image":"26a0.png","sheet_x":2,"sheet_y":11,"short_name":"warning","short_names":["warning"],"text":null,"texts":null,"category":"Symbols","sort_order":94,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HIGH VOLTAGE SIGN","unified":"26A1","variations":["26A1-FE0F"],"docomo":"E642","au":"E487","softbank":"E13D","google":"FE004","image":"26a1.png","sheet_x":2,"sheet_y":12,"short_name":"zap","short_names":["zap"],"text":null,"texts":null,"category":"Nature","sort_order":132,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MEDIUM WHITE CIRCLE","unified":"26AA","variations":["26AA-FE0F"],"docomo":"E69C","au":"E53A","softbank":"E219","google":"FEB65","image":"26aa.png","sheet_x":2,"sheet_y":13,"short_name":"white_circle","short_names":["white_circle"],"text":null,"texts":null,"category":"Symbols","sort_order":207,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MEDIUM BLACK CIRCLE","unified":"26AB","variations":["26AB-FE0F"],"docomo":"E69C","au":"E53B","softbank":"E219","google":"FEB66","image":"26ab.png","sheet_x":2,"sheet_y":14,"short_name":"black_circle","short_names":["black_circle"],"text":null,"texts":null,"category":"Symbols","sort_order":208,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"COFFIN","unified":"26B0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"26b0.png","sheet_x":2,"sheet_y":15,"short_name":"coffin","short_names":["coffin"],"text":null,"texts":null,"category":"Objects","sort_order":71,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FUNERAL URN","unified":"26B1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"26b1.png","sheet_x":2,"sheet_y":16,"short_name":"funeral_urn","short_names":["funeral_urn"],"text":null,"texts":null,"category":"Objects","sort_order":72,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SOCCER BALL","unified":"26BD","variations":["26BD-FE0F"],"docomo":"E656","au":"E4B6","softbank":"E018","google":"FE7D4","image":"26bd.png","sheet_x":2,"sheet_y":17,"short_name":"soccer","short_names":["soccer"],"text":null,"texts":null,"category":"Activity","sort_order":1,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BASEBALL","unified":"26BE","variations":["26BE-FE0F"],"docomo":"E653","au":"E4BA","softbank":"E016","google":"FE7D1","image":"26be.png","sheet_x":2,"sheet_y":18,"short_name":"baseball","short_names":["baseball"],"text":null,"texts":null,"category":"Activity","sort_order":4,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SNOWMAN WITHOUT SNOW","unified":"26C4","variations":["26C4-FE0F"],"docomo":"E641","au":"E485","softbank":"E048","google":"FE003","image":"26c4.png","sheet_x":2,"sheet_y":19,"short_name":"snowman","short_names":["snowman"],"text":null,"texts":null,"category":"Nature","sort_order":138,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SUN BEHIND CLOUD","unified":"26C5","variations":["26C5-FE0F"],"docomo":"E63E-E63F","au":"E48E","softbank":"E04A-E049","google":"FE00F","image":"26c5.png","sheet_x":2,"sheet_y":20,"short_name":"partly_sunny","short_names":["partly_sunny"],"text":null,"texts":null,"category":"Nature","sort_order":125,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"THUNDER CLOUD AND RAIN","unified":"26C8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"26c8.png","sheet_x":2,"sheet_y":21,"short_name":"thunder_cloud_and_rain","short_names":["thunder_cloud_and_rain"],"text":null,"texts":null,"category":"Nature","sort_order":130,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"OPHIUCHUS","unified":"26CE","variations":[],"docomo":null,"au":"E49B","softbank":"E24B","google":"FE037","image":"26ce.png","sheet_x":2,"sheet_y":22,"short_name":"ophiuchus","short_names":["ophiuchus"],"text":null,"texts":null,"category":"Symbols","sort_order":27,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PICK","unified":"26CF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"26cf.png","sheet_x":2,"sheet_y":23,"short_name":"pick","short_names":["pick"],"text":null,"texts":null,"category":"Objects","sort_order":59,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HELMET WITH WHITE CROSS","unified":"26D1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"26d1.png","sheet_x":2,"sheet_y":24,"short_name":"helmet_with_white_cross","short_names":["helmet_with_white_cross"],"text":null,"texts":null,"category":"People","sort_order":193,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CHAINS","unified":"26D3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"26d3.png","sheet_x":2,"sheet_y":25,"short_name":"chains","short_names":["chains"],"text":null,"texts":null,"category":"Objects","sort_order":62,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NO ENTRY","unified":"26D4","variations":["26D4-FE0F"],"docomo":"E72F","au":"E484","softbank":"E137","google":"FEB26","image":"26d4.png","sheet_x":2,"sheet_y":26,"short_name":"no_entry","short_names":["no_entry"],"text":null,"texts":null,"category":"Symbols","sort_order":69,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SHINTO SHRINE","unified":"26E9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"26e9.png","sheet_x":2,"sheet_y":27,"short_name":"shinto_shrine","short_names":["shinto_shrine"],"text":null,"texts":null,"category":"Places","sort_order":115,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CHURCH","unified":"26EA","variations":["26EA-FE0F"],"docomo":null,"au":"E5BB","softbank":"E037","google":"FE4BB","image":"26ea.png","sheet_x":2,"sheet_y":28,"short_name":"church","short_names":["church"],"text":null,"texts":null,"category":"Places","sort_order":111,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MOUNTAIN","unified":"26F0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"26f0.png","sheet_x":2,"sheet_y":29,"short_name":"mountain","short_names":["mountain"],"text":null,"texts":null,"category":"Places","sort_order":66,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"UMBRELLA ON GROUND","unified":"26F1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"26f1.png","sheet_x":2,"sheet_y":30,"short_name":"umbrella_on_ground","short_names":["umbrella_on_ground"],"text":null,"texts":null,"category":"Objects","sort_order":98,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FOUNTAIN","unified":"26F2","variations":["26F2-FE0F"],"docomo":null,"au":"E5CF","softbank":"E121","google":"FE4BC","image":"26f2.png","sheet_x":2,"sheet_y":31,"short_name":"fountain","short_names":["fountain"],"text":null,"texts":null,"category":"Places","sort_order":64,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FLAG IN HOLE","unified":"26F3","variations":["26F3-FE0F"],"docomo":"E654","au":"E599","softbank":"E014","google":"FE7D2","image":"26f3.png","sheet_x":2,"sheet_y":32,"short_name":"golf","short_names":["golf"],"text":null,"texts":null,"category":"Activity","sort_order":9,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FERRY","unified":"26F4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"26f4.png","sheet_x":2,"sheet_y":33,"short_name":"ferry","short_names":["ferry"],"text":null,"texts":null,"category":"Places","sort_order":44,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SAILBOAT","unified":"26F5","variations":["26F5-FE0F"],"docomo":"E6A3","au":"E4B4","softbank":"E01C","google":"FE7EA","image":"26f5.png","sheet_x":2,"sheet_y":34,"short_name":"boat","short_names":["boat","sailboat"],"text":null,"texts":null,"category":"Places","sort_order":41,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SKIER","unified":"26F7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"26f7.png","sheet_x":2,"sheet_y":35,"short_name":"skier","short_names":["skier"],"text":null,"texts":null,"category":"Activity","sort_order":17,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ICE SKATE","unified":"26F8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"26f8.png","sheet_x":2,"sheet_y":36,"short_name":"ice_skate","short_names":["ice_skate"],"text":null,"texts":null,"category":"Activity","sort_order":19,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PERSON WITH BALL","unified":"26F9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"26f9.png","sheet_x":2,"sheet_y":37,"short_name":"person_with_ball","short_names":["person_with_ball"],"text":null,"texts":null,"category":"Activity","sort_order":26,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"26F9-1F3FB":{"unified":"26F9-1F3FB","image":"26f9-1f3fb.png","sheet_x":2,"sheet_y":38,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"26F9-1F3FC":{"unified":"26F9-1F3FC","image":"26f9-1f3fc.png","sheet_x":2,"sheet_y":39,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"26F9-1F3FD":{"unified":"26F9-1F3FD","image":"26f9-1f3fd.png","sheet_x":2,"sheet_y":40,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"26F9-1F3FE":{"unified":"26F9-1F3FE","image":"26f9-1f3fe.png","sheet_x":3,"sheet_y":0,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"26F9-1F3FF":{"unified":"26F9-1F3FF","image":"26f9-1f3ff.png","sheet_x":3,"sheet_y":1,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"TENT","unified":"26FA","variations":["26FA-FE0F"],"docomo":null,"au":"E5D0","softbank":"E122","google":"FE7FB","image":"26fa.png","sheet_x":3,"sheet_y":2,"short_name":"tent","short_names":["tent"],"text":null,"texts":null,"category":"Places","sort_order":72,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FUEL PUMP","unified":"26FD","variations":["26FD-FE0F"],"docomo":"E66B","au":"E571","softbank":"E03A","google":"FE7F5","image":"26fd.png","sheet_x":3,"sheet_y":3,"short_name":"fuelpump","short_names":["fuelpump"],"text":null,"texts":null,"category":"Places","sort_order":51,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK SCISSORS","unified":"2702","variations":["2702-FE0F"],"docomo":"E675","au":"E516","softbank":"E313","google":"FE53E","image":"2702.png","sheet_x":3,"sheet_y":4,"short_name":"scissors","short_names":["scissors"],"text":null,"texts":null,"category":"Objects","sort_order":158,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHITE HEAVY CHECK MARK","unified":"2705","variations":[],"docomo":null,"au":"E55E","softbank":null,"google":"FEB4A","image":"2705.png","sheet_x":3,"sheet_y":5,"short_name":"white_check_mark","short_names":["white_check_mark"],"text":null,"texts":null,"category":"Symbols","sort_order":103,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"AIRPLANE","unified":"2708","variations":["2708-FE0F"],"docomo":"E662","au":"E4B3","softbank":"E01D","google":"FE7E9","image":"2708.png","sheet_x":3,"sheet_y":6,"short_name":"airplane","short_names":["airplane"],"text":null,"texts":null,"category":"Places","sort_order":38,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ENVELOPE","unified":"2709","variations":["2709-FE0F"],"docomo":"E6D3","au":"E521","softbank":"E103","google":"FE529","image":"2709.png","sheet_x":3,"sheet_y":7,"short_name":"email","short_names":["email","envelope"],"text":null,"texts":null,"category":"Objects","sort_order":111,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RAISED FIST","unified":"270A","variations":[],"docomo":"E693","au":"EB83","softbank":"E010","google":"FEB93","image":"270a.png","sheet_x":3,"sheet_y":8,"short_name":"fist","short_names":["fist"],"text":null,"texts":null,"category":"People","sort_order":94,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"270A-1F3FB":{"unified":"270A-1F3FB","image":"270a-1f3fb.png","sheet_x":3,"sheet_y":9,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"270A-1F3FC":{"unified":"270A-1F3FC","image":"270a-1f3fc.png","sheet_x":3,"sheet_y":10,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"270A-1F3FD":{"unified":"270A-1F3FD","image":"270a-1f3fd.png","sheet_x":3,"sheet_y":11,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"270A-1F3FE":{"unified":"270A-1F3FE","image":"270a-1f3fe.png","sheet_x":3,"sheet_y":12,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"270A-1F3FF":{"unified":"270A-1F3FF","image":"270a-1f3ff.png","sheet_x":3,"sheet_y":13,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"RAISED HAND","unified":"270B","variations":[],"docomo":"E695","au":"E5A7","softbank":"E012","google":"FEB95","image":"270b.png","sheet_x":3,"sheet_y":14,"short_name":"hand","short_names":["hand","raised_hand"],"text":null,"texts":null,"category":"People","sort_order":97,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"270B-1F3FB":{"unified":"270B-1F3FB","image":"270b-1f3fb.png","sheet_x":3,"sheet_y":15,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"270B-1F3FC":{"unified":"270B-1F3FC","image":"270b-1f3fc.png","sheet_x":3,"sheet_y":16,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"270B-1F3FD":{"unified":"270B-1F3FD","image":"270b-1f3fd.png","sheet_x":3,"sheet_y":17,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"270B-1F3FE":{"unified":"270B-1F3FE","image":"270b-1f3fe.png","sheet_x":3,"sheet_y":18,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"270B-1F3FF":{"unified":"270B-1F3FF","image":"270b-1f3ff.png","sheet_x":3,"sheet_y":19,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"VICTORY HAND","unified":"270C","variations":["270C-FE0F"],"docomo":"E694","au":"E5A6","softbank":"E011","google":"FEB94","image":"270c.png","sheet_x":3,"sheet_y":20,"short_name":"v","short_names":["v"],"text":null,"texts":null,"category":"People","sort_order":95,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"270C-1F3FB":{"unified":"270C-1F3FB","image":"270c-1f3fb.png","sheet_x":3,"sheet_y":21,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"270C-1F3FC":{"unified":"270C-1F3FC","image":"270c-1f3fc.png","sheet_x":3,"sheet_y":22,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"270C-1F3FD":{"unified":"270C-1F3FD","image":"270c-1f3fd.png","sheet_x":3,"sheet_y":23,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"270C-1F3FE":{"unified":"270C-1F3FE","image":"270c-1f3fe.png","sheet_x":3,"sheet_y":24,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"270C-1F3FF":{"unified":"270C-1F3FF","image":"270c-1f3ff.png","sheet_x":3,"sheet_y":25,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"WRITING HAND","unified":"270D","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"270d.png","sheet_x":3,"sheet_y":26,"short_name":"writing_hand","short_names":["writing_hand"],"text":null,"texts":null,"category":"People","sort_order":110,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"270D-1F3FB":{"unified":"270D-1F3FB","image":"270d-1f3fb.png","sheet_x":3,"sheet_y":27,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"270D-1F3FC":{"unified":"270D-1F3FC","image":"270d-1f3fc.png","sheet_x":3,"sheet_y":28,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"270D-1F3FD":{"unified":"270D-1F3FD","image":"270d-1f3fd.png","sheet_x":3,"sheet_y":29,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"270D-1F3FE":{"unified":"270D-1F3FE","image":"270d-1f3fe.png","sheet_x":3,"sheet_y":30,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"270D-1F3FF":{"unified":"270D-1F3FF","image":"270d-1f3ff.png","sheet_x":3,"sheet_y":31,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"PENCIL","unified":"270F","variations":["270F-FE0F"],"docomo":"E719","au":"E4A1","softbank":"E301","google":"FE539","image":"270f.png","sheet_x":3,"sheet_y":32,"short_name":"pencil2","short_names":["pencil2"],"text":null,"texts":null,"category":"Objects","sort_order":174,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK NIB","unified":"2712","variations":["2712-FE0F"],"docomo":"E6AE","au":"EB03","softbank":null,"google":"FE536","image":"2712.png","sheet_x":3,"sheet_y":33,"short_name":"black_nib","short_names":["black_nib"],"text":null,"texts":null,"category":"Objects","sort_order":172,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HEAVY CHECK MARK","unified":"2714","variations":["2714-FE0F"],"docomo":null,"au":"E557","softbank":null,"google":"FEB49","image":"2714.png","sheet_x":3,"sheet_y":34,"short_name":"heavy_check_mark","short_names":["heavy_check_mark"],"text":null,"texts":null,"category":"Symbols","sort_order":189,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HEAVY MULTIPLICATION X","unified":"2716","variations":["2716-FE0F"],"docomo":null,"au":"E54F","softbank":"E333","google":"FEB53","image":"2716.png","sheet_x":3,"sheet_y":35,"short_name":"heavy_multiplication_x","short_names":["heavy_multiplication_x"],"text":null,"texts":null,"category":"Symbols","sort_order":194,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LATIN CROSS","unified":"271D","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"271d.png","sheet_x":3,"sheet_y":36,"short_name":"latin_cross","short_names":["latin_cross"],"text":null,"texts":null,"category":"Symbols","sort_order":17,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"STAR OF DAVID","unified":"2721","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2721.png","sheet_x":3,"sheet_y":37,"short_name":"star_of_david","short_names":["star_of_david"],"text":null,"texts":null,"category":"Symbols","sort_order":21,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPARKLES","unified":"2728","variations":[],"docomo":"E6FA","au":"EAAB","softbank":"E32E","google":"FEB60","image":"2728.png","sheet_x":3,"sheet_y":38,"short_name":"sparkles","short_names":["sparkles"],"text":null,"texts":null,"category":"Nature","sort_order":121,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"EIGHT SPOKED ASTERISK","unified":"2733","variations":["2733-FE0F"],"docomo":"E6F8","au":"E53E","softbank":"E206","google":"FEB62","image":"2733.png","sheet_x":3,"sheet_y":39,"short_name":"eight_spoked_asterisk","short_names":["eight_spoked_asterisk"],"text":null,"texts":null,"category":"Symbols","sort_order":101,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"EIGHT POINTED BLACK STAR","unified":"2734","variations":["2734-FE0F"],"docomo":"E6F8","au":"E479","softbank":"E205","google":"FEB61","image":"2734.png","sheet_x":3,"sheet_y":40,"short_name":"eight_pointed_black_star","short_names":["eight_pointed_black_star"],"text":null,"texts":null,"category":"Symbols","sort_order":53,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SNOWFLAKE","unified":"2744","variations":["2744-FE0F"],"docomo":null,"au":"E48A","softbank":null,"google":"FE00E","image":"2744.png","sheet_x":4,"sheet_y":0,"short_name":"snowflake","short_names":["snowflake"],"text":null,"texts":null,"category":"Nature","sort_order":135,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPARKLE","unified":"2747","variations":["2747-FE0F"],"docomo":"E6FA","au":"E46C","softbank":"E32E","google":"FEB77","image":"2747.png","sheet_x":4,"sheet_y":1,"short_name":"sparkle","short_names":["sparkle"],"text":null,"texts":null,"category":"Symbols","sort_order":100,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CROSS MARK","unified":"274C","variations":[],"docomo":null,"au":"E550","softbank":"E333","google":"FEB45","image":"274c.png","sheet_x":4,"sheet_y":2,"short_name":"x","short_names":["x"],"text":null,"texts":null,"category":"Symbols","sort_order":72,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NEGATIVE SQUARED CROSS MARK","unified":"274E","variations":[],"docomo":null,"au":"E551","softbank":"E333","google":"FEB46","image":"274e.png","sheet_x":4,"sheet_y":3,"short_name":"negative_squared_cross_mark","short_names":["negative_squared_cross_mark"],"text":null,"texts":null,"category":"Symbols","sort_order":102,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK QUESTION MARK ORNAMENT","unified":"2753","variations":[],"docomo":null,"au":"E483","softbank":"E020","google":"FEB09","image":"2753.png","sheet_x":4,"sheet_y":4,"short_name":"question","short_names":["question"],"text":null,"texts":null,"category":"Symbols","sort_order":84,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHITE QUESTION MARK ORNAMENT","unified":"2754","variations":[],"docomo":null,"au":"E483","softbank":"E336","google":"FEB0A","image":"2754.png","sheet_x":4,"sheet_y":5,"short_name":"grey_question","short_names":["grey_question"],"text":null,"texts":null,"category":"Symbols","sort_order":85,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHITE EXCLAMATION MARK ORNAMENT","unified":"2755","variations":[],"docomo":"E702","au":"E482","softbank":"E337","google":"FEB0B","image":"2755.png","sheet_x":4,"sheet_y":6,"short_name":"grey_exclamation","short_names":["grey_exclamation"],"text":null,"texts":null,"category":"Symbols","sort_order":83,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HEAVY EXCLAMATION MARK SYMBOL","unified":"2757","variations":["2757-FE0F"],"docomo":"E702","au":"E482","softbank":"E021","google":"FEB04","image":"2757.png","sheet_x":4,"sheet_y":7,"short_name":"exclamation","short_names":["exclamation","heavy_exclamation_mark"],"text":null,"texts":null,"category":"Symbols","sort_order":82,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HEAVY HEART EXCLAMATION MARK ORNAMENT","unified":"2763","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"2763.png","sheet_x":4,"sheet_y":8,"short_name":"heavy_heart_exclamation_mark_ornament","short_names":["heavy_heart_exclamation_mark_ornament"],"text":null,"texts":null,"category":"Symbols","sort_order":7,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HEAVY BLACK HEART","unified":"2764","variations":["2764-FE0F"],"docomo":"E6EC","au":"E595","softbank":"E022","google":"FEB0C","image":"2764.png","sheet_x":4,"sheet_y":9,"short_name":"heart","short_names":["heart"],"text":"<3","texts":["<3"],"category":"Symbols","sort_order":1,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HEAVY PLUS SIGN","unified":"2795","variations":[],"docomo":null,"au":"E53C","softbank":null,"google":"FEB51","image":"2795.png","sheet_x":4,"sheet_y":10,"short_name":"heavy_plus_sign","short_names":["heavy_plus_sign"],"text":null,"texts":null,"category":"Symbols","sort_order":191,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HEAVY MINUS SIGN","unified":"2796","variations":[],"docomo":null,"au":"E53D","softbank":null,"google":"FEB52","image":"2796.png","sheet_x":4,"sheet_y":11,"short_name":"heavy_minus_sign","short_names":["heavy_minus_sign"],"text":null,"texts":null,"category":"Symbols","sort_order":192,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HEAVY DIVISION SIGN","unified":"2797","variations":[],"docomo":null,"au":"E554","softbank":null,"google":"FEB54","image":"2797.png","sheet_x":4,"sheet_y":12,"short_name":"heavy_division_sign","short_names":["heavy_division_sign"],"text":null,"texts":null,"category":"Symbols","sort_order":193,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK RIGHTWARDS ARROW","unified":"27A1","variations":["27A1-FE0F"],"docomo":null,"au":"E552","softbank":"E234","google":"FEAFA","image":"27a1.png","sheet_x":4,"sheet_y":13,"short_name":"arrow_right","short_names":["arrow_right"],"text":null,"texts":null,"category":"Symbols","sort_order":163,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CURLY LOOP","unified":"27B0","variations":[],"docomo":"E70A","au":"EB31","softbank":null,"google":"FEB08","image":"27b0.png","sheet_x":4,"sheet_y":14,"short_name":"curly_loop","short_names":["curly_loop"],"text":null,"texts":null,"category":"Symbols","sort_order":188,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DOUBLE CURLY LOOP","unified":"27BF","variations":[],"docomo":"E6DF","au":null,"softbank":"E211","google":"FE82B","image":"27bf.png","sheet_x":4,"sheet_y":15,"short_name":"loop","short_names":["loop"],"text":null,"texts":null,"category":"Symbols","sort_order":106,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ARROW POINTING RIGHTWARDS THEN CURVING UPWARDS","unified":"2934","variations":["2934-FE0F"],"docomo":"E6F5","au":"EB2D","softbank":"E236","google":"FEAF4","image":"2934.png","sheet_x":4,"sheet_y":16,"short_name":"arrow_heading_up","short_names":["arrow_heading_up"],"text":null,"texts":null,"category":"Symbols","sort_order":176,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS","unified":"2935","variations":["2935-FE0F"],"docomo":"E700","au":"EB2E","softbank":"E238","google":"FEAF5","image":"2935.png","sheet_x":4,"sheet_y":17,"short_name":"arrow_heading_down","short_names":["arrow_heading_down"],"text":null,"texts":null,"category":"Symbols","sort_order":177,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LEFTWARDS BLACK ARROW","unified":"2B05","variations":["2B05-FE0F"],"docomo":null,"au":"E553","softbank":"E235","google":"FEAFB","image":"2b05.png","sheet_x":4,"sheet_y":18,"short_name":"arrow_left","short_names":["arrow_left"],"text":null,"texts":null,"category":"Symbols","sort_order":164,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"UPWARDS BLACK ARROW","unified":"2B06","variations":["2B06-FE0F"],"docomo":null,"au":"E53F","softbank":"E232","google":"FEAF8","image":"2b06.png","sheet_x":4,"sheet_y":19,"short_name":"arrow_up","short_names":["arrow_up"],"text":null,"texts":null,"category":"Symbols","sort_order":165,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DOWNWARDS BLACK ARROW","unified":"2B07","variations":["2B07-FE0F"],"docomo":null,"au":"E540","softbank":"E233","google":"FEAF9","image":"2b07.png","sheet_x":4,"sheet_y":20,"short_name":"arrow_down","short_names":["arrow_down"],"text":null,"texts":null,"category":"Symbols","sort_order":166,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK LARGE SQUARE","unified":"2B1B","variations":["2B1B-FE0F"],"docomo":null,"au":"E549","softbank":"E21A","google":"FEB6C","image":"2b1b.png","sheet_x":4,"sheet_y":21,"short_name":"black_large_square","short_names":["black_large_square"],"text":null,"texts":null,"category":"Symbols","sort_order":218,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHITE LARGE SQUARE","unified":"2B1C","variations":["2B1C-FE0F"],"docomo":null,"au":"E548","softbank":"E21B","google":"FEB6B","image":"2b1c.png","sheet_x":4,"sheet_y":22,"short_name":"white_large_square","short_names":["white_large_square"],"text":null,"texts":null,"category":"Symbols","sort_order":219,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHITE MEDIUM STAR","unified":"2B50","variations":["2B50-FE0F"],"docomo":null,"au":"E48B","softbank":"E32F","google":"FEB68","image":"2b50.png","sheet_x":4,"sheet_y":23,"short_name":"star","short_names":["star"],"text":null,"texts":null,"category":"Nature","sort_order":118,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HEAVY LARGE CIRCLE","unified":"2B55","variations":["2B55-FE0F"],"docomo":"E6A0","au":"EAAD","softbank":"E332","google":"FEB44","image":"2b55.png","sheet_x":4,"sheet_y":24,"short_name":"o","short_names":["o"],"text":null,"texts":null,"category":"Symbols","sort_order":73,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WAVY DASH","unified":"3030","variations":["3030-FE0F"],"docomo":"E709","au":null,"softbank":null,"google":"FEB07","image":"3030.png","sheet_x":4,"sheet_y":25,"short_name":"wavy_dash","short_names":["wavy_dash"],"text":null,"texts":null,"category":"Symbols","sort_order":187,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PART ALTERNATION MARK","unified":"303D","variations":["303D-FE0F"],"docomo":null,"au":null,"softbank":"E12C","google":"FE81B","image":"303d.png","sheet_x":4,"sheet_y":26,"short_name":"part_alternation_mark","short_names":["part_alternation_mark"],"text":null,"texts":null,"category":"Symbols","sort_order":93,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CIRCLED IDEOGRAPH CONGRATULATION","unified":"3297","variations":["3297-FE0F"],"docomo":null,"au":"EA99","softbank":"E30D","google":"FEB43","image":"3297.png","sheet_x":4,"sheet_y":27,"short_name":"congratulations","short_names":["congratulations"],"text":null,"texts":null,"category":"Symbols","sort_order":59,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CIRCLED IDEOGRAPH SECRET","unified":"3299","variations":["3299-FE0F"],"docomo":"E734","au":"E4F1","softbank":"E315","google":"FEB2B","image":"3299.png","sheet_x":4,"sheet_y":28,"short_name":"secret","short_names":["secret"],"text":null,"texts":null,"category":"Symbols","sort_order":58,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MAHJONG TILE RED DRAGON","unified":"1F004","variations":["1F004-FE0F"],"docomo":null,"au":"E5D1","softbank":"E12D","google":"FE80B","image":"1f004.png","sheet_x":4,"sheet_y":29,"short_name":"mahjong","short_names":["mahjong"],"text":null,"texts":null,"category":"Symbols","sort_order":236,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PLAYING CARD BLACK JOKER","unified":"1F0CF","variations":[],"docomo":null,"au":"EB6F","softbank":null,"google":"FE812","image":"1f0cf.png","sheet_x":4,"sheet_y":30,"short_name":"black_joker","short_names":["black_joker"],"text":null,"texts":null,"category":"Symbols","sort_order":235,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NEGATIVE SQUARED LATIN CAPITAL LETTER A","unified":"1F170","variations":["1F170-FE0F"],"docomo":null,"au":"EB26","softbank":"E532","google":"FE50B","image":"1f170.png","sheet_x":4,"sheet_y":31,"short_name":"a","short_names":["a"],"text":null,"texts":null,"category":"Symbols","sort_order":63,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NEGATIVE SQUARED LATIN CAPITAL LETTER B","unified":"1F171","variations":["1F171-FE0F"],"docomo":null,"au":"EB27","softbank":"E533","google":"FE50C","image":"1f171.png","sheet_x":4,"sheet_y":32,"short_name":"b","short_names":["b"],"text":null,"texts":null,"category":"Symbols","sort_order":64,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NEGATIVE SQUARED LATIN CAPITAL LETTER O","unified":"1F17E","variations":["1F17E-FE0F"],"docomo":null,"au":"EB28","softbank":"E535","google":"FE50E","image":"1f17e.png","sheet_x":4,"sheet_y":33,"short_name":"o2","short_names":["o2"],"text":null,"texts":null,"category":"Symbols","sort_order":67,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NEGATIVE SQUARED LATIN CAPITAL LETTER P","unified":"1F17F","variations":["1F17F-FE0F"],"docomo":"E66C","au":"E4A6","softbank":"E14F","google":"FE7F6","image":"1f17f.png","sheet_x":4,"sheet_y":34,"short_name":"parking","short_names":["parking"],"text":null,"texts":null,"category":"Symbols","sort_order":118,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NEGATIVE SQUARED AB","unified":"1F18E","variations":[],"docomo":null,"au":"EB29","softbank":"E534","google":"FE50D","image":"1f18e.png","sheet_x":4,"sheet_y":35,"short_name":"ab","short_names":["ab"],"text":null,"texts":null,"category":"Symbols","sort_order":65,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED CL","unified":"1F191","variations":[],"docomo":"E6DB","au":"E5AB","softbank":null,"google":"FEB84","image":"1f191.png","sheet_x":4,"sheet_y":36,"short_name":"cl","short_names":["cl"],"text":null,"texts":null,"category":"Symbols","sort_order":66,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED COOL","unified":"1F192","variations":[],"docomo":null,"au":"EA85","softbank":"E214","google":"FEB38","image":"1f192.png","sheet_x":4,"sheet_y":37,"short_name":"cool","short_names":["cool"],"text":null,"texts":null,"category":"Symbols","sort_order":131,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED FREE","unified":"1F193","variations":[],"docomo":"E6D7","au":"E578","softbank":null,"google":"FEB21","image":"1f193.png","sheet_x":4,"sheet_y":38,"short_name":"free","short_names":["free"],"text":null,"texts":null,"category":"Symbols","sort_order":133,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED ID","unified":"1F194","variations":[],"docomo":"E6D8","au":"EA88","softbank":"E229","google":"FEB81","image":"1f194.png","sheet_x":4,"sheet_y":39,"short_name":"id","short_names":["id"],"text":null,"texts":null,"category":"Symbols","sort_order":40,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED NEW","unified":"1F195","variations":[],"docomo":"E6DD","au":"E5B5","softbank":"E212","google":"FEB36","image":"1f195.png","sheet_x":4,"sheet_y":40,"short_name":"new","short_names":["new"],"text":null,"texts":null,"category":"Symbols","sort_order":132,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED NG","unified":"1F196","variations":[],"docomo":"E72F","au":null,"softbank":null,"google":"FEB28","image":"1f196.png","sheet_x":5,"sheet_y":0,"short_name":"ng","short_names":["ng"],"text":null,"texts":null,"category":"Symbols","sort_order":128,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED OK","unified":"1F197","variations":[],"docomo":"E70B","au":"E5AD","softbank":"E24D","google":"FEB27","image":"1f197.png","sheet_x":5,"sheet_y":1,"short_name":"ok","short_names":["ok"],"text":null,"texts":null,"category":"Symbols","sort_order":129,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED SOS","unified":"1F198","variations":[],"docomo":null,"au":"E4E8","softbank":null,"google":"FEB4F","image":"1f198.png","sheet_x":5,"sheet_y":2,"short_name":"sos","short_names":["sos"],"text":null,"texts":null,"category":"Symbols","sort_order":68,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED UP WITH EXCLAMATION MARK","unified":"1F199","variations":[],"docomo":null,"au":"E50F","softbank":"E213","google":"FEB37","image":"1f199.png","sheet_x":5,"sheet_y":3,"short_name":"up","short_names":["up"],"text":null,"texts":null,"category":"Symbols","sort_order":130,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED VS","unified":"1F19A","variations":[],"docomo":null,"au":"E5D2","softbank":"E12E","google":"FEB32","image":"1f19a.png","sheet_x":5,"sheet_y":4,"short_name":"vs","short_names":["vs"],"text":null,"texts":null,"category":"Symbols","sort_order":54,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED KATAKANA KOKO","unified":"1F201","variations":[],"docomo":null,"au":null,"softbank":"E203","google":"FEB24","image":"1f201.png","sheet_x":5,"sheet_y":5,"short_name":"koko","short_names":["koko"],"text":null,"texts":null,"category":"Symbols","sort_order":127,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED KATAKANA SA","unified":"1F202","variations":["1F202-FE0F"],"docomo":null,"au":"EA87","softbank":"E228","google":"FEB3F","image":"1f202.png","sheet_x":5,"sheet_y":6,"short_name":"sa","short_names":["sa"],"text":null,"texts":null,"category":"Symbols","sort_order":110,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-7121","unified":"1F21A","variations":["1F21A-FE0F"],"docomo":null,"au":null,"softbank":"E216","google":"FEB3A","image":"1f21a.png","sheet_x":5,"sheet_y":7,"short_name":"u7121","short_names":["u7121"],"text":null,"texts":null,"category":"Symbols","sort_order":49,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-6307","unified":"1F22F","variations":["1F22F-FE0F"],"docomo":null,"au":"EA8B","softbank":"E22C","google":"FEB40","image":"1f22f.png","sheet_x":5,"sheet_y":8,"short_name":"u6307","short_names":["u6307"],"text":null,"texts":null,"category":"Symbols","sort_order":98,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-7981","unified":"1F232","variations":[],"docomo":"E738","au":null,"softbank":null,"google":"FEB2E","image":"1f232.png","sheet_x":5,"sheet_y":9,"short_name":"u7981","short_names":["u7981"],"text":null,"texts":null,"category":"Symbols","sort_order":62,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-7A7A","unified":"1F233","variations":[],"docomo":"E739","au":"EA8A","softbank":"E22B","google":"FEB2F","image":"1f233.png","sheet_x":5,"sheet_y":10,"short_name":"u7a7a","short_names":["u7a7a"],"text":null,"texts":null,"category":"Symbols","sort_order":42,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-5408","unified":"1F234","variations":[],"docomo":"E73A","au":null,"softbank":null,"google":"FEB30","image":"1f234.png","sheet_x":5,"sheet_y":11,"short_name":"u5408","short_names":["u5408"],"text":null,"texts":null,"category":"Symbols","sort_order":60,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-6E80","unified":"1F235","variations":[],"docomo":"E73B","au":"EA89","softbank":"E22A","google":"FEB31","image":"1f235.png","sheet_x":5,"sheet_y":12,"short_name":"u6e80","short_names":["u6e80"],"text":null,"texts":null,"category":"Symbols","sort_order":61,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-6709","unified":"1F236","variations":[],"docomo":null,"au":null,"softbank":"E215","google":"FEB39","image":"1f236.png","sheet_x":5,"sheet_y":13,"short_name":"u6709","short_names":["u6709"],"text":null,"texts":null,"category":"Symbols","sort_order":48,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-6708","unified":"1F237","variations":["1F237-FE0F"],"docomo":null,"au":null,"softbank":"E217","google":"FEB3B","image":"1f237.png","sheet_x":5,"sheet_y":14,"short_name":"u6708","short_names":["u6708"],"text":null,"texts":null,"category":"Symbols","sort_order":52,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-7533","unified":"1F238","variations":[],"docomo":null,"au":null,"softbank":"E218","google":"FEB3C","image":"1f238.png","sheet_x":5,"sheet_y":15,"short_name":"u7533","short_names":["u7533"],"text":null,"texts":null,"category":"Symbols","sort_order":50,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-5272","unified":"1F239","variations":[],"docomo":null,"au":"EA86","softbank":"E227","google":"FEB3E","image":"1f239.png","sheet_x":5,"sheet_y":16,"short_name":"u5272","short_names":["u5272"],"text":null,"texts":null,"category":"Symbols","sort_order":43,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SQUARED CJK UNIFIED IDEOGRAPH-55B6","unified":"1F23A","variations":[],"docomo":null,"au":"EA8C","softbank":"E22D","google":"FEB41","image":"1f23a.png","sheet_x":5,"sheet_y":17,"short_name":"u55b6","short_names":["u55b6"],"text":null,"texts":null,"category":"Symbols","sort_order":51,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CIRCLED IDEOGRAPH ADVANTAGE","unified":"1F250","variations":[],"docomo":null,"au":"E4F7","softbank":"E226","google":"FEB3D","image":"1f250.png","sheet_x":5,"sheet_y":18,"short_name":"ideograph_advantage","short_names":["ideograph_advantage"],"text":null,"texts":null,"category":"Symbols","sort_order":57,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CIRCLED IDEOGRAPH ACCEPT","unified":"1F251","variations":[],"docomo":null,"au":"EB01","softbank":null,"google":"FEB50","image":"1f251.png","sheet_x":5,"sheet_y":19,"short_name":"accept","short_names":["accept"],"text":null,"texts":null,"category":"Symbols","sort_order":55,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CYCLONE","unified":"1F300","variations":[],"docomo":"E643","au":"E469","softbank":"E443","google":"FE005","image":"1f300.png","sheet_x":5,"sheet_y":20,"short_name":"cyclone","short_names":["cyclone"],"text":null,"texts":null,"category":"Symbols","sort_order":105,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FOGGY","unified":"1F301","variations":[],"docomo":"E644","au":"E598","softbank":null,"google":"FE006","image":"1f301.png","sheet_x":5,"sheet_y":21,"short_name":"foggy","short_names":["foggy"],"text":null,"texts":null,"category":"Places","sort_order":61,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOSED UMBRELLA","unified":"1F302","variations":[],"docomo":"E645","au":"EAE8","softbank":"E43C","google":"FE007","image":"1f302.png","sheet_x":5,"sheet_y":22,"short_name":"closed_umbrella","short_names":["closed_umbrella"],"text":null,"texts":null,"category":"People","sort_order":204,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NIGHT WITH STARS","unified":"1F303","variations":[],"docomo":"E6B3","au":"EAF1","softbank":"E44B","google":"FE008","image":"1f303.png","sheet_x":5,"sheet_y":23,"short_name":"night_with_stars","short_names":["night_with_stars"],"text":null,"texts":null,"category":"Places","sort_order":84,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SUNRISE OVER MOUNTAINS","unified":"1F304","variations":[],"docomo":"E63E","au":"EAF4","softbank":"E04D","google":"FE009","image":"1f304.png","sheet_x":5,"sheet_y":24,"short_name":"sunrise_over_mountains","short_names":["sunrise_over_mountains"],"text":null,"texts":null,"category":"Places","sort_order":77,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SUNRISE","unified":"1F305","variations":[],"docomo":"E63E","au":"EAF4","softbank":"E449","google":"FE00A","image":"1f305.png","sheet_x":5,"sheet_y":25,"short_name":"sunrise","short_names":["sunrise"],"text":null,"texts":null,"category":"Places","sort_order":76,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CITYSCAPE AT DUSK","unified":"1F306","variations":[],"docomo":null,"au":"E5DA","softbank":"E146","google":"FE00B","image":"1f306.png","sheet_x":5,"sheet_y":26,"short_name":"city_sunset","short_names":["city_sunset"],"text":null,"texts":null,"category":"Places","sort_order":82,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SUNSET OVER BUILDINGS","unified":"1F307","variations":[],"docomo":"E63E","au":"E5DA","softbank":"E44A","google":"FE00C","image":"1f307.png","sheet_x":5,"sheet_y":27,"short_name":"city_sunrise","short_names":["city_sunrise"],"text":null,"texts":null,"category":"Places","sort_order":81,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RAINBOW","unified":"1F308","variations":[],"docomo":null,"au":"EAF2","softbank":"E44C","google":"FE00D","image":"1f308.png","sheet_x":5,"sheet_y":28,"short_name":"rainbow","short_names":["rainbow"],"text":null,"texts":null,"category":"Places","sort_order":90,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BRIDGE AT NIGHT","unified":"1F309","variations":[],"docomo":"E6B3","au":"E4BF","softbank":"E44B","google":"FE010","image":"1f309.png","sheet_x":5,"sheet_y":29,"short_name":"bridge_at_night","short_names":["bridge_at_night"],"text":null,"texts":null,"category":"Places","sort_order":85,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WATER WAVE","unified":"1F30A","variations":[],"docomo":"E73F","au":"EB7C","softbank":"E43E","google":"FE038","image":"1f30a.png","sheet_x":5,"sheet_y":30,"short_name":"ocean","short_names":["ocean"],"text":null,"texts":null,"category":"Nature","sort_order":147,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"VOLCANO","unified":"1F30B","variations":[],"docomo":null,"au":"EB53","softbank":null,"google":"FE03A","image":"1f30b.png","sheet_x":5,"sheet_y":31,"short_name":"volcano","short_names":["volcano"],"text":null,"texts":null,"category":"Places","sort_order":69,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MILKY WAY","unified":"1F30C","variations":[],"docomo":"E6B3","au":"EB5F","softbank":"E44B","google":"FE03B","image":"1f30c.png","sheet_x":5,"sheet_y":32,"short_name":"milky_way","short_names":["milky_way"],"text":null,"texts":null,"category":"Places","sort_order":86,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"EARTH GLOBE EUROPE-AFRICA","unified":"1F30D","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f30d.png","sheet_x":5,"sheet_y":33,"short_name":"earth_africa","short_names":["earth_africa"],"text":null,"texts":null,"category":"Nature","sort_order":102,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"EARTH GLOBE AMERICAS","unified":"1F30E","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f30e.png","sheet_x":5,"sheet_y":34,"short_name":"earth_americas","short_names":["earth_americas"],"text":null,"texts":null,"category":"Nature","sort_order":101,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"EARTH GLOBE ASIA-AUSTRALIA","unified":"1F30F","variations":[],"docomo":null,"au":"E5B3","softbank":null,"google":"FE039","image":"1f30f.png","sheet_x":5,"sheet_y":35,"short_name":"earth_asia","short_names":["earth_asia"],"text":null,"texts":null,"category":"Nature","sort_order":103,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GLOBE WITH MERIDIANS","unified":"1F310","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f310.png","sheet_x":5,"sheet_y":36,"short_name":"globe_with_meridians","short_names":["globe_with_meridians"],"text":null,"texts":null,"category":"Symbols","sort_order":107,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NEW MOON SYMBOL","unified":"1F311","variations":[],"docomo":"E69C","au":"E5A8","softbank":null,"google":"FE011","image":"1f311.png","sheet_x":5,"sheet_y":37,"short_name":"new_moon","short_names":["new_moon"],"text":null,"texts":null,"category":"Nature","sort_order":108,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WAXING CRESCENT MOON SYMBOL","unified":"1F312","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f312.png","sheet_x":5,"sheet_y":38,"short_name":"waxing_crescent_moon","short_names":["waxing_crescent_moon"],"text":null,"texts":null,"category":"Nature","sort_order":109,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FIRST QUARTER MOON SYMBOL","unified":"1F313","variations":[],"docomo":"E69E","au":"E5AA","softbank":"E04C","google":"FE013","image":"1f313.png","sheet_x":5,"sheet_y":39,"short_name":"first_quarter_moon","short_names":["first_quarter_moon"],"text":null,"texts":null,"category":"Nature","sort_order":110,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WAXING GIBBOUS MOON SYMBOL","unified":"1F314","variations":[],"docomo":"E69D","au":"E5A9","softbank":"E04C","google":"FE012","image":"1f314.png","sheet_x":5,"sheet_y":40,"short_name":"moon","short_names":["moon","waxing_gibbous_moon"],"text":null,"texts":null,"category":"Nature","sort_order":111,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FULL MOON SYMBOL","unified":"1F315","variations":[],"docomo":"E6A0","au":null,"softbank":null,"google":"FE015","image":"1f315.png","sheet_x":6,"sheet_y":0,"short_name":"full_moon","short_names":["full_moon"],"text":null,"texts":null,"category":"Nature","sort_order":104,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WANING GIBBOUS MOON SYMBOL","unified":"1F316","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f316.png","sheet_x":6,"sheet_y":1,"short_name":"waning_gibbous_moon","short_names":["waning_gibbous_moon"],"text":null,"texts":null,"category":"Nature","sort_order":105,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LAST QUARTER MOON SYMBOL","unified":"1F317","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f317.png","sheet_x":6,"sheet_y":2,"short_name":"last_quarter_moon","short_names":["last_quarter_moon"],"text":null,"texts":null,"category":"Nature","sort_order":106,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WANING CRESCENT MOON SYMBOL","unified":"1F318","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f318.png","sheet_x":6,"sheet_y":3,"short_name":"waning_crescent_moon","short_names":["waning_crescent_moon"],"text":null,"texts":null,"category":"Nature","sort_order":107,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CRESCENT MOON","unified":"1F319","variations":[],"docomo":"E69F","au":"E486","softbank":"E04C","google":"FE014","image":"1f319.png","sheet_x":6,"sheet_y":4,"short_name":"crescent_moon","short_names":["crescent_moon"],"text":null,"texts":null,"category":"Nature","sort_order":117,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NEW MOON WITH FACE","unified":"1F31A","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f31a.png","sheet_x":6,"sheet_y":5,"short_name":"new_moon_with_face","short_names":["new_moon_with_face"],"text":null,"texts":null,"category":"Nature","sort_order":112,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FIRST QUARTER MOON WITH FACE","unified":"1F31B","variations":[],"docomo":"E69E","au":"E489","softbank":"E04C","google":"FE016","image":"1f31b.png","sheet_x":6,"sheet_y":6,"short_name":"first_quarter_moon_with_face","short_names":["first_quarter_moon_with_face"],"text":null,"texts":null,"category":"Nature","sort_order":114,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LAST QUARTER MOON WITH FACE","unified":"1F31C","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f31c.png","sheet_x":6,"sheet_y":7,"short_name":"last_quarter_moon_with_face","short_names":["last_quarter_moon_with_face"],"text":null,"texts":null,"category":"Nature","sort_order":115,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FULL MOON WITH FACE","unified":"1F31D","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f31d.png","sheet_x":6,"sheet_y":8,"short_name":"full_moon_with_face","short_names":["full_moon_with_face"],"text":null,"texts":null,"category":"Nature","sort_order":113,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SUN WITH FACE","unified":"1F31E","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f31e.png","sheet_x":6,"sheet_y":9,"short_name":"sun_with_face","short_names":["sun_with_face"],"text":null,"texts":null,"category":"Nature","sort_order":116,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GLOWING STAR","unified":"1F31F","variations":[],"docomo":null,"au":"E48B","softbank":"E335","google":"FEB69","image":"1f31f.png","sheet_x":6,"sheet_y":10,"short_name":"star2","short_names":["star2"],"text":null,"texts":null,"category":"Nature","sort_order":119,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SHOOTING STAR","unified":"1F320","variations":[],"docomo":null,"au":"E468","softbank":null,"google":"FEB6A","image":"1f320.png","sheet_x":6,"sheet_y":11,"short_name":"stars","short_names":["stars"],"text":null,"texts":null,"category":"Places","sort_order":87,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"THERMOMETER","unified":"1F321","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f321.png","sheet_x":6,"sheet_y":12,"short_name":"thermometer","short_names":["thermometer"],"text":null,"texts":null,"category":"Objects","sort_order":83,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHITE SUN WITH SMALL CLOUD","unified":"1F324","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f324.png","sheet_x":6,"sheet_y":13,"short_name":"mostly_sunny","short_names":["mostly_sunny","sun_small_cloud"],"text":null,"texts":null,"category":"Nature","sort_order":124,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHITE SUN BEHIND CLOUD","unified":"1F325","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f325.png","sheet_x":6,"sheet_y":14,"short_name":"barely_sunny","short_names":["barely_sunny","sun_behind_cloud"],"text":null,"texts":null,"category":"Nature","sort_order":126,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHITE SUN BEHIND CLOUD WITH RAIN","unified":"1F326","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f326.png","sheet_x":6,"sheet_y":15,"short_name":"partly_sunny_rain","short_names":["partly_sunny_rain","sun_behind_rain_cloud"],"text":null,"texts":null,"category":"Nature","sort_order":127,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOUD WITH RAIN","unified":"1F327","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f327.png","sheet_x":6,"sheet_y":16,"short_name":"rain_cloud","short_names":["rain_cloud"],"text":null,"texts":null,"category":"Nature","sort_order":129,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOUD WITH SNOW","unified":"1F328","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f328.png","sheet_x":6,"sheet_y":17,"short_name":"snow_cloud","short_names":["snow_cloud"],"text":null,"texts":null,"category":"Nature","sort_order":136,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOUD WITH LIGHTNING","unified":"1F329","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f329.png","sheet_x":6,"sheet_y":18,"short_name":"lightning","short_names":["lightning","lightning_cloud"],"text":null,"texts":null,"category":"Nature","sort_order":131,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOUD WITH TORNADO","unified":"1F32A","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f32a.png","sheet_x":6,"sheet_y":19,"short_name":"tornado","short_names":["tornado","tornado_cloud"],"text":null,"texts":null,"category":"Nature","sort_order":141,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FOG","unified":"1F32B","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f32b.png","sheet_x":6,"sheet_y":20,"short_name":"fog","short_names":["fog"],"text":null,"texts":null,"category":"Nature","sort_order":142,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WIND BLOWING FACE","unified":"1F32C","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f32c.png","sheet_x":6,"sheet_y":21,"short_name":"wind_blowing_face","short_names":["wind_blowing_face"],"text":null,"texts":null,"category":"Nature","sort_order":139,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HOT DOG","unified":"1F32D","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f32d.png","sheet_x":6,"sheet_y":22,"short_name":"hotdog","short_names":["hotdog"],"text":null,"texts":null,"category":"Foods","sort_order":28,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TACO","unified":"1F32E","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f32e.png","sheet_x":6,"sheet_y":23,"short_name":"taco","short_names":["taco"],"text":null,"texts":null,"category":"Foods","sort_order":31,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BURRITO","unified":"1F32F","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f32f.png","sheet_x":6,"sheet_y":24,"short_name":"burrito","short_names":["burrito"],"text":null,"texts":null,"category":"Foods","sort_order":32,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CHESTNUT","unified":"1F330","variations":[],"docomo":null,"au":"EB38","softbank":null,"google":"FE04C","image":"1f330.png","sheet_x":6,"sheet_y":25,"short_name":"chestnut","short_names":["chestnut"],"text":null,"texts":null,"category":"Nature","sort_order":97,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SEEDLING","unified":"1F331","variations":[],"docomo":"E746","au":"EB7D","softbank":"E110","google":"FE03E","image":"1f331.png","sheet_x":6,"sheet_y":26,"short_name":"seedling","short_names":["seedling"],"text":null,"texts":null,"category":"Nature","sort_order":79,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"EVERGREEN TREE","unified":"1F332","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f332.png","sheet_x":6,"sheet_y":27,"short_name":"evergreen_tree","short_names":["evergreen_tree"],"text":null,"texts":null,"category":"Nature","sort_order":76,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DECIDUOUS TREE","unified":"1F333","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f333.png","sheet_x":6,"sheet_y":28,"short_name":"deciduous_tree","short_names":["deciduous_tree"],"text":null,"texts":null,"category":"Nature","sort_order":77,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PALM TREE","unified":"1F334","variations":[],"docomo":null,"au":"E4E2","softbank":"E307","google":"FE047","image":"1f334.png","sheet_x":6,"sheet_y":29,"short_name":"palm_tree","short_names":["palm_tree"],"text":null,"texts":null,"category":"Nature","sort_order":78,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CACTUS","unified":"1F335","variations":[],"docomo":null,"au":"EA96","softbank":"E308","google":"FE048","image":"1f335.png","sheet_x":6,"sheet_y":30,"short_name":"cactus","short_names":["cactus"],"text":null,"texts":null,"category":"Nature","sort_order":74,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HOT PEPPER","unified":"1F336","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f336.png","sheet_x":6,"sheet_y":31,"short_name":"hot_pepper","short_names":["hot_pepper"],"text":null,"texts":null,"category":"Foods","sort_order":16,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TULIP","unified":"1F337","variations":[],"docomo":"E743","au":"E4E4","softbank":"E304","google":"FE03D","image":"1f337.png","sheet_x":6,"sheet_y":32,"short_name":"tulip","short_names":["tulip"],"text":null,"texts":null,"category":"Nature","sort_order":92,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CHERRY BLOSSOM","unified":"1F338","variations":[],"docomo":"E748","au":"E4CA","softbank":"E030","google":"FE040","image":"1f338.png","sheet_x":6,"sheet_y":33,"short_name":"cherry_blossom","short_names":["cherry_blossom"],"text":null,"texts":null,"category":"Nature","sort_order":94,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ROSE","unified":"1F339","variations":[],"docomo":null,"au":"E5BA","softbank":"E032","google":"FE041","image":"1f339.png","sheet_x":6,"sheet_y":34,"short_name":"rose","short_names":["rose"],"text":null,"texts":null,"category":"Nature","sort_order":91,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HIBISCUS","unified":"1F33A","variations":[],"docomo":null,"au":"EA94","softbank":"E303","google":"FE045","image":"1f33a.png","sheet_x":6,"sheet_y":35,"short_name":"hibiscus","short_names":["hibiscus"],"text":null,"texts":null,"category":"Nature","sort_order":89,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SUNFLOWER","unified":"1F33B","variations":[],"docomo":null,"au":"E4E3","softbank":"E305","google":"FE046","image":"1f33b.png","sheet_x":6,"sheet_y":36,"short_name":"sunflower","short_names":["sunflower"],"text":null,"texts":null,"category":"Nature","sort_order":90,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLOSSOM","unified":"1F33C","variations":[],"docomo":null,"au":"EB49","softbank":"E305","google":"FE04D","image":"1f33c.png","sheet_x":6,"sheet_y":37,"short_name":"blossom","short_names":["blossom"],"text":null,"texts":null,"category":"Nature","sort_order":93,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"EAR OF MAIZE","unified":"1F33D","variations":[],"docomo":null,"au":"EB36","softbank":null,"google":"FE04A","image":"1f33d.png","sheet_x":6,"sheet_y":38,"short_name":"corn","short_names":["corn"],"text":null,"texts":null,"category":"Foods","sort_order":17,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"EAR OF RICE","unified":"1F33E","variations":[],"docomo":null,"au":null,"softbank":"E444","google":"FE049","image":"1f33e.png","sheet_x":6,"sheet_y":39,"short_name":"ear_of_rice","short_names":["ear_of_rice"],"text":null,"texts":null,"category":"Nature","sort_order":88,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HERB","unified":"1F33F","variations":[],"docomo":"E741","au":"EB82","softbank":"E110","google":"FE04E","image":"1f33f.png","sheet_x":6,"sheet_y":40,"short_name":"herb","short_names":["herb"],"text":null,"texts":null,"category":"Nature","sort_order":80,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FOUR LEAF CLOVER","unified":"1F340","variations":[],"docomo":"E741","au":"E513","softbank":"E110","google":"FE03C","image":"1f340.png","sheet_x":7,"sheet_y":0,"short_name":"four_leaf_clover","short_names":["four_leaf_clover"],"text":null,"texts":null,"category":"Nature","sort_order":82,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MAPLE LEAF","unified":"1F341","variations":[],"docomo":"E747","au":"E4CE","softbank":"E118","google":"FE03F","image":"1f341.png","sheet_x":7,"sheet_y":1,"short_name":"maple_leaf","short_names":["maple_leaf"],"text":null,"texts":null,"category":"Nature","sort_order":87,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FALLEN LEAF","unified":"1F342","variations":[],"docomo":"E747","au":"E5CD","softbank":"E119","google":"FE042","image":"1f342.png","sheet_x":7,"sheet_y":2,"short_name":"fallen_leaf","short_names":["fallen_leaf"],"text":null,"texts":null,"category":"Nature","sort_order":86,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LEAF FLUTTERING IN WIND","unified":"1F343","variations":[],"docomo":null,"au":"E5CD","softbank":"E447","google":"FE043","image":"1f343.png","sheet_x":7,"sheet_y":3,"short_name":"leaves","short_names":["leaves"],"text":null,"texts":null,"category":"Nature","sort_order":85,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MUSHROOM","unified":"1F344","variations":[],"docomo":null,"au":"EB37","softbank":null,"google":"FE04B","image":"1f344.png","sheet_x":7,"sheet_y":4,"short_name":"mushroom","short_names":["mushroom"],"text":null,"texts":null,"category":"Nature","sort_order":96,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TOMATO","unified":"1F345","variations":[],"docomo":null,"au":"EABB","softbank":"E349","google":"FE055","image":"1f345.png","sheet_x":7,"sheet_y":5,"short_name":"tomato","short_names":["tomato"],"text":null,"texts":null,"category":"Foods","sort_order":14,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"AUBERGINE","unified":"1F346","variations":[],"docomo":null,"au":"EABC","softbank":"E34A","google":"FE056","image":"1f346.png","sheet_x":7,"sheet_y":6,"short_name":"eggplant","short_names":["eggplant"],"text":null,"texts":null,"category":"Foods","sort_order":15,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GRAPES","unified":"1F347","variations":[],"docomo":null,"au":"EB34","softbank":null,"google":"FE059","image":"1f347.png","sheet_x":7,"sheet_y":7,"short_name":"grapes","short_names":["grapes"],"text":null,"texts":null,"category":"Foods","sort_order":8,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MELON","unified":"1F348","variations":[],"docomo":null,"au":"EB32","softbank":null,"google":"FE057","image":"1f348.png","sheet_x":7,"sheet_y":8,"short_name":"melon","short_names":["melon"],"text":null,"texts":null,"category":"Foods","sort_order":10,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WATERMELON","unified":"1F349","variations":[],"docomo":null,"au":"E4CD","softbank":"E348","google":"FE054","image":"1f349.png","sheet_x":7,"sheet_y":9,"short_name":"watermelon","short_names":["watermelon"],"text":null,"texts":null,"category":"Foods","sort_order":7,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TANGERINE","unified":"1F34A","variations":[],"docomo":null,"au":"EABA","softbank":"E346","google":"FE052","image":"1f34a.png","sheet_x":7,"sheet_y":10,"short_name":"tangerine","short_names":["tangerine"],"text":null,"texts":null,"category":"Foods","sort_order":4,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LEMON","unified":"1F34B","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f34b.png","sheet_x":7,"sheet_y":11,"short_name":"lemon","short_names":["lemon"],"text":null,"texts":null,"category":"Foods","sort_order":5,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BANANA","unified":"1F34C","variations":[],"docomo":"E744","au":"EB35","softbank":null,"google":"FE050","image":"1f34c.png","sheet_x":7,"sheet_y":12,"short_name":"banana","short_names":["banana"],"text":null,"texts":null,"category":"Foods","sort_order":6,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PINEAPPLE","unified":"1F34D","variations":[],"docomo":null,"au":"EB33","softbank":null,"google":"FE058","image":"1f34d.png","sheet_x":7,"sheet_y":13,"short_name":"pineapple","short_names":["pineapple"],"text":null,"texts":null,"category":"Foods","sort_order":13,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RED APPLE","unified":"1F34E","variations":[],"docomo":"E745","au":"EAB9","softbank":"E345","google":"FE051","image":"1f34e.png","sheet_x":7,"sheet_y":14,"short_name":"apple","short_names":["apple"],"text":null,"texts":null,"category":"Foods","sort_order":2,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GREEN APPLE","unified":"1F34F","variations":[],"docomo":"E745","au":"EB5A","softbank":"E345","google":"FE05B","image":"1f34f.png","sheet_x":7,"sheet_y":15,"short_name":"green_apple","short_names":["green_apple"],"text":null,"texts":null,"category":"Foods","sort_order":1,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PEAR","unified":"1F350","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f350.png","sheet_x":7,"sheet_y":16,"short_name":"pear","short_names":["pear"],"text":null,"texts":null,"category":"Foods","sort_order":3,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PEACH","unified":"1F351","variations":[],"docomo":null,"au":"EB39","softbank":null,"google":"FE05A","image":"1f351.png","sheet_x":7,"sheet_y":17,"short_name":"peach","short_names":["peach"],"text":null,"texts":null,"category":"Foods","sort_order":12,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CHERRIES","unified":"1F352","variations":[],"docomo":"E742","au":"E4D2","softbank":null,"google":"FE04F","image":"1f352.png","sheet_x":7,"sheet_y":18,"short_name":"cherries","short_names":["cherries"],"text":null,"texts":null,"category":"Foods","sort_order":11,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"STRAWBERRY","unified":"1F353","variations":[],"docomo":null,"au":"E4D4","softbank":"E347","google":"FE053","image":"1f353.png","sheet_x":7,"sheet_y":19,"short_name":"strawberry","short_names":["strawberry"],"text":null,"texts":null,"category":"Foods","sort_order":9,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HAMBURGER","unified":"1F354","variations":[],"docomo":"E673","au":"E4D6","softbank":"E120","google":"FE960","image":"1f354.png","sheet_x":7,"sheet_y":20,"short_name":"hamburger","short_names":["hamburger"],"text":null,"texts":null,"category":"Foods","sort_order":26,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SLICE OF PIZZA","unified":"1F355","variations":[],"docomo":null,"au":"EB3B","softbank":null,"google":"FE975","image":"1f355.png","sheet_x":7,"sheet_y":21,"short_name":"pizza","short_names":["pizza"],"text":null,"texts":null,"category":"Foods","sort_order":29,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MEAT ON BONE","unified":"1F356","variations":[],"docomo":null,"au":"E4C4","softbank":null,"google":"FE972","image":"1f356.png","sheet_x":7,"sheet_y":22,"short_name":"meat_on_bone","short_names":["meat_on_bone"],"text":null,"texts":null,"category":"Foods","sort_order":23,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"POULTRY LEG","unified":"1F357","variations":[],"docomo":null,"au":"EB3C","softbank":null,"google":"FE976","image":"1f357.png","sheet_x":7,"sheet_y":23,"short_name":"poultry_leg","short_names":["poultry_leg"],"text":null,"texts":null,"category":"Foods","sort_order":22,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RICE CRACKER","unified":"1F358","variations":[],"docomo":null,"au":"EAB3","softbank":"E33D","google":"FE969","image":"1f358.png","sheet_x":7,"sheet_y":24,"short_name":"rice_cracker","short_names":["rice_cracker"],"text":null,"texts":null,"category":"Foods","sort_order":41,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RICE BALL","unified":"1F359","variations":[],"docomo":"E749","au":"E4D5","softbank":"E342","google":"FE961","image":"1f359.png","sheet_x":7,"sheet_y":25,"short_name":"rice_ball","short_names":["rice_ball"],"text":null,"texts":null,"category":"Foods","sort_order":39,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"COOKED RICE","unified":"1F35A","variations":[],"docomo":"E74C","au":"EAB4","softbank":"E33E","google":"FE96A","image":"1f35a.png","sheet_x":7,"sheet_y":26,"short_name":"rice","short_names":["rice"],"text":null,"texts":null,"category":"Foods","sort_order":40,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CURRY AND RICE","unified":"1F35B","variations":[],"docomo":null,"au":"EAB6","softbank":"E341","google":"FE96C","image":"1f35b.png","sheet_x":7,"sheet_y":27,"short_name":"curry","short_names":["curry"],"text":null,"texts":null,"category":"Foods","sort_order":38,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"STEAMING BOWL","unified":"1F35C","variations":[],"docomo":"E74C","au":"E5B4","softbank":"E340","google":"FE963","image":"1f35c.png","sheet_x":7,"sheet_y":28,"short_name":"ramen","short_names":["ramen"],"text":null,"texts":null,"category":"Foods","sort_order":33,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPAGHETTI","unified":"1F35D","variations":[],"docomo":null,"au":"EAB5","softbank":"E33F","google":"FE96B","image":"1f35d.png","sheet_x":7,"sheet_y":29,"short_name":"spaghetti","short_names":["spaghetti"],"text":null,"texts":null,"category":"Foods","sort_order":30,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BREAD","unified":"1F35E","variations":[],"docomo":"E74D","au":"EAAF","softbank":"E339","google":"FE964","image":"1f35e.png","sheet_x":7,"sheet_y":30,"short_name":"bread","short_names":["bread"],"text":null,"texts":null,"category":"Foods","sort_order":20,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FRENCH FRIES","unified":"1F35F","variations":[],"docomo":null,"au":"EAB1","softbank":"E33B","google":"FE967","image":"1f35f.png","sheet_x":7,"sheet_y":31,"short_name":"fries","short_names":["fries"],"text":null,"texts":null,"category":"Foods","sort_order":27,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ROASTED SWEET POTATO","unified":"1F360","variations":[],"docomo":null,"au":"EB3A","softbank":null,"google":"FE974","image":"1f360.png","sheet_x":7,"sheet_y":32,"short_name":"sweet_potato","short_names":["sweet_potato"],"text":null,"texts":null,"category":"Foods","sort_order":18,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DANGO","unified":"1F361","variations":[],"docomo":null,"au":"EAB2","softbank":"E33C","google":"FE968","image":"1f361.png","sheet_x":7,"sheet_y":33,"short_name":"dango","short_names":["dango"],"text":null,"texts":null,"category":"Foods","sort_order":43,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ODEN","unified":"1F362","variations":[],"docomo":null,"au":"EAB7","softbank":"E343","google":"FE96D","image":"1f362.png","sheet_x":7,"sheet_y":34,"short_name":"oden","short_names":["oden"],"text":null,"texts":null,"category":"Foods","sort_order":42,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SUSHI","unified":"1F363","variations":[],"docomo":null,"au":"EAB8","softbank":"E344","google":"FE96E","image":"1f363.png","sheet_x":7,"sheet_y":35,"short_name":"sushi","short_names":["sushi"],"text":null,"texts":null,"category":"Foods","sort_order":36,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FRIED SHRIMP","unified":"1F364","variations":[],"docomo":null,"au":"EB70","softbank":null,"google":"FE97F","image":"1f364.png","sheet_x":7,"sheet_y":36,"short_name":"fried_shrimp","short_names":["fried_shrimp"],"text":null,"texts":null,"category":"Foods","sort_order":24,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FISH CAKE WITH SWIRL DESIGN","unified":"1F365","variations":[],"docomo":"E643","au":"E4ED","softbank":null,"google":"FE973","image":"1f365.png","sheet_x":7,"sheet_y":37,"short_name":"fish_cake","short_names":["fish_cake"],"text":null,"texts":null,"category":"Foods","sort_order":35,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SOFT ICE CREAM","unified":"1F366","variations":[],"docomo":null,"au":"EAB0","softbank":"E33A","google":"FE966","image":"1f366.png","sheet_x":7,"sheet_y":38,"short_name":"icecream","short_names":["icecream"],"text":null,"texts":null,"category":"Foods","sort_order":46,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SHAVED ICE","unified":"1F367","variations":[],"docomo":null,"au":"EAEA","softbank":"E43F","google":"FE971","image":"1f367.png","sheet_x":7,"sheet_y":39,"short_name":"shaved_ice","short_names":["shaved_ice"],"text":null,"texts":null,"category":"Foods","sort_order":44,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ICE CREAM","unified":"1F368","variations":[],"docomo":null,"au":"EB4A","softbank":null,"google":"FE977","image":"1f368.png","sheet_x":7,"sheet_y":40,"short_name":"ice_cream","short_names":["ice_cream"],"text":null,"texts":null,"category":"Foods","sort_order":45,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DOUGHNUT","unified":"1F369","variations":[],"docomo":null,"au":"EB4B","softbank":null,"google":"FE978","image":"1f369.png","sheet_x":8,"sheet_y":0,"short_name":"doughnut","short_names":["doughnut"],"text":null,"texts":null,"category":"Foods","sort_order":54,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"COOKIE","unified":"1F36A","variations":[],"docomo":null,"au":"EB4C","softbank":null,"google":"FE979","image":"1f36a.png","sheet_x":8,"sheet_y":1,"short_name":"cookie","short_names":["cookie"],"text":null,"texts":null,"category":"Foods","sort_order":55,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CHOCOLATE BAR","unified":"1F36B","variations":[],"docomo":null,"au":"EB4D","softbank":null,"google":"FE97A","image":"1f36b.png","sheet_x":8,"sheet_y":2,"short_name":"chocolate_bar","short_names":["chocolate_bar"],"text":null,"texts":null,"category":"Foods","sort_order":52,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CANDY","unified":"1F36C","variations":[],"docomo":null,"au":"EB4E","softbank":null,"google":"FE97B","image":"1f36c.png","sheet_x":8,"sheet_y":3,"short_name":"candy","short_names":["candy"],"text":null,"texts":null,"category":"Foods","sort_order":50,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LOLLIPOP","unified":"1F36D","variations":[],"docomo":null,"au":"EB4F","softbank":null,"google":"FE97C","image":"1f36d.png","sheet_x":8,"sheet_y":4,"short_name":"lollipop","short_names":["lollipop"],"text":null,"texts":null,"category":"Foods","sort_order":51,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CUSTARD","unified":"1F36E","variations":[],"docomo":null,"au":"EB56","softbank":null,"google":"FE97D","image":"1f36e.png","sheet_x":8,"sheet_y":5,"short_name":"custard","short_names":["custard"],"text":null,"texts":null,"category":"Foods","sort_order":49,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HONEY POT","unified":"1F36F","variations":[],"docomo":null,"au":"EB59","softbank":null,"google":"FE97E","image":"1f36f.png","sheet_x":8,"sheet_y":6,"short_name":"honey_pot","short_names":["honey_pot"],"text":null,"texts":null,"category":"Foods","sort_order":19,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SHORTCAKE","unified":"1F370","variations":[],"docomo":"E74A","au":"E4D0","softbank":"E046","google":"FE962","image":"1f370.png","sheet_x":8,"sheet_y":7,"short_name":"cake","short_names":["cake"],"text":null,"texts":null,"category":"Foods","sort_order":47,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BENTO BOX","unified":"1F371","variations":[],"docomo":null,"au":"EABD","softbank":"E34C","google":"FE96F","image":"1f371.png","sheet_x":8,"sheet_y":8,"short_name":"bento","short_names":["bento"],"text":null,"texts":null,"category":"Foods","sort_order":37,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"POT OF FOOD","unified":"1F372","variations":[],"docomo":null,"au":"EABE","softbank":"E34D","google":"FE970","image":"1f372.png","sheet_x":8,"sheet_y":9,"short_name":"stew","short_names":["stew"],"text":null,"texts":null,"category":"Foods","sort_order":34,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"COOKING","unified":"1F373","variations":[],"docomo":null,"au":"E4D1","softbank":"E147","google":"FE965","image":"1f373.png","sheet_x":8,"sheet_y":10,"short_name":"egg","short_names":["egg"],"text":null,"texts":null,"category":"Foods","sort_order":25,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FORK AND KNIFE","unified":"1F374","variations":[],"docomo":"E66F","au":"E4AC","softbank":"E043","google":"FE980","image":"1f374.png","sheet_x":8,"sheet_y":11,"short_name":"fork_and_knife","short_names":["fork_and_knife"],"text":null,"texts":null,"category":"Foods","sort_order":66,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TEACUP WITHOUT HANDLE","unified":"1F375","variations":[],"docomo":"E71E","au":"EAAE","softbank":"E338","google":"FE984","image":"1f375.png","sheet_x":8,"sheet_y":12,"short_name":"tea","short_names":["tea"],"text":null,"texts":null,"category":"Foods","sort_order":63,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SAKE BOTTLE AND CUP","unified":"1F376","variations":[],"docomo":"E74B","au":"EA97","softbank":"E30B","google":"FE985","image":"1f376.png","sheet_x":8,"sheet_y":13,"short_name":"sake","short_names":["sake"],"text":null,"texts":null,"category":"Foods","sort_order":62,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WINE GLASS","unified":"1F377","variations":[],"docomo":"E756","au":"E4C1","softbank":"E044","google":"FE986","image":"1f377.png","sheet_x":8,"sheet_y":14,"short_name":"wine_glass","short_names":["wine_glass"],"text":null,"texts":null,"category":"Foods","sort_order":58,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"COCKTAIL GLASS","unified":"1F378","variations":[],"docomo":"E671","au":"E4C2","softbank":"E044","google":"FE982","image":"1f378.png","sheet_x":8,"sheet_y":15,"short_name":"cocktail","short_names":["cocktail"],"text":null,"texts":null,"category":"Foods","sort_order":59,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TROPICAL DRINK","unified":"1F379","variations":[],"docomo":"E671","au":"EB3E","softbank":"E044","google":"FE988","image":"1f379.png","sheet_x":8,"sheet_y":16,"short_name":"tropical_drink","short_names":["tropical_drink"],"text":null,"texts":null,"category":"Foods","sort_order":60,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BEER MUG","unified":"1F37A","variations":[],"docomo":"E672","au":"E4C3","softbank":"E047","google":"FE983","image":"1f37a.png","sheet_x":8,"sheet_y":17,"short_name":"beer","short_names":["beer"],"text":null,"texts":null,"category":"Foods","sort_order":56,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLINKING BEER MUGS","unified":"1F37B","variations":[],"docomo":"E672","au":"EA98","softbank":"E30C","google":"FE987","image":"1f37b.png","sheet_x":8,"sheet_y":18,"short_name":"beers","short_names":["beers"],"text":null,"texts":null,"category":"Foods","sort_order":57,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BABY BOTTLE","unified":"1F37C","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f37c.png","sheet_x":8,"sheet_y":19,"short_name":"baby_bottle","short_names":["baby_bottle"],"text":null,"texts":null,"category":"Foods","sort_order":65,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FORK AND KNIFE WITH PLATE","unified":"1F37D","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f37d.png","sheet_x":8,"sheet_y":20,"short_name":"knife_fork_plate","short_names":["knife_fork_plate"],"text":null,"texts":null,"category":"Foods","sort_order":67,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BOTTLE WITH POPPING CORK","unified":"1F37E","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f37e.png","sheet_x":8,"sheet_y":21,"short_name":"champagne","short_names":["champagne"],"text":null,"texts":null,"category":"Foods","sort_order":61,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"POPCORN","unified":"1F37F","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f37f.png","sheet_x":8,"sheet_y":22,"short_name":"popcorn","short_names":["popcorn"],"text":null,"texts":null,"category":"Foods","sort_order":53,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RIBBON","unified":"1F380","variations":[],"docomo":"E684","au":"E59F","softbank":"E314","google":"FE50F","image":"1f380.png","sheet_x":8,"sheet_y":23,"short_name":"ribbon","short_names":["ribbon"],"text":null,"texts":null,"category":"Objects","sort_order":103,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WRAPPED PRESENT","unified":"1F381","variations":[],"docomo":"E685","au":"E4CF","softbank":"E112","google":"FE510","image":"1f381.png","sheet_x":8,"sheet_y":24,"short_name":"gift","short_names":["gift"],"text":null,"texts":null,"category":"Objects","sort_order":104,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BIRTHDAY CAKE","unified":"1F382","variations":[],"docomo":"E686","au":"E5A0","softbank":"E34B","google":"FE511","image":"1f382.png","sheet_x":8,"sheet_y":25,"short_name":"birthday","short_names":["birthday"],"text":null,"texts":null,"category":"Foods","sort_order":48,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"JACK-O-LANTERN","unified":"1F383","variations":[],"docomo":null,"au":"EAEE","softbank":"E445","google":"FE51F","image":"1f383.png","sheet_x":8,"sheet_y":26,"short_name":"jack_o_lantern","short_names":["jack_o_lantern"],"text":null,"texts":null,"category":"Nature","sort_order":98,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CHRISTMAS TREE","unified":"1F384","variations":[],"docomo":"E6A4","au":"E4C9","softbank":"E033","google":"FE512","image":"1f384.png","sheet_x":8,"sheet_y":27,"short_name":"christmas_tree","short_names":["christmas_tree"],"text":null,"texts":null,"category":"Nature","sort_order":75,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FATHER CHRISTMAS","unified":"1F385","variations":[],"docomo":null,"au":"EAF0","softbank":"E448","google":"FE513","image":"1f385.png","sheet_x":8,"sheet_y":28,"short_name":"santa","short_names":["santa"],"text":null,"texts":null,"category":"People","sort_order":135,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F385-1F3FB":{"unified":"1F385-1F3FB","image":"1f385-1f3fb.png","sheet_x":8,"sheet_y":29,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F385-1F3FC":{"unified":"1F385-1F3FC","image":"1f385-1f3fc.png","sheet_x":8,"sheet_y":30,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F385-1F3FD":{"unified":"1F385-1F3FD","image":"1f385-1f3fd.png","sheet_x":8,"sheet_y":31,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F385-1F3FE":{"unified":"1F385-1F3FE","image":"1f385-1f3fe.png","sheet_x":8,"sheet_y":32,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F385-1F3FF":{"unified":"1F385-1F3FF","image":"1f385-1f3ff.png","sheet_x":8,"sheet_y":33,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"FIREWORKS","unified":"1F386","variations":[],"docomo":null,"au":"E5CC","softbank":"E117","google":"FE515","image":"1f386.png","sheet_x":8,"sheet_y":34,"short_name":"fireworks","short_names":["fireworks"],"text":null,"texts":null,"category":"Places","sort_order":89,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FIREWORK SPARKLER","unified":"1F387","variations":[],"docomo":null,"au":"EAEB","softbank":"E440","google":"FE51D","image":"1f387.png","sheet_x":8,"sheet_y":35,"short_name":"sparkler","short_names":["sparkler"],"text":null,"texts":null,"category":"Places","sort_order":88,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BALLOON","unified":"1F388","variations":[],"docomo":null,"au":"EA9B","softbank":"E310","google":"FE516","image":"1f388.png","sheet_x":8,"sheet_y":36,"short_name":"balloon","short_names":["balloon"],"text":null,"texts":null,"category":"Objects","sort_order":101,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PARTY POPPER","unified":"1F389","variations":[],"docomo":null,"au":"EA9C","softbank":"E312","google":"FE517","image":"1f389.png","sheet_x":8,"sheet_y":37,"short_name":"tada","short_names":["tada"],"text":null,"texts":null,"category":"Objects","sort_order":106,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CONFETTI BALL","unified":"1F38A","variations":[],"docomo":null,"au":"E46F","softbank":null,"google":"FE520","image":"1f38a.png","sheet_x":8,"sheet_y":38,"short_name":"confetti_ball","short_names":["confetti_ball"],"text":null,"texts":null,"category":"Objects","sort_order":105,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TANABATA TREE","unified":"1F38B","variations":[],"docomo":null,"au":"EB3D","softbank":null,"google":"FE521","image":"1f38b.png","sheet_x":8,"sheet_y":39,"short_name":"tanabata_tree","short_names":["tanabata_tree"],"text":null,"texts":null,"category":"Nature","sort_order":84,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CROSSED FLAGS","unified":"1F38C","variations":[],"docomo":null,"au":"E5D9","softbank":"E143","google":"FE514","image":"1f38c.png","sheet_x":8,"sheet_y":40,"short_name":"crossed_flags","short_names":["crossed_flags"],"text":null,"texts":null,"category":"Objects","sort_order":109,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PINE DECORATION","unified":"1F38D","variations":[],"docomo":null,"au":"EAE3","softbank":"E436","google":"FE518","image":"1f38d.png","sheet_x":9,"sheet_y":0,"short_name":"bamboo","short_names":["bamboo"],"text":null,"texts":null,"category":"Nature","sort_order":83,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"JAPANESE DOLLS","unified":"1F38E","variations":[],"docomo":null,"au":"EAE4","softbank":"E438","google":"FE519","image":"1f38e.png","sheet_x":9,"sheet_y":1,"short_name":"dolls","short_names":["dolls"],"text":null,"texts":null,"category":"Objects","sort_order":107,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CARP STREAMER","unified":"1F38F","variations":[],"docomo":null,"au":"EAE7","softbank":"E43B","google":"FE51C","image":"1f38f.png","sheet_x":9,"sheet_y":2,"short_name":"flags","short_names":["flags"],"text":null,"texts":null,"category":"Objects","sort_order":102,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WIND CHIME","unified":"1F390","variations":[],"docomo":null,"au":"EAED","softbank":"E442","google":"FE51E","image":"1f390.png","sheet_x":9,"sheet_y":3,"short_name":"wind_chime","short_names":["wind_chime"],"text":null,"texts":null,"category":"Objects","sort_order":108,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MOON VIEWING CEREMONY","unified":"1F391","variations":[],"docomo":null,"au":"EAEF","softbank":"E446","google":"FE017","image":"1f391.png","sheet_x":9,"sheet_y":4,"short_name":"rice_scene","short_names":["rice_scene"],"text":null,"texts":null,"category":"Places","sort_order":65,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SCHOOL SATCHEL","unified":"1F392","variations":[],"docomo":null,"au":"EAE6","softbank":"E43A","google":"FE51B","image":"1f392.png","sheet_x":9,"sheet_y":5,"short_name":"school_satchel","short_names":["school_satchel"],"text":null,"texts":null,"category":"People","sort_order":196,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GRADUATION CAP","unified":"1F393","variations":[],"docomo":null,"au":"EAE5","softbank":"E439","google":"FE51A","image":"1f393.png","sheet_x":9,"sheet_y":6,"short_name":"mortar_board","short_names":["mortar_board"],"text":null,"texts":null,"category":"People","sort_order":194,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MILITARY MEDAL","unified":"1F396","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f396.png","sheet_x":9,"sheet_y":7,"short_name":"medal","short_names":["medal"],"text":null,"texts":null,"category":"Activity","sort_order":35,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REMINDER RIBBON","unified":"1F397","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f397.png","sheet_x":9,"sheet_y":8,"short_name":"reminder_ribbon","short_names":["reminder_ribbon"],"text":null,"texts":null,"category":"Activity","sort_order":36,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"STUDIO MICROPHONE","unified":"1F399","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f399.png","sheet_x":9,"sheet_y":9,"short_name":"studio_microphone","short_names":["studio_microphone"],"text":null,"texts":null,"category":"Objects","sort_order":29,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LEVEL SLIDER","unified":"1F39A","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f39a.png","sheet_x":9,"sheet_y":10,"short_name":"level_slider","short_names":["level_slider"],"text":null,"texts":null,"category":"Objects","sort_order":30,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CONTROL KNOBS","unified":"1F39B","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f39b.png","sheet_x":9,"sheet_y":11,"short_name":"control_knobs","short_names":["control_knobs"],"text":null,"texts":null,"category":"Objects","sort_order":31,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FILM FRAMES","unified":"1F39E","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f39e.png","sheet_x":9,"sheet_y":12,"short_name":"film_frames","short_names":["film_frames"],"text":null,"texts":null,"category":"Objects","sort_order":22,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ADMISSION TICKETS","unified":"1F39F","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f39f.png","sheet_x":9,"sheet_y":13,"short_name":"admission_tickets","short_names":["admission_tickets"],"text":null,"texts":null,"category":"Activity","sort_order":39,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CAROUSEL HORSE","unified":"1F3A0","variations":[],"docomo":"E679","au":null,"softbank":null,"google":"FE7FC","image":"1f3a0.png","sheet_x":9,"sheet_y":14,"short_name":"carousel_horse","short_names":["carousel_horse"],"text":null,"texts":null,"category":"Places","sort_order":59,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FERRIS WHEEL","unified":"1F3A1","variations":[],"docomo":null,"au":"E46D","softbank":"E124","google":"FE7FD","image":"1f3a1.png","sheet_x":9,"sheet_y":15,"short_name":"ferris_wheel","short_names":["ferris_wheel"],"text":null,"texts":null,"category":"Places","sort_order":57,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ROLLER COASTER","unified":"1F3A2","variations":[],"docomo":null,"au":"EAE2","softbank":"E433","google":"FE7FE","image":"1f3a2.png","sheet_x":9,"sheet_y":16,"short_name":"roller_coaster","short_names":["roller_coaster"],"text":null,"texts":null,"category":"Places","sort_order":58,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FISHING POLE AND FISH","unified":"1F3A3","variations":[],"docomo":"E751","au":"EB42","softbank":"E019","google":"FE7FF","image":"1f3a3.png","sheet_x":9,"sheet_y":17,"short_name":"fishing_pole_and_fish","short_names":["fishing_pole_and_fish"],"text":null,"texts":null,"category":"Activity","sort_order":21,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MICROPHONE","unified":"1F3A4","variations":[],"docomo":"E676","au":"E503","softbank":"E03C","google":"FE800","image":"1f3a4.png","sheet_x":9,"sheet_y":18,"short_name":"microphone","short_names":["microphone"],"text":null,"texts":null,"category":"Activity","sort_order":43,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MOVIE CAMERA","unified":"1F3A5","variations":[],"docomo":"E677","au":"E517","softbank":"E03D","google":"FE801","image":"1f3a5.png","sheet_x":9,"sheet_y":19,"short_name":"movie_camera","short_names":["movie_camera"],"text":null,"texts":null,"category":"Objects","sort_order":20,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CINEMA","unified":"1F3A6","variations":[],"docomo":"E677","au":"E517","softbank":"E507","google":"FE802","image":"1f3a6.png","sheet_x":9,"sheet_y":20,"short_name":"cinema","short_names":["cinema"],"text":null,"texts":null,"category":"Symbols","sort_order":125,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HEADPHONE","unified":"1F3A7","variations":[],"docomo":"E67A","au":"E508","softbank":"E30A","google":"FE803","image":"1f3a7.png","sheet_x":9,"sheet_y":21,"short_name":"headphones","short_names":["headphones"],"text":null,"texts":null,"category":"Activity","sort_order":44,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ARTIST PALETTE","unified":"1F3A8","variations":[],"docomo":"E67B","au":"E59C","softbank":"E502","google":"FE804","image":"1f3a8.png","sheet_x":9,"sheet_y":22,"short_name":"art","short_names":["art"],"text":null,"texts":null,"category":"Activity","sort_order":41,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TOP HAT","unified":"1F3A9","variations":[],"docomo":"E67C","au":"EAF5","softbank":"E503","google":"FE805","image":"1f3a9.png","sheet_x":9,"sheet_y":23,"short_name":"tophat","short_names":["tophat"],"text":null,"texts":null,"category":"People","sort_order":192,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CIRCUS TENT","unified":"1F3AA","variations":[],"docomo":"E67D","au":"E59E","softbank":null,"google":"FE806","image":"1f3aa.png","sheet_x":9,"sheet_y":24,"short_name":"circus_tent","short_names":["circus_tent"],"text":null,"texts":null,"category":"Activity","sort_order":42,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TICKET","unified":"1F3AB","variations":[],"docomo":"E67E","au":"E49E","softbank":"E125","google":"FE807","image":"1f3ab.png","sheet_x":9,"sheet_y":25,"short_name":"ticket","short_names":["ticket"],"text":null,"texts":null,"category":"Activity","sort_order":38,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLAPPER BOARD","unified":"1F3AC","variations":[],"docomo":"E6AC","au":"E4BE","softbank":"E324","google":"FE808","image":"1f3ac.png","sheet_x":9,"sheet_y":26,"short_name":"clapper","short_names":["clapper"],"text":null,"texts":null,"category":"Activity","sort_order":51,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PERFORMING ARTS","unified":"1F3AD","variations":[],"docomo":null,"au":"E59D","softbank":"E503","google":"FE809","image":"1f3ad.png","sheet_x":9,"sheet_y":27,"short_name":"performing_arts","short_names":["performing_arts"],"text":null,"texts":null,"category":"Activity","sort_order":40,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"VIDEO GAME","unified":"1F3AE","variations":[],"docomo":"E68B","au":"E4C6","softbank":null,"google":"FE80A","image":"1f3ae.png","sheet_x":9,"sheet_y":28,"short_name":"video_game","short_names":["video_game"],"text":null,"texts":null,"category":"Activity","sort_order":52,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DIRECT HIT","unified":"1F3AF","variations":[],"docomo":null,"au":"E4C5","softbank":"E130","google":"FE80C","image":"1f3af.png","sheet_x":9,"sheet_y":29,"short_name":"dart","short_names":["dart"],"text":null,"texts":null,"category":"Activity","sort_order":54,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SLOT MACHINE","unified":"1F3B0","variations":[],"docomo":null,"au":"E46E","softbank":"E133","google":"FE80D","image":"1f3b0.png","sheet_x":9,"sheet_y":30,"short_name":"slot_machine","short_names":["slot_machine"],"text":null,"texts":null,"category":"Activity","sort_order":56,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BILLIARDS","unified":"1F3B1","variations":[],"docomo":null,"au":"EADD","softbank":"E42C","google":"FE80E","image":"1f3b1.png","sheet_x":9,"sheet_y":31,"short_name":"8ball","short_names":["8ball"],"text":null,"texts":null,"category":"Activity","sort_order":8,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GAME DIE","unified":"1F3B2","variations":[],"docomo":null,"au":"E4C8","softbank":null,"google":"FE80F","image":"1f3b2.png","sheet_x":9,"sheet_y":32,"short_name":"game_die","short_names":["game_die"],"text":null,"texts":null,"category":"Activity","sort_order":55,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BOWLING","unified":"1F3B3","variations":[],"docomo":null,"au":"EB43","softbank":null,"google":"FE810","image":"1f3b3.png","sheet_x":9,"sheet_y":33,"short_name":"bowling","short_names":["bowling"],"text":null,"texts":null,"category":"Activity","sort_order":57,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FLOWER PLAYING CARDS","unified":"1F3B4","variations":[],"docomo":null,"au":"EB6E","softbank":null,"google":"FE811","image":"1f3b4.png","sheet_x":9,"sheet_y":34,"short_name":"flower_playing_cards","short_names":["flower_playing_cards"],"text":null,"texts":null,"category":"Symbols","sort_order":241,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MUSICAL NOTE","unified":"1F3B5","variations":[],"docomo":"E6F6","au":"E5BE","softbank":"E03E","google":"FE813","image":"1f3b5.png","sheet_x":9,"sheet_y":35,"short_name":"musical_note","short_names":["musical_note"],"text":null,"texts":null,"category":"Symbols","sort_order":185,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MULTIPLE MUSICAL NOTES","unified":"1F3B6","variations":[],"docomo":"E6FF","au":"E505","softbank":"E326","google":"FE814","image":"1f3b6.png","sheet_x":9,"sheet_y":36,"short_name":"notes","short_names":["notes"],"text":null,"texts":null,"category":"Symbols","sort_order":186,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SAXOPHONE","unified":"1F3B7","variations":[],"docomo":null,"au":null,"softbank":"E040","google":"FE815","image":"1f3b7.png","sheet_x":9,"sheet_y":37,"short_name":"saxophone","short_names":["saxophone"],"text":null,"texts":null,"category":"Activity","sort_order":47,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GUITAR","unified":"1F3B8","variations":[],"docomo":null,"au":"E506","softbank":"E041","google":"FE816","image":"1f3b8.png","sheet_x":9,"sheet_y":38,"short_name":"guitar","short_names":["guitar"],"text":null,"texts":null,"category":"Activity","sort_order":49,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MUSICAL KEYBOARD","unified":"1F3B9","variations":[],"docomo":null,"au":"EB40","softbank":null,"google":"FE817","image":"1f3b9.png","sheet_x":9,"sheet_y":39,"short_name":"musical_keyboard","short_names":["musical_keyboard"],"text":null,"texts":null,"category":"Activity","sort_order":46,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TRUMPET","unified":"1F3BA","variations":[],"docomo":null,"au":"EADC","softbank":"E042","google":"FE818","image":"1f3ba.png","sheet_x":9,"sheet_y":40,"short_name":"trumpet","short_names":["trumpet"],"text":null,"texts":null,"category":"Activity","sort_order":48,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"VIOLIN","unified":"1F3BB","variations":[],"docomo":null,"au":"E507","softbank":null,"google":"FE819","image":"1f3bb.png","sheet_x":10,"sheet_y":0,"short_name":"violin","short_names":["violin"],"text":null,"texts":null,"category":"Activity","sort_order":50,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MUSICAL SCORE","unified":"1F3BC","variations":[],"docomo":"E6FF","au":"EACC","softbank":"E326","google":"FE81A","image":"1f3bc.png","sheet_x":10,"sheet_y":1,"short_name":"musical_score","short_names":["musical_score"],"text":null,"texts":null,"category":"Activity","sort_order":45,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RUNNING SHIRT WITH SASH","unified":"1F3BD","variations":[],"docomo":"E652","au":null,"softbank":null,"google":"FE7D0","image":"1f3bd.png","sheet_x":10,"sheet_y":2,"short_name":"running_shirt_with_sash","short_names":["running_shirt_with_sash"],"text":null,"texts":null,"category":"Activity","sort_order":33,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TENNIS RACQUET AND BALL","unified":"1F3BE","variations":[],"docomo":"E655","au":"E4B7","softbank":"E015","google":"FE7D3","image":"1f3be.png","sheet_x":10,"sheet_y":3,"short_name":"tennis","short_names":["tennis"],"text":null,"texts":null,"category":"Activity","sort_order":5,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SKI AND SKI BOOT","unified":"1F3BF","variations":[],"docomo":"E657","au":"EAAC","softbank":"E013","google":"FE7D5","image":"1f3bf.png","sheet_x":10,"sheet_y":4,"short_name":"ski","short_names":["ski"],"text":null,"texts":null,"category":"Activity","sort_order":16,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BASKETBALL AND HOOP","unified":"1F3C0","variations":[],"docomo":"E658","au":"E59A","softbank":"E42A","google":"FE7D6","image":"1f3c0.png","sheet_x":10,"sheet_y":5,"short_name":"basketball","short_names":["basketball"],"text":null,"texts":null,"category":"Activity","sort_order":2,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CHEQUERED FLAG","unified":"1F3C1","variations":[],"docomo":"E659","au":"E4B9","softbank":"E132","google":"FE7D7","image":"1f3c1.png","sheet_x":10,"sheet_y":6,"short_name":"checkered_flag","short_names":["checkered_flag"],"text":null,"texts":null,"category":"Places","sort_order":55,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SNOWBOARDER","unified":"1F3C2","variations":[],"docomo":"E712","au":"E4B8","softbank":null,"google":"FE7D8","image":"1f3c2.png","sheet_x":10,"sheet_y":7,"short_name":"snowboarder","short_names":["snowboarder"],"text":null,"texts":null,"category":"Activity","sort_order":18,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RUNNER","unified":"1F3C3","variations":[],"docomo":"E733","au":"E46B","softbank":"E115","google":"FE7D9","image":"1f3c3.png","sheet_x":10,"sheet_y":8,"short_name":"runner","short_names":["runner","running"],"text":null,"texts":null,"category":"People","sort_order":140,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F3C3-1F3FB":{"unified":"1F3C3-1F3FB","image":"1f3c3-1f3fb.png","sheet_x":10,"sheet_y":9,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3C3-1F3FC":{"unified":"1F3C3-1F3FC","image":"1f3c3-1f3fc.png","sheet_x":10,"sheet_y":10,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3C3-1F3FD":{"unified":"1F3C3-1F3FD","image":"1f3c3-1f3fd.png","sheet_x":10,"sheet_y":11,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3C3-1F3FE":{"unified":"1F3C3-1F3FE","image":"1f3c3-1f3fe.png","sheet_x":10,"sheet_y":12,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3C3-1F3FF":{"unified":"1F3C3-1F3FF","image":"1f3c3-1f3ff.png","sheet_x":10,"sheet_y":13,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"SURFER","unified":"1F3C4","variations":[],"docomo":"E712","au":"EB41","softbank":"E017","google":"FE7DA","image":"1f3c4.png","sheet_x":10,"sheet_y":14,"short_name":"surfer","short_names":["surfer"],"text":null,"texts":null,"category":"Activity","sort_order":24,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F3C4-1F3FB":{"unified":"1F3C4-1F3FB","image":"1f3c4-1f3fb.png","sheet_x":10,"sheet_y":15,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3C4-1F3FC":{"unified":"1F3C4-1F3FC","image":"1f3c4-1f3fc.png","sheet_x":10,"sheet_y":16,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3C4-1F3FD":{"unified":"1F3C4-1F3FD","image":"1f3c4-1f3fd.png","sheet_x":10,"sheet_y":17,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3C4-1F3FE":{"unified":"1F3C4-1F3FE","image":"1f3c4-1f3fe.png","sheet_x":10,"sheet_y":18,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3C4-1F3FF":{"unified":"1F3C4-1F3FF","image":"1f3c4-1f3ff.png","sheet_x":10,"sheet_y":19,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"SPORTS MEDAL","unified":"1F3C5","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3c5.png","sheet_x":10,"sheet_y":20,"short_name":"sports_medal","short_names":["sports_medal"],"text":null,"texts":null,"category":"Activity","sort_order":34,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TROPHY","unified":"1F3C6","variations":[],"docomo":null,"au":"E5D3","softbank":"E131","google":"FE7DB","image":"1f3c6.png","sheet_x":10,"sheet_y":21,"short_name":"trophy","short_names":["trophy"],"text":null,"texts":null,"category":"Activity","sort_order":32,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HORSE RACING","unified":"1F3C7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3c7.png","sheet_x":10,"sheet_y":22,"short_name":"horse_racing","short_names":["horse_racing"],"text":null,"texts":null,"category":"Activity","sort_order":30,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F3C7-1F3FB":{"unified":"1F3C7-1F3FB","image":"1f3c7-1f3fb.png","sheet_x":10,"sheet_y":23,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3C7-1F3FC":{"unified":"1F3C7-1F3FC","image":"1f3c7-1f3fc.png","sheet_x":10,"sheet_y":24,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3C7-1F3FD":{"unified":"1F3C7-1F3FD","image":"1f3c7-1f3fd.png","sheet_x":10,"sheet_y":25,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3C7-1F3FE":{"unified":"1F3C7-1F3FE","image":"1f3c7-1f3fe.png","sheet_x":10,"sheet_y":26,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3C7-1F3FF":{"unified":"1F3C7-1F3FF","image":"1f3c7-1f3ff.png","sheet_x":10,"sheet_y":27,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"AMERICAN FOOTBALL","unified":"1F3C8","variations":[],"docomo":null,"au":"E4BB","softbank":"E42B","google":"FE7DD","image":"1f3c8.png","sheet_x":10,"sheet_y":28,"short_name":"football","short_names":["football"],"text":null,"texts":null,"category":"Activity","sort_order":3,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RUGBY FOOTBALL","unified":"1F3C9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3c9.png","sheet_x":10,"sheet_y":29,"short_name":"rugby_football","short_names":["rugby_football"],"text":null,"texts":null,"category":"Activity","sort_order":7,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SWIMMER","unified":"1F3CA","variations":[],"docomo":null,"au":"EADE","softbank":"E42D","google":"FE7DE","image":"1f3ca.png","sheet_x":10,"sheet_y":30,"short_name":"swimmer","short_names":["swimmer"],"text":null,"texts":null,"category":"Activity","sort_order":23,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F3CA-1F3FB":{"unified":"1F3CA-1F3FB","image":"1f3ca-1f3fb.png","sheet_x":10,"sheet_y":31,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3CA-1F3FC":{"unified":"1F3CA-1F3FC","image":"1f3ca-1f3fc.png","sheet_x":10,"sheet_y":32,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3CA-1F3FD":{"unified":"1F3CA-1F3FD","image":"1f3ca-1f3fd.png","sheet_x":10,"sheet_y":33,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3CA-1F3FE":{"unified":"1F3CA-1F3FE","image":"1f3ca-1f3fe.png","sheet_x":10,"sheet_y":34,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3CA-1F3FF":{"unified":"1F3CA-1F3FF","image":"1f3ca-1f3ff.png","sheet_x":10,"sheet_y":35,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"WEIGHT LIFTER","unified":"1F3CB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3cb.png","sheet_x":10,"sheet_y":36,"short_name":"weight_lifter","short_names":["weight_lifter"],"text":null,"texts":null,"category":"Activity","sort_order":27,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F3CB-1F3FB":{"unified":"1F3CB-1F3FB","image":"1f3cb-1f3fb.png","sheet_x":10,"sheet_y":37,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3CB-1F3FC":{"unified":"1F3CB-1F3FC","image":"1f3cb-1f3fc.png","sheet_x":10,"sheet_y":38,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3CB-1F3FD":{"unified":"1F3CB-1F3FD","image":"1f3cb-1f3fd.png","sheet_x":10,"sheet_y":39,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3CB-1F3FE":{"unified":"1F3CB-1F3FE","image":"1f3cb-1f3fe.png","sheet_x":10,"sheet_y":40,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F3CB-1F3FF":{"unified":"1F3CB-1F3FF","image":"1f3cb-1f3ff.png","sheet_x":11,"sheet_y":0,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"GOLFER","unified":"1F3CC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3cc.png","sheet_x":11,"sheet_y":1,"short_name":"golfer","short_names":["golfer"],"text":null,"texts":null,"category":"Activity","sort_order":10,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RACING MOTORCYCLE","unified":"1F3CD","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3cd.png","sheet_x":11,"sheet_y":2,"short_name":"racing_motorcycle","short_names":["racing_motorcycle"],"text":null,"texts":null,"category":"Places","sort_order":14,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RACING CAR","unified":"1F3CE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3ce.png","sheet_x":11,"sheet_y":3,"short_name":"racing_car","short_names":["racing_car"],"text":null,"texts":null,"category":"Places","sort_order":6,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CRICKET BAT AND BALL","unified":"1F3CF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3cf.png","sheet_x":11,"sheet_y":4,"short_name":"cricket_bat_and_ball","short_names":["cricket_bat_and_ball"],"text":null,"texts":null,"category":"Activity","sort_order":15,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"VOLLEYBALL","unified":"1F3D0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d0.png","sheet_x":11,"sheet_y":5,"short_name":"volleyball","short_names":["volleyball"],"text":null,"texts":null,"category":"Activity","sort_order":6,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FIELD HOCKEY STICK AND BALL","unified":"1F3D1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d1.png","sheet_x":11,"sheet_y":6,"short_name":"field_hockey_stick_and_ball","short_names":["field_hockey_stick_and_ball"],"text":null,"texts":null,"category":"Activity","sort_order":14,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ICE HOCKEY STICK AND PUCK","unified":"1F3D2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d2.png","sheet_x":11,"sheet_y":7,"short_name":"ice_hockey_stick_and_puck","short_names":["ice_hockey_stick_and_puck"],"text":null,"texts":null,"category":"Activity","sort_order":13,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TABLE TENNIS PADDLE AND BALL","unified":"1F3D3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d3.png","sheet_x":11,"sheet_y":8,"short_name":"table_tennis_paddle_and_ball","short_names":["table_tennis_paddle_and_ball"],"text":null,"texts":null,"category":"Activity","sort_order":11,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SNOW CAPPED MOUNTAIN","unified":"1F3D4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d4.png","sheet_x":11,"sheet_y":9,"short_name":"snow_capped_mountain","short_names":["snow_capped_mountain"],"text":null,"texts":null,"category":"Places","sort_order":67,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CAMPING","unified":"1F3D5","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d5.png","sheet_x":11,"sheet_y":10,"short_name":"camping","short_names":["camping"],"text":null,"texts":null,"category":"Places","sort_order":71,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BEACH WITH UMBRELLA","unified":"1F3D6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d6.png","sheet_x":11,"sheet_y":11,"short_name":"beach_with_umbrella","short_names":["beach_with_umbrella"],"text":null,"texts":null,"category":"Places","sort_order":79,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BUILDING CONSTRUCTION","unified":"1F3D7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d7.png","sheet_x":11,"sheet_y":12,"short_name":"building_construction","short_names":["building_construction"],"text":null,"texts":null,"category":"Places","sort_order":60,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HOUSE BUILDINGS","unified":"1F3D8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d8.png","sheet_x":11,"sheet_y":13,"short_name":"house_buildings","short_names":["house_buildings"],"text":null,"texts":null,"category":"Places","sort_order":91,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CITYSCAPE","unified":"1F3D9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3d9.png","sheet_x":11,"sheet_y":14,"short_name":"cityscape","short_names":["cityscape"],"text":null,"texts":null,"category":"Places","sort_order":83,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DERELICT HOUSE BUILDING","unified":"1F3DA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3da.png","sheet_x":11,"sheet_y":15,"short_name":"derelict_house_building","short_names":["derelict_house_building"],"text":null,"texts":null,"category":"Places","sort_order":98,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLASSICAL BUILDING","unified":"1F3DB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3db.png","sheet_x":11,"sheet_y":16,"short_name":"classical_building","short_names":["classical_building"],"text":null,"texts":null,"category":"Places","sort_order":110,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DESERT","unified":"1F3DC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3dc.png","sheet_x":11,"sheet_y":17,"short_name":"desert","short_names":["desert"],"text":null,"texts":null,"category":"Places","sort_order":78,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DESERT ISLAND","unified":"1F3DD","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3dd.png","sheet_x":11,"sheet_y":18,"short_name":"desert_island","short_names":["desert_island"],"text":null,"texts":null,"category":"Places","sort_order":80,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NATIONAL PARK","unified":"1F3DE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3de.png","sheet_x":11,"sheet_y":19,"short_name":"national_park","short_names":["national_park"],"text":null,"texts":null,"category":"Places","sort_order":73,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"STADIUM","unified":"1F3DF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3df.png","sheet_x":11,"sheet_y":20,"short_name":"stadium","short_names":["stadium"],"text":null,"texts":null,"category":"Places","sort_order":94,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HOUSE BUILDING","unified":"1F3E0","variations":[],"docomo":"E663","au":"E4AB","softbank":"E036","google":"FE4B0","image":"1f3e0.png","sheet_x":11,"sheet_y":21,"short_name":"house","short_names":["house"],"text":null,"texts":null,"category":"Places","sort_order":96,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HOUSE WITH GARDEN","unified":"1F3E1","variations":[],"docomo":"E663","au":"EB09","softbank":"E036","google":"FE4B1","image":"1f3e1.png","sheet_x":11,"sheet_y":22,"short_name":"house_with_garden","short_names":["house_with_garden"],"text":null,"texts":null,"category":"Places","sort_order":97,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"OFFICE BUILDING","unified":"1F3E2","variations":[],"docomo":"E664","au":"E4AD","softbank":"E038","google":"FE4B2","image":"1f3e2.png","sheet_x":11,"sheet_y":23,"short_name":"office","short_names":["office"],"text":null,"texts":null,"category":"Places","sort_order":99,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"JAPANESE POST OFFICE","unified":"1F3E3","variations":[],"docomo":"E665","au":"E5DE","softbank":"E153","google":"FE4B3","image":"1f3e3.png","sheet_x":11,"sheet_y":24,"short_name":"post_office","short_names":["post_office"],"text":null,"texts":null,"category":"Places","sort_order":101,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"EUROPEAN POST OFFICE","unified":"1F3E4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3e4.png","sheet_x":11,"sheet_y":25,"short_name":"european_post_office","short_names":["european_post_office"],"text":null,"texts":null,"category":"Places","sort_order":102,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HOSPITAL","unified":"1F3E5","variations":[],"docomo":"E666","au":"E5DF","softbank":"E155","google":"FE4B4","image":"1f3e5.png","sheet_x":11,"sheet_y":26,"short_name":"hospital","short_names":["hospital"],"text":null,"texts":null,"category":"Places","sort_order":103,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BANK","unified":"1F3E6","variations":[],"docomo":"E667","au":"E4AA","softbank":"E14D","google":"FE4B5","image":"1f3e6.png","sheet_x":11,"sheet_y":27,"short_name":"bank","short_names":["bank"],"text":null,"texts":null,"category":"Places","sort_order":104,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"AUTOMATED TELLER MACHINE","unified":"1F3E7","variations":[],"docomo":"E668","au":"E4A3","softbank":"E154","google":"FE4B6","image":"1f3e7.png","sheet_x":11,"sheet_y":28,"short_name":"atm","short_names":["atm"],"text":null,"texts":null,"category":"Symbols","sort_order":109,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HOTEL","unified":"1F3E8","variations":[],"docomo":"E669","au":"EA81","softbank":"E158","google":"FE4B7","image":"1f3e8.png","sheet_x":11,"sheet_y":29,"short_name":"hotel","short_names":["hotel"],"text":null,"texts":null,"category":"Places","sort_order":105,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LOVE HOTEL","unified":"1F3E9","variations":[],"docomo":"E669-E6EF","au":"EAF3","softbank":"E501","google":"FE4B8","image":"1f3e9.png","sheet_x":11,"sheet_y":30,"short_name":"love_hotel","short_names":["love_hotel"],"text":null,"texts":null,"category":"Places","sort_order":108,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CONVENIENCE STORE","unified":"1F3EA","variations":[],"docomo":"E66A","au":"E4A4","softbank":"E156","google":"FE4B9","image":"1f3ea.png","sheet_x":11,"sheet_y":31,"short_name":"convenience_store","short_names":["convenience_store"],"text":null,"texts":null,"category":"Places","sort_order":106,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SCHOOL","unified":"1F3EB","variations":[],"docomo":"E73E","au":"EA80","softbank":"E157","google":"FE4BA","image":"1f3eb.png","sheet_x":11,"sheet_y":32,"short_name":"school","short_names":["school"],"text":null,"texts":null,"category":"Places","sort_order":107,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DEPARTMENT STORE","unified":"1F3EC","variations":[],"docomo":null,"au":"EAF6","softbank":"E504","google":"FE4BD","image":"1f3ec.png","sheet_x":11,"sheet_y":33,"short_name":"department_store","short_names":["department_store"],"text":null,"texts":null,"category":"Places","sort_order":100,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACTORY","unified":"1F3ED","variations":[],"docomo":null,"au":"EAF9","softbank":"E508","google":"FE4C0","image":"1f3ed.png","sheet_x":11,"sheet_y":34,"short_name":"factory","short_names":["factory"],"text":null,"texts":null,"category":"Places","sort_order":63,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"IZAKAYA LANTERN","unified":"1F3EE","variations":[],"docomo":"E74B","au":"E4BD","softbank":"E30B","google":"FE4C2","image":"1f3ee.png","sheet_x":11,"sheet_y":35,"short_name":"izakaya_lantern","short_names":["izakaya_lantern","lantern"],"text":null,"texts":null,"category":"Objects","sort_order":110,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"JAPANESE CASTLE","unified":"1F3EF","variations":[],"docomo":null,"au":"EAF7","softbank":"E505","google":"FE4BE","image":"1f3ef.png","sheet_x":11,"sheet_y":36,"short_name":"japanese_castle","short_names":["japanese_castle"],"text":null,"texts":null,"category":"Places","sort_order":93,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"EUROPEAN CASTLE","unified":"1F3F0","variations":[],"docomo":null,"au":"EAF8","softbank":"E506","google":"FE4BF","image":"1f3f0.png","sheet_x":11,"sheet_y":37,"short_name":"european_castle","short_names":["european_castle"],"text":null,"texts":null,"category":"Places","sort_order":92,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WAVING WHITE FLAG","unified":"1F3F3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f3.png","sheet_x":11,"sheet_y":38,"short_name":"waving_white_flag","short_names":["waving_white_flag"],"text":null,"texts":null,"category":"Objects","sort_order":164,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WAVING BLACK FLAG","unified":"1F3F4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f4.png","sheet_x":11,"sheet_y":39,"short_name":"waving_black_flag","short_names":["waving_black_flag"],"text":null,"texts":null,"category":"Objects","sort_order":165,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ROSETTE","unified":"1F3F5","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f5.png","sheet_x":11,"sheet_y":40,"short_name":"rosette","short_names":["rosette"],"text":null,"texts":null,"category":"Activity","sort_order":37,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LABEL","unified":"1F3F7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f7.png","sheet_x":12,"sheet_y":0,"short_name":"label","short_names":["label"],"text":null,"texts":null,"category":"Objects","sort_order":84,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BADMINTON RACQUET AND SHUTTLECOCK","unified":"1F3F8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f8.png","sheet_x":12,"sheet_y":1,"short_name":"badminton_racquet_and_shuttlecock","short_names":["badminton_racquet_and_shuttlecock"],"text":null,"texts":null,"category":"Activity","sort_order":12,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BOW AND ARROW","unified":"1F3F9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3f9.png","sheet_x":12,"sheet_y":2,"short_name":"bow_and_arrow","short_names":["bow_and_arrow"],"text":null,"texts":null,"category":"Activity","sort_order":20,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"AMPHORA","unified":"1F3FA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3fa.png","sheet_x":12,"sheet_y":3,"short_name":"amphora","short_names":["amphora"],"text":null,"texts":null,"category":"Objects","sort_order":73,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"EMOJI MODIFIER FITZPATRICK TYPE-1-2","unified":"1F3FB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3fb.png","sheet_x":12,"sheet_y":4,"short_name":"skin-tone-2","short_names":["skin-tone-2"],"text":null,"texts":null,"category":null,"sort_order":null,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"EMOJI MODIFIER FITZPATRICK TYPE-3","unified":"1F3FC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3fc.png","sheet_x":12,"sheet_y":5,"short_name":"skin-tone-3","short_names":["skin-tone-3"],"text":null,"texts":null,"category":null,"sort_order":null,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"EMOJI MODIFIER FITZPATRICK TYPE-4","unified":"1F3FD","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3fd.png","sheet_x":12,"sheet_y":6,"short_name":"skin-tone-4","short_names":["skin-tone-4"],"text":null,"texts":null,"category":null,"sort_order":null,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"EMOJI MODIFIER FITZPATRICK TYPE-5","unified":"1F3FE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3fe.png","sheet_x":12,"sheet_y":7,"short_name":"skin-tone-5","short_names":["skin-tone-5"],"text":null,"texts":null,"category":null,"sort_order":null,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"EMOJI MODIFIER FITZPATRICK TYPE-6","unified":"1F3FF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f3ff.png","sheet_x":12,"sheet_y":8,"short_name":"skin-tone-6","short_names":["skin-tone-6"],"text":null,"texts":null,"category":null,"sort_order":null,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"RAT","unified":"1F400","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f400.png","sheet_x":12,"sheet_y":9,"short_name":"rat","short_names":["rat"],"text":null,"texts":null,"category":"Nature","sort_order":61,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MOUSE","unified":"1F401","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f401.png","sheet_x":12,"sheet_y":10,"short_name":"mouse2","short_names":["mouse2"],"text":null,"texts":null,"category":"Nature","sort_order":62,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"OX","unified":"1F402","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f402.png","sheet_x":12,"sheet_y":11,"short_name":"ox","short_names":["ox"],"text":null,"texts":null,"category":"Nature","sort_order":51,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WATER BUFFALO","unified":"1F403","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f403.png","sheet_x":12,"sheet_y":12,"short_name":"water_buffalo","short_names":["water_buffalo"],"text":null,"texts":null,"category":"Nature","sort_order":50,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"COW","unified":"1F404","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f404.png","sheet_x":12,"sheet_y":13,"short_name":"cow2","short_names":["cow2"],"text":null,"texts":null,"category":"Nature","sort_order":52,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TIGER","unified":"1F405","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f405.png","sheet_x":12,"sheet_y":14,"short_name":"tiger2","short_names":["tiger2"],"text":null,"texts":null,"category":"Nature","sort_order":49,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LEOPARD","unified":"1F406","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f406.png","sheet_x":12,"sheet_y":15,"short_name":"leopard","short_names":["leopard"],"text":null,"texts":null,"category":"Nature","sort_order":48,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RABBIT","unified":"1F407","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f407.png","sheet_x":12,"sheet_y":16,"short_name":"rabbit2","short_names":["rabbit2"],"text":null,"texts":null,"category":"Nature","sort_order":69,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CAT","unified":"1F408","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f408.png","sheet_x":12,"sheet_y":17,"short_name":"cat2","short_names":["cat2"],"text":null,"texts":null,"category":"Nature","sort_order":68,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DRAGON","unified":"1F409","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f409.png","sheet_x":12,"sheet_y":18,"short_name":"dragon","short_names":["dragon"],"text":null,"texts":null,"category":"Nature","sort_order":72,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CROCODILE","unified":"1F40A","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f40a.png","sheet_x":12,"sheet_y":19,"short_name":"crocodile","short_names":["crocodile"],"text":null,"texts":null,"category":"Nature","sort_order":47,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHALE","unified":"1F40B","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f40b.png","sheet_x":12,"sheet_y":20,"short_name":"whale2","short_names":["whale2"],"text":null,"texts":null,"category":"Nature","sort_order":46,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SNAIL","unified":"1F40C","variations":[],"docomo":"E74E","au":"EB7E","softbank":null,"google":"FE1B9","image":"1f40c.png","sheet_x":12,"sheet_y":21,"short_name":"snail","short_names":["snail"],"text":null,"texts":null,"category":"Nature","sort_order":33,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SNAKE","unified":"1F40D","variations":[],"docomo":null,"au":"EB22","softbank":"E52D","google":"FE1D3","image":"1f40d.png","sheet_x":12,"sheet_y":22,"short_name":"snake","short_names":["snake"],"text":null,"texts":null,"category":"Nature","sort_order":39,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HORSE","unified":"1F40E","variations":[],"docomo":"E754","au":"E4D8","softbank":"E134","google":"FE7DC","image":"1f40e.png","sheet_x":12,"sheet_y":23,"short_name":"racehorse","short_names":["racehorse"],"text":null,"texts":null,"category":"Nature","sort_order":59,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RAM","unified":"1F40F","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f40f.png","sheet_x":12,"sheet_y":24,"short_name":"ram","short_names":["ram"],"text":null,"texts":null,"category":"Nature","sort_order":57,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GOAT","unified":"1F410","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f410.png","sheet_x":12,"sheet_y":25,"short_name":"goat","short_names":["goat"],"text":null,"texts":null,"category":"Nature","sort_order":56,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SHEEP","unified":"1F411","variations":[],"docomo":null,"au":"E48F","softbank":"E529","google":"FE1CF","image":"1f411.png","sheet_x":12,"sheet_y":26,"short_name":"sheep","short_names":["sheep"],"text":null,"texts":null,"category":"Nature","sort_order":58,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MONKEY","unified":"1F412","variations":[],"docomo":null,"au":"E4D9","softbank":"E528","google":"FE1CE","image":"1f412.png","sheet_x":12,"sheet_y":27,"short_name":"monkey","short_names":["monkey"],"text":null,"texts":null,"category":"Nature","sort_order":20,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ROOSTER","unified":"1F413","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f413.png","sheet_x":12,"sheet_y":28,"short_name":"rooster","short_names":["rooster"],"text":null,"texts":null,"category":"Nature","sort_order":63,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CHICKEN","unified":"1F414","variations":[],"docomo":null,"au":"EB23","softbank":"E52E","google":"FE1D4","image":"1f414.png","sheet_x":12,"sheet_y":29,"short_name":"chicken","short_names":["chicken"],"text":null,"texts":null,"category":"Nature","sort_order":21,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DOG","unified":"1F415","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f415.png","sheet_x":12,"sheet_y":30,"short_name":"dog2","short_names":["dog2"],"text":null,"texts":null,"category":"Nature","sort_order":66,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PIG","unified":"1F416","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f416.png","sheet_x":12,"sheet_y":31,"short_name":"pig2","short_names":["pig2"],"text":null,"texts":null,"category":"Nature","sort_order":60,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BOAR","unified":"1F417","variations":[],"docomo":null,"au":"EB24","softbank":"E52F","google":"FE1D5","image":"1f417.png","sheet_x":12,"sheet_y":32,"short_name":"boar","short_names":["boar"],"text":null,"texts":null,"category":"Nature","sort_order":28,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ELEPHANT","unified":"1F418","variations":[],"docomo":null,"au":"EB1F","softbank":"E526","google":"FE1CC","image":"1f418.png","sheet_x":12,"sheet_y":33,"short_name":"elephant","short_names":["elephant"],"text":null,"texts":null,"category":"Nature","sort_order":55,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"OCTOPUS","unified":"1F419","variations":[],"docomo":null,"au":"E5C7","softbank":"E10A","google":"FE1C5","image":"1f419.png","sheet_x":12,"sheet_y":34,"short_name":"octopus","short_names":["octopus"],"text":null,"texts":null,"category":"Nature","sort_order":15,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPIRAL SHELL","unified":"1F41A","variations":[],"docomo":null,"au":"EAEC","softbank":"E441","google":"FE1C6","image":"1f41a.png","sheet_x":12,"sheet_y":35,"short_name":"shell","short_names":["shell"],"text":null,"texts":null,"category":"Nature","sort_order":99,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BUG","unified":"1F41B","variations":[],"docomo":null,"au":"EB1E","softbank":"E525","google":"FE1CB","image":"1f41b.png","sheet_x":12,"sheet_y":36,"short_name":"bug","short_names":["bug"],"text":null,"texts":null,"category":"Nature","sort_order":32,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ANT","unified":"1F41C","variations":[],"docomo":null,"au":"E4DD","softbank":null,"google":"FE1DA","image":"1f41c.png","sheet_x":12,"sheet_y":37,"short_name":"ant","short_names":["ant"],"text":null,"texts":null,"category":"Nature","sort_order":35,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HONEYBEE","unified":"1F41D","variations":[],"docomo":null,"au":"EB57","softbank":null,"google":"FE1E1","image":"1f41d.png","sheet_x":12,"sheet_y":38,"short_name":"bee","short_names":["bee","honeybee"],"text":null,"texts":null,"category":"Nature","sort_order":31,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LADY BEETLE","unified":"1F41E","variations":[],"docomo":null,"au":"EB58","softbank":null,"google":"FE1E2","image":"1f41e.png","sheet_x":12,"sheet_y":39,"short_name":"beetle","short_names":["beetle"],"text":null,"texts":null,"category":"Nature","sort_order":34,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FISH","unified":"1F41F","variations":[],"docomo":"E751","au":"E49A","softbank":"E019","google":"FE1BD","image":"1f41f.png","sheet_x":12,"sheet_y":40,"short_name":"fish","short_names":["fish"],"text":null,"texts":null,"category":"Nature","sort_order":42,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TROPICAL FISH","unified":"1F420","variations":[],"docomo":"E751","au":"EB1D","softbank":"E522","google":"FE1C9","image":"1f420.png","sheet_x":13,"sheet_y":0,"short_name":"tropical_fish","short_names":["tropical_fish"],"text":null,"texts":null,"category":"Nature","sort_order":41,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLOWFISH","unified":"1F421","variations":[],"docomo":"E751","au":"E4D3","softbank":"E019","google":"FE1D9","image":"1f421.png","sheet_x":13,"sheet_y":1,"short_name":"blowfish","short_names":["blowfish"],"text":null,"texts":null,"category":"Nature","sort_order":43,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TURTLE","unified":"1F422","variations":[],"docomo":null,"au":"E5D4","softbank":null,"google":"FE1DC","image":"1f422.png","sheet_x":13,"sheet_y":2,"short_name":"turtle","short_names":["turtle"],"text":null,"texts":null,"category":"Nature","sort_order":40,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HATCHING CHICK","unified":"1F423","variations":[],"docomo":"E74F","au":"E5DB","softbank":"E523","google":"FE1DD","image":"1f423.png","sheet_x":13,"sheet_y":3,"short_name":"hatching_chick","short_names":["hatching_chick"],"text":null,"texts":null,"category":"Nature","sort_order":25,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BABY CHICK","unified":"1F424","variations":[],"docomo":"E74F","au":"E4E0","softbank":"E523","google":"FE1BA","image":"1f424.png","sheet_x":13,"sheet_y":4,"short_name":"baby_chick","short_names":["baby_chick"],"text":null,"texts":null,"category":"Nature","sort_order":24,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FRONT-FACING BABY CHICK","unified":"1F425","variations":[],"docomo":"E74F","au":"EB76","softbank":"E523","google":"FE1BB","image":"1f425.png","sheet_x":13,"sheet_y":5,"short_name":"hatched_chick","short_names":["hatched_chick"],"text":null,"texts":null,"category":"Nature","sort_order":26,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BIRD","unified":"1F426","variations":[],"docomo":"E74F","au":"E4E0","softbank":"E521","google":"FE1C8","image":"1f426.png","sheet_x":13,"sheet_y":6,"short_name":"bird","short_names":["bird"],"text":null,"texts":null,"category":"Nature","sort_order":23,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PENGUIN","unified":"1F427","variations":[],"docomo":"E750","au":"E4DC","softbank":"E055","google":"FE1BC","image":"1f427.png","sheet_x":13,"sheet_y":7,"short_name":"penguin","short_names":["penguin"],"text":null,"texts":null,"category":"Nature","sort_order":22,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KOALA","unified":"1F428","variations":[],"docomo":null,"au":"EB20","softbank":"E527","google":"FE1CD","image":"1f428.png","sheet_x":13,"sheet_y":8,"short_name":"koala","short_names":["koala"],"text":null,"texts":null,"category":"Nature","sort_order":8,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"POODLE","unified":"1F429","variations":[],"docomo":"E6A1","au":"E4DF","softbank":"E052","google":"FE1D8","image":"1f429.png","sheet_x":13,"sheet_y":9,"short_name":"poodle","short_names":["poodle"],"text":null,"texts":null,"category":"Nature","sort_order":67,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DROMEDARY CAMEL","unified":"1F42A","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f42a.png","sheet_x":13,"sheet_y":10,"short_name":"dromedary_camel","short_names":["dromedary_camel"],"text":null,"texts":null,"category":"Nature","sort_order":53,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BACTRIAN CAMEL","unified":"1F42B","variations":[],"docomo":null,"au":"EB25","softbank":"E530","google":"FE1D6","image":"1f42b.png","sheet_x":13,"sheet_y":11,"short_name":"camel","short_names":["camel"],"text":null,"texts":null,"category":"Nature","sort_order":54,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DOLPHIN","unified":"1F42C","variations":[],"docomo":null,"au":"EB1B","softbank":"E520","google":"FE1C7","image":"1f42c.png","sheet_x":13,"sheet_y":12,"short_name":"dolphin","short_names":["dolphin","flipper"],"text":null,"texts":null,"category":"Nature","sort_order":44,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MOUSE FACE","unified":"1F42D","variations":[],"docomo":null,"au":"E5C2","softbank":"E053","google":"FE1C2","image":"1f42d.png","sheet_x":13,"sheet_y":13,"short_name":"mouse","short_names":["mouse"],"text":null,"texts":null,"category":"Nature","sort_order":3,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"COW FACE","unified":"1F42E","variations":[],"docomo":null,"au":"EB21","softbank":"E52B","google":"FE1D1","image":"1f42e.png","sheet_x":13,"sheet_y":14,"short_name":"cow","short_names":["cow"],"text":null,"texts":null,"category":"Nature","sort_order":11,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TIGER FACE","unified":"1F42F","variations":[],"docomo":null,"au":"E5C0","softbank":"E050","google":"FE1C0","image":"1f42f.png","sheet_x":13,"sheet_y":15,"short_name":"tiger","short_names":["tiger"],"text":null,"texts":null,"category":"Nature","sort_order":9,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RABBIT FACE","unified":"1F430","variations":[],"docomo":null,"au":"E4D7","softbank":"E52C","google":"FE1D2","image":"1f430.png","sheet_x":13,"sheet_y":16,"short_name":"rabbit","short_names":["rabbit"],"text":null,"texts":null,"category":"Nature","sort_order":5,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CAT FACE","unified":"1F431","variations":[],"docomo":"E6A2","au":"E4DB","softbank":"E04F","google":"FE1B8","image":"1f431.png","sheet_x":13,"sheet_y":17,"short_name":"cat","short_names":["cat"],"text":null,"texts":null,"category":"Nature","sort_order":2,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DRAGON FACE","unified":"1F432","variations":[],"docomo":null,"au":"EB3F","softbank":null,"google":"FE1DE","image":"1f432.png","sheet_x":13,"sheet_y":18,"short_name":"dragon_face","short_names":["dragon_face"],"text":null,"texts":null,"category":"Nature","sort_order":73,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPOUTING WHALE","unified":"1F433","variations":[],"docomo":null,"au":"E470","softbank":"E054","google":"FE1C3","image":"1f433.png","sheet_x":13,"sheet_y":19,"short_name":"whale","short_names":["whale"],"text":null,"texts":null,"category":"Nature","sort_order":45,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HORSE FACE","unified":"1F434","variations":[],"docomo":"E754","au":"E4D8","softbank":"E01A","google":"FE1BE","image":"1f434.png","sheet_x":13,"sheet_y":20,"short_name":"horse","short_names":["horse"],"text":null,"texts":null,"category":"Nature","sort_order":29,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MONKEY FACE","unified":"1F435","variations":[],"docomo":null,"au":"E4D9","softbank":"E109","google":"FE1C4","image":"1f435.png","sheet_x":13,"sheet_y":21,"short_name":"monkey_face","short_names":["monkey_face"],"text":null,"texts":[":o)"],"category":"Nature","sort_order":16,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DOG FACE","unified":"1F436","variations":[],"docomo":"E6A1","au":"E4E1","softbank":"E052","google":"FE1B7","image":"1f436.png","sheet_x":13,"sheet_y":22,"short_name":"dog","short_names":["dog"],"text":null,"texts":null,"category":"Nature","sort_order":1,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PIG FACE","unified":"1F437","variations":[],"docomo":"E755","au":"E4DE","softbank":"E10B","google":"FE1BF","image":"1f437.png","sheet_x":13,"sheet_y":23,"short_name":"pig","short_names":["pig"],"text":null,"texts":null,"category":"Nature","sort_order":12,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FROG FACE","unified":"1F438","variations":[],"docomo":null,"au":"E4DA","softbank":"E531","google":"FE1D7","image":"1f438.png","sheet_x":13,"sheet_y":24,"short_name":"frog","short_names":["frog"],"text":null,"texts":null,"category":"Nature","sort_order":14,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HAMSTER FACE","unified":"1F439","variations":[],"docomo":null,"au":null,"softbank":"E524","google":"FE1CA","image":"1f439.png","sheet_x":13,"sheet_y":25,"short_name":"hamster","short_names":["hamster"],"text":null,"texts":null,"category":"Nature","sort_order":4,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WOLF FACE","unified":"1F43A","variations":[],"docomo":"E6A1","au":"E4E1","softbank":"E52A","google":"FE1D0","image":"1f43a.png","sheet_x":13,"sheet_y":26,"short_name":"wolf","short_names":["wolf"],"text":null,"texts":null,"category":"Nature","sort_order":27,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BEAR FACE","unified":"1F43B","variations":[],"docomo":null,"au":"E5C1","softbank":"E051","google":"FE1C1","image":"1f43b.png","sheet_x":13,"sheet_y":27,"short_name":"bear","short_names":["bear"],"text":null,"texts":null,"category":"Nature","sort_order":6,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PANDA FACE","unified":"1F43C","variations":[],"docomo":null,"au":"EB46","softbank":null,"google":"FE1DF","image":"1f43c.png","sheet_x":13,"sheet_y":28,"short_name":"panda_face","short_names":["panda_face"],"text":null,"texts":null,"category":"Nature","sort_order":7,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PIG NOSE","unified":"1F43D","variations":[],"docomo":"E755","au":"EB48","softbank":"E10B","google":"FE1E0","image":"1f43d.png","sheet_x":13,"sheet_y":29,"short_name":"pig_nose","short_names":["pig_nose"],"text":null,"texts":null,"category":"Nature","sort_order":13,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PAW PRINTS","unified":"1F43E","variations":[],"docomo":"E698","au":"E4EE","softbank":"E536","google":"FE1DB","image":"1f43e.png","sheet_x":13,"sheet_y":30,"short_name":"feet","short_names":["feet","paw_prints"],"text":null,"texts":null,"category":"Nature","sort_order":71,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CHIPMUNK","unified":"1F43F","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f43f.png","sheet_x":13,"sheet_y":31,"short_name":"chipmunk","short_names":["chipmunk"],"text":null,"texts":null,"category":"Nature","sort_order":70,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"EYES","unified":"1F440","variations":[],"docomo":"E691","au":"E5A4","softbank":"E419","google":"FE190","image":"1f440.png","sheet_x":13,"sheet_y":32,"short_name":"eyes","short_names":["eyes"],"text":null,"texts":null,"category":"People","sort_order":117,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"EYE","unified":"1F441","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f441.png","sheet_x":13,"sheet_y":33,"short_name":"eye","short_names":["eye"],"text":null,"texts":null,"category":"People","sort_order":116,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"EAR","unified":"1F442","variations":[],"docomo":"E692","au":"E5A5","softbank":"E41B","google":"FE191","image":"1f442.png","sheet_x":13,"sheet_y":34,"short_name":"ear","short_names":["ear"],"text":null,"texts":null,"category":"People","sort_order":114,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F442-1F3FB":{"unified":"1F442-1F3FB","image":"1f442-1f3fb.png","sheet_x":13,"sheet_y":35,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F442-1F3FC":{"unified":"1F442-1F3FC","image":"1f442-1f3fc.png","sheet_x":13,"sheet_y":36,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F442-1F3FD":{"unified":"1F442-1F3FD","image":"1f442-1f3fd.png","sheet_x":13,"sheet_y":37,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F442-1F3FE":{"unified":"1F442-1F3FE","image":"1f442-1f3fe.png","sheet_x":13,"sheet_y":38,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F442-1F3FF":{"unified":"1F442-1F3FF","image":"1f442-1f3ff.png","sheet_x":13,"sheet_y":39,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"NOSE","unified":"1F443","variations":[],"docomo":null,"au":"EAD0","softbank":"E41A","google":"FE192","image":"1f443.png","sheet_x":13,"sheet_y":40,"short_name":"nose","short_names":["nose"],"text":null,"texts":null,"category":"People","sort_order":115,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F443-1F3FB":{"unified":"1F443-1F3FB","image":"1f443-1f3fb.png","sheet_x":14,"sheet_y":0,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F443-1F3FC":{"unified":"1F443-1F3FC","image":"1f443-1f3fc.png","sheet_x":14,"sheet_y":1,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F443-1F3FD":{"unified":"1F443-1F3FD","image":"1f443-1f3fd.png","sheet_x":14,"sheet_y":2,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F443-1F3FE":{"unified":"1F443-1F3FE","image":"1f443-1f3fe.png","sheet_x":14,"sheet_y":3,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F443-1F3FF":{"unified":"1F443-1F3FF","image":"1f443-1f3ff.png","sheet_x":14,"sheet_y":4,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"MOUTH","unified":"1F444","variations":[],"docomo":"E6F9","au":"EAD1","softbank":"E41C","google":"FE193","image":"1f444.png","sheet_x":14,"sheet_y":5,"short_name":"lips","short_names":["lips"],"text":null,"texts":null,"category":"People","sort_order":112,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TONGUE","unified":"1F445","variations":[],"docomo":"E728","au":"EB47","softbank":"E409","google":"FE194","image":"1f445.png","sheet_x":14,"sheet_y":6,"short_name":"tongue","short_names":["tongue"],"text":null,"texts":null,"category":"People","sort_order":113,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHITE UP POINTING BACKHAND INDEX","unified":"1F446","variations":[],"docomo":null,"au":"EA8D","softbank":"E22E","google":"FEB99","image":"1f446.png","sheet_x":14,"sheet_y":7,"short_name":"point_up_2","short_names":["point_up_2"],"text":null,"texts":null,"category":"People","sort_order":102,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F446-1F3FB":{"unified":"1F446-1F3FB","image":"1f446-1f3fb.png","sheet_x":14,"sheet_y":8,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F446-1F3FC":{"unified":"1F446-1F3FC","image":"1f446-1f3fc.png","sheet_x":14,"sheet_y":9,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F446-1F3FD":{"unified":"1F446-1F3FD","image":"1f446-1f3fd.png","sheet_x":14,"sheet_y":10,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F446-1F3FE":{"unified":"1F446-1F3FE","image":"1f446-1f3fe.png","sheet_x":14,"sheet_y":11,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F446-1F3FF":{"unified":"1F446-1F3FF","image":"1f446-1f3ff.png","sheet_x":14,"sheet_y":12,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"WHITE DOWN POINTING BACKHAND INDEX","unified":"1F447","variations":[],"docomo":null,"au":"EA8E","softbank":"E22F","google":"FEB9A","image":"1f447.png","sheet_x":14,"sheet_y":13,"short_name":"point_down","short_names":["point_down"],"text":null,"texts":null,"category":"People","sort_order":103,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F447-1F3FB":{"unified":"1F447-1F3FB","image":"1f447-1f3fb.png","sheet_x":14,"sheet_y":14,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F447-1F3FC":{"unified":"1F447-1F3FC","image":"1f447-1f3fc.png","sheet_x":14,"sheet_y":15,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F447-1F3FD":{"unified":"1F447-1F3FD","image":"1f447-1f3fd.png","sheet_x":14,"sheet_y":16,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F447-1F3FE":{"unified":"1F447-1F3FE","image":"1f447-1f3fe.png","sheet_x":14,"sheet_y":17,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F447-1F3FF":{"unified":"1F447-1F3FF","image":"1f447-1f3ff.png","sheet_x":14,"sheet_y":18,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"WHITE LEFT POINTING BACKHAND INDEX","unified":"1F448","variations":[],"docomo":null,"au":"E4FF","softbank":"E230","google":"FEB9B","image":"1f448.png","sheet_x":14,"sheet_y":19,"short_name":"point_left","short_names":["point_left"],"text":null,"texts":null,"category":"People","sort_order":104,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F448-1F3FB":{"unified":"1F448-1F3FB","image":"1f448-1f3fb.png","sheet_x":14,"sheet_y":20,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F448-1F3FC":{"unified":"1F448-1F3FC","image":"1f448-1f3fc.png","sheet_x":14,"sheet_y":21,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F448-1F3FD":{"unified":"1F448-1F3FD","image":"1f448-1f3fd.png","sheet_x":14,"sheet_y":22,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F448-1F3FE":{"unified":"1F448-1F3FE","image":"1f448-1f3fe.png","sheet_x":14,"sheet_y":23,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F448-1F3FF":{"unified":"1F448-1F3FF","image":"1f448-1f3ff.png","sheet_x":14,"sheet_y":24,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"WHITE RIGHT POINTING BACKHAND INDEX","unified":"1F449","variations":[],"docomo":null,"au":"E500","softbank":"E231","google":"FEB9C","image":"1f449.png","sheet_x":14,"sheet_y":25,"short_name":"point_right","short_names":["point_right"],"text":null,"texts":null,"category":"People","sort_order":105,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F449-1F3FB":{"unified":"1F449-1F3FB","image":"1f449-1f3fb.png","sheet_x":14,"sheet_y":26,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F449-1F3FC":{"unified":"1F449-1F3FC","image":"1f449-1f3fc.png","sheet_x":14,"sheet_y":27,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F449-1F3FD":{"unified":"1F449-1F3FD","image":"1f449-1f3fd.png","sheet_x":14,"sheet_y":28,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F449-1F3FE":{"unified":"1F449-1F3FE","image":"1f449-1f3fe.png","sheet_x":14,"sheet_y":29,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F449-1F3FF":{"unified":"1F449-1F3FF","image":"1f449-1f3ff.png","sheet_x":14,"sheet_y":30,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"FISTED HAND SIGN","unified":"1F44A","variations":[],"docomo":"E6FD","au":"E4F3","softbank":"E00D","google":"FEB96","image":"1f44a.png","sheet_x":14,"sheet_y":31,"short_name":"facepunch","short_names":["facepunch","punch"],"text":null,"texts":null,"category":"People","sort_order":93,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F44A-1F3FB":{"unified":"1F44A-1F3FB","image":"1f44a-1f3fb.png","sheet_x":14,"sheet_y":32,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44A-1F3FC":{"unified":"1F44A-1F3FC","image":"1f44a-1f3fc.png","sheet_x":14,"sheet_y":33,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44A-1F3FD":{"unified":"1F44A-1F3FD","image":"1f44a-1f3fd.png","sheet_x":14,"sheet_y":34,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44A-1F3FE":{"unified":"1F44A-1F3FE","image":"1f44a-1f3fe.png","sheet_x":14,"sheet_y":35,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44A-1F3FF":{"unified":"1F44A-1F3FF","image":"1f44a-1f3ff.png","sheet_x":14,"sheet_y":36,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"WAVING HAND SIGN","unified":"1F44B","variations":[],"docomo":"E695","au":"EAD6","softbank":"E41E","google":"FEB9D","image":"1f44b.png","sheet_x":14,"sheet_y":37,"short_name":"wave","short_names":["wave"],"text":null,"texts":null,"category":"People","sort_order":90,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F44B-1F3FB":{"unified":"1F44B-1F3FB","image":"1f44b-1f3fb.png","sheet_x":14,"sheet_y":38,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44B-1F3FC":{"unified":"1F44B-1F3FC","image":"1f44b-1f3fc.png","sheet_x":14,"sheet_y":39,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44B-1F3FD":{"unified":"1F44B-1F3FD","image":"1f44b-1f3fd.png","sheet_x":14,"sheet_y":40,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44B-1F3FE":{"unified":"1F44B-1F3FE","image":"1f44b-1f3fe.png","sheet_x":15,"sheet_y":0,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44B-1F3FF":{"unified":"1F44B-1F3FF","image":"1f44b-1f3ff.png","sheet_x":15,"sheet_y":1,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"OK HAND SIGN","unified":"1F44C","variations":[],"docomo":"E70B","au":"EAD4","softbank":"E420","google":"FEB9F","image":"1f44c.png","sheet_x":15,"sheet_y":2,"short_name":"ok_hand","short_names":["ok_hand"],"text":null,"texts":null,"category":"People","sort_order":96,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F44C-1F3FB":{"unified":"1F44C-1F3FB","image":"1f44c-1f3fb.png","sheet_x":15,"sheet_y":3,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44C-1F3FC":{"unified":"1F44C-1F3FC","image":"1f44c-1f3fc.png","sheet_x":15,"sheet_y":4,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44C-1F3FD":{"unified":"1F44C-1F3FD","image":"1f44c-1f3fd.png","sheet_x":15,"sheet_y":5,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44C-1F3FE":{"unified":"1F44C-1F3FE","image":"1f44c-1f3fe.png","sheet_x":15,"sheet_y":6,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44C-1F3FF":{"unified":"1F44C-1F3FF","image":"1f44c-1f3ff.png","sheet_x":15,"sheet_y":7,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"THUMBS UP SIGN","unified":"1F44D","variations":[],"docomo":"E727","au":"E4F9","softbank":"E00E","google":"FEB97","image":"1f44d.png","sheet_x":15,"sheet_y":8,"short_name":"+1","short_names":["+1","thumbsup"],"text":null,"texts":null,"category":"People","sort_order":91,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F44D-1F3FB":{"unified":"1F44D-1F3FB","image":"1f44d-1f3fb.png","sheet_x":15,"sheet_y":9,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44D-1F3FC":{"unified":"1F44D-1F3FC","image":"1f44d-1f3fc.png","sheet_x":15,"sheet_y":10,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44D-1F3FD":{"unified":"1F44D-1F3FD","image":"1f44d-1f3fd.png","sheet_x":15,"sheet_y":11,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44D-1F3FE":{"unified":"1F44D-1F3FE","image":"1f44d-1f3fe.png","sheet_x":15,"sheet_y":12,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44D-1F3FF":{"unified":"1F44D-1F3FF","image":"1f44d-1f3ff.png","sheet_x":15,"sheet_y":13,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"THUMBS DOWN SIGN","unified":"1F44E","variations":[],"docomo":"E700","au":"EAD5","softbank":"E421","google":"FEBA0","image":"1f44e.png","sheet_x":15,"sheet_y":14,"short_name":"-1","short_names":["-1","thumbsdown"],"text":null,"texts":null,"category":"People","sort_order":92,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F44E-1F3FB":{"unified":"1F44E-1F3FB","image":"1f44e-1f3fb.png","sheet_x":15,"sheet_y":15,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44E-1F3FC":{"unified":"1F44E-1F3FC","image":"1f44e-1f3fc.png","sheet_x":15,"sheet_y":16,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44E-1F3FD":{"unified":"1F44E-1F3FD","image":"1f44e-1f3fd.png","sheet_x":15,"sheet_y":17,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44E-1F3FE":{"unified":"1F44E-1F3FE","image":"1f44e-1f3fe.png","sheet_x":15,"sheet_y":18,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44E-1F3FF":{"unified":"1F44E-1F3FF","image":"1f44e-1f3ff.png","sheet_x":15,"sheet_y":19,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"CLAPPING HANDS SIGN","unified":"1F44F","variations":[],"docomo":null,"au":"EAD3","softbank":"E41F","google":"FEB9E","image":"1f44f.png","sheet_x":15,"sheet_y":20,"short_name":"clap","short_names":["clap"],"text":null,"texts":null,"category":"People","sort_order":89,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F44F-1F3FB":{"unified":"1F44F-1F3FB","image":"1f44f-1f3fb.png","sheet_x":15,"sheet_y":21,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44F-1F3FC":{"unified":"1F44F-1F3FC","image":"1f44f-1f3fc.png","sheet_x":15,"sheet_y":22,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44F-1F3FD":{"unified":"1F44F-1F3FD","image":"1f44f-1f3fd.png","sheet_x":15,"sheet_y":23,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44F-1F3FE":{"unified":"1F44F-1F3FE","image":"1f44f-1f3fe.png","sheet_x":15,"sheet_y":24,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F44F-1F3FF":{"unified":"1F44F-1F3FF","image":"1f44f-1f3ff.png","sheet_x":15,"sheet_y":25,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"OPEN HANDS SIGN","unified":"1F450","variations":[],"docomo":"E695","au":"EAD6","softbank":"E422","google":"FEBA1","image":"1f450.png","sheet_x":15,"sheet_y":26,"short_name":"open_hands","short_names":["open_hands"],"text":null,"texts":null,"category":"People","sort_order":98,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F450-1F3FB":{"unified":"1F450-1F3FB","image":"1f450-1f3fb.png","sheet_x":15,"sheet_y":27,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F450-1F3FC":{"unified":"1F450-1F3FC","image":"1f450-1f3fc.png","sheet_x":15,"sheet_y":28,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F450-1F3FD":{"unified":"1F450-1F3FD","image":"1f450-1f3fd.png","sheet_x":15,"sheet_y":29,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F450-1F3FE":{"unified":"1F450-1F3FE","image":"1f450-1f3fe.png","sheet_x":15,"sheet_y":30,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F450-1F3FF":{"unified":"1F450-1F3FF","image":"1f450-1f3ff.png","sheet_x":15,"sheet_y":31,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"CROWN","unified":"1F451","variations":[],"docomo":"E71A","au":"E5C9","softbank":"E10E","google":"FE4D1","image":"1f451.png","sheet_x":15,"sheet_y":32,"short_name":"crown","short_names":["crown"],"text":null,"texts":null,"category":"People","sort_order":195,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WOMANS HAT","unified":"1F452","variations":[],"docomo":null,"au":"EA9E","softbank":"E318","google":"FE4D4","image":"1f452.png","sheet_x":15,"sheet_y":33,"short_name":"womans_hat","short_names":["womans_hat"],"text":null,"texts":null,"category":"People","sort_order":191,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"EYEGLASSES","unified":"1F453","variations":[],"docomo":"E69A","au":"E4FE","softbank":null,"google":"FE4CE","image":"1f453.png","sheet_x":15,"sheet_y":34,"short_name":"eyeglasses","short_names":["eyeglasses"],"text":null,"texts":null,"category":"People","sort_order":201,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NECKTIE","unified":"1F454","variations":[],"docomo":null,"au":"EA93","softbank":"E302","google":"FE4D3","image":"1f454.png","sheet_x":15,"sheet_y":35,"short_name":"necktie","short_names":["necktie"],"text":null,"texts":null,"category":"People","sort_order":179,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"T-SHIRT","unified":"1F455","variations":[],"docomo":"E70E","au":"E5B6","softbank":"E006","google":"FE4CF","image":"1f455.png","sheet_x":15,"sheet_y":36,"short_name":"shirt","short_names":["shirt","tshirt"],"text":null,"texts":null,"category":"People","sort_order":177,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"JEANS","unified":"1F456","variations":[],"docomo":"E711","au":"EB77","softbank":null,"google":"FE4D0","image":"1f456.png","sheet_x":15,"sheet_y":37,"short_name":"jeans","short_names":["jeans"],"text":null,"texts":null,"category":"People","sort_order":178,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DRESS","unified":"1F457","variations":[],"docomo":null,"au":"EB6B","softbank":"E319","google":"FE4D5","image":"1f457.png","sheet_x":15,"sheet_y":38,"short_name":"dress","short_names":["dress"],"text":null,"texts":null,"category":"People","sort_order":180,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KIMONO","unified":"1F458","variations":[],"docomo":null,"au":"EAA3","softbank":"E321","google":"FE4D9","image":"1f458.png","sheet_x":15,"sheet_y":39,"short_name":"kimono","short_names":["kimono"],"text":null,"texts":null,"category":"People","sort_order":182,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BIKINI","unified":"1F459","variations":[],"docomo":null,"au":"EAA4","softbank":"E322","google":"FE4DA","image":"1f459.png","sheet_x":15,"sheet_y":40,"short_name":"bikini","short_names":["bikini"],"text":null,"texts":null,"category":"People","sort_order":181,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WOMANS CLOTHES","unified":"1F45A","variations":[],"docomo":"E70E","au":"E50D","softbank":"E006","google":"FE4DB","image":"1f45a.png","sheet_x":16,"sheet_y":0,"short_name":"womans_clothes","short_names":["womans_clothes"],"text":null,"texts":null,"category":"People","sort_order":176,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PURSE","unified":"1F45B","variations":[],"docomo":"E70F","au":"E504","softbank":null,"google":"FE4DC","image":"1f45b.png","sheet_x":16,"sheet_y":1,"short_name":"purse","short_names":["purse"],"text":null,"texts":null,"category":"People","sort_order":198,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HANDBAG","unified":"1F45C","variations":[],"docomo":"E682","au":"E49C","softbank":"E323","google":"FE4F0","image":"1f45c.png","sheet_x":16,"sheet_y":2,"short_name":"handbag","short_names":["handbag"],"text":null,"texts":null,"category":"People","sort_order":199,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"POUCH","unified":"1F45D","variations":[],"docomo":"E6AD","au":null,"softbank":null,"google":"FE4F1","image":"1f45d.png","sheet_x":16,"sheet_y":3,"short_name":"pouch","short_names":["pouch"],"text":null,"texts":null,"category":"People","sort_order":197,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MANS SHOE","unified":"1F45E","variations":[],"docomo":"E699","au":"E5B7","softbank":"E007","google":"FE4CC","image":"1f45e.png","sheet_x":16,"sheet_y":4,"short_name":"mans_shoe","short_names":["mans_shoe","shoe"],"text":null,"texts":null,"category":"People","sort_order":189,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ATHLETIC SHOE","unified":"1F45F","variations":[],"docomo":"E699","au":"EB2B","softbank":"E007","google":"FE4CD","image":"1f45f.png","sheet_x":16,"sheet_y":5,"short_name":"athletic_shoe","short_names":["athletic_shoe"],"text":null,"texts":null,"category":"People","sort_order":190,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HIGH-HEELED SHOE","unified":"1F460","variations":[],"docomo":"E674","au":"E51A","softbank":"E13E","google":"FE4D6","image":"1f460.png","sheet_x":16,"sheet_y":6,"short_name":"high_heel","short_names":["high_heel"],"text":null,"texts":null,"category":"People","sort_order":186,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WOMANS SANDAL","unified":"1F461","variations":[],"docomo":"E674","au":"E51A","softbank":"E31A","google":"FE4D7","image":"1f461.png","sheet_x":16,"sheet_y":7,"short_name":"sandal","short_names":["sandal"],"text":null,"texts":null,"category":"People","sort_order":187,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WOMANS BOOTS","unified":"1F462","variations":[],"docomo":null,"au":"EA9F","softbank":"E31B","google":"FE4D8","image":"1f462.png","sheet_x":16,"sheet_y":8,"short_name":"boot","short_names":["boot"],"text":null,"texts":null,"category":"People","sort_order":188,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FOOTPRINTS","unified":"1F463","variations":[],"docomo":"E698","au":"EB2A","softbank":"E536","google":"FE553","image":"1f463.png","sheet_x":16,"sheet_y":9,"short_name":"footprints","short_names":["footprints"],"text":null,"texts":null,"category":"People","sort_order":185,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BUST IN SILHOUETTE","unified":"1F464","variations":[],"docomo":"E6B1","au":null,"softbank":null,"google":"FE19A","image":"1f464.png","sheet_x":16,"sheet_y":10,"short_name":"bust_in_silhouette","short_names":["bust_in_silhouette"],"text":null,"texts":null,"category":"People","sort_order":118,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BUSTS IN SILHOUETTE","unified":"1F465","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f465.png","sheet_x":16,"sheet_y":11,"short_name":"busts_in_silhouette","short_names":["busts_in_silhouette"],"text":null,"texts":null,"category":"People","sort_order":119,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BOY","unified":"1F466","variations":[],"docomo":"E6F0","au":"E4FC","softbank":"E001","google":"FE19B","image":"1f466.png","sheet_x":16,"sheet_y":12,"short_name":"boy","short_names":["boy"],"text":null,"texts":null,"category":"People","sort_order":122,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F466-1F3FB":{"unified":"1F466-1F3FB","image":"1f466-1f3fb.png","sheet_x":16,"sheet_y":13,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F466-1F3FC":{"unified":"1F466-1F3FC","image":"1f466-1f3fc.png","sheet_x":16,"sheet_y":14,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F466-1F3FD":{"unified":"1F466-1F3FD","image":"1f466-1f3fd.png","sheet_x":16,"sheet_y":15,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F466-1F3FE":{"unified":"1F466-1F3FE","image":"1f466-1f3fe.png","sheet_x":16,"sheet_y":16,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F466-1F3FF":{"unified":"1F466-1F3FF","image":"1f466-1f3ff.png","sheet_x":16,"sheet_y":17,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"GIRL","unified":"1F467","variations":[],"docomo":"E6F0","au":"E4FA","softbank":"E002","google":"FE19C","image":"1f467.png","sheet_x":16,"sheet_y":18,"short_name":"girl","short_names":["girl"],"text":null,"texts":null,"category":"People","sort_order":123,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F467-1F3FB":{"unified":"1F467-1F3FB","image":"1f467-1f3fb.png","sheet_x":16,"sheet_y":19,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F467-1F3FC":{"unified":"1F467-1F3FC","image":"1f467-1f3fc.png","sheet_x":16,"sheet_y":20,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F467-1F3FD":{"unified":"1F467-1F3FD","image":"1f467-1f3fd.png","sheet_x":16,"sheet_y":21,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F467-1F3FE":{"unified":"1F467-1F3FE","image":"1f467-1f3fe.png","sheet_x":16,"sheet_y":22,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F467-1F3FF":{"unified":"1F467-1F3FF","image":"1f467-1f3ff.png","sheet_x":16,"sheet_y":23,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"MAN","unified":"1F468","variations":[],"docomo":"E6F0","au":"E4FC","softbank":"E004","google":"FE19D","image":"1f468.png","sheet_x":16,"sheet_y":24,"short_name":"man","short_names":["man"],"text":null,"texts":null,"category":"People","sort_order":124,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F468-1F3FB":{"unified":"1F468-1F3FB","image":"1f468-1f3fb.png","sheet_x":16,"sheet_y":25,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F468-1F3FC":{"unified":"1F468-1F3FC","image":"1f468-1f3fc.png","sheet_x":16,"sheet_y":26,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F468-1F3FD":{"unified":"1F468-1F3FD","image":"1f468-1f3fd.png","sheet_x":16,"sheet_y":27,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F468-1F3FE":{"unified":"1F468-1F3FE","image":"1f468-1f3fe.png","sheet_x":16,"sheet_y":28,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F468-1F3FF":{"unified":"1F468-1F3FF","image":"1f468-1f3ff.png","sheet_x":16,"sheet_y":29,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"WOMAN","unified":"1F469","variations":[],"docomo":"E6F0","au":"E4FA","softbank":"E005","google":"FE19E","image":"1f469.png","sheet_x":16,"sheet_y":30,"short_name":"woman","short_names":["woman"],"text":null,"texts":null,"category":"People","sort_order":125,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F469-1F3FB":{"unified":"1F469-1F3FB","image":"1f469-1f3fb.png","sheet_x":16,"sheet_y":31,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F469-1F3FC":{"unified":"1F469-1F3FC","image":"1f469-1f3fc.png","sheet_x":16,"sheet_y":32,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F469-1F3FD":{"unified":"1F469-1F3FD","image":"1f469-1f3fd.png","sheet_x":16,"sheet_y":33,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F469-1F3FE":{"unified":"1F469-1F3FE","image":"1f469-1f3fe.png","sheet_x":16,"sheet_y":34,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F469-1F3FF":{"unified":"1F469-1F3FF","image":"1f469-1f3ff.png","sheet_x":16,"sheet_y":35,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"FAMILY","unified":"1F46A","variations":["1F468-200D-1F469-200D-1F466"],"docomo":null,"au":"E501","softbank":null,"google":"FE19F","image":"1f46a.png","sheet_x":16,"sheet_y":36,"short_name":"family","short_names":["family","man-woman-boy"],"text":null,"texts":null,"category":"People","sort_order":161,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MAN AND WOMAN HOLDING HANDS","unified":"1F46B","variations":[],"docomo":null,"au":null,"softbank":"E428","google":"FE1A0","image":"1f46b.png","sheet_x":16,"sheet_y":37,"short_name":"couple","short_names":["couple","man_and_woman_holding_hands"],"text":null,"texts":null,"category":"People","sort_order":143,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TWO MEN HOLDING HANDS","unified":"1F46C","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f46c.png","sheet_x":16,"sheet_y":38,"short_name":"two_men_holding_hands","short_names":["two_men_holding_hands"],"text":null,"texts":null,"category":"People","sort_order":144,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TWO WOMEN HOLDING HANDS","unified":"1F46D","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f46d.png","sheet_x":16,"sheet_y":39,"short_name":"two_women_holding_hands","short_names":["two_women_holding_hands"],"text":null,"texts":null,"category":"People","sort_order":145,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"POLICE OFFICER","unified":"1F46E","variations":[],"docomo":null,"au":"E5DD","softbank":"E152","google":"FE1A1","image":"1f46e.png","sheet_x":16,"sheet_y":40,"short_name":"cop","short_names":["cop"],"text":null,"texts":null,"category":"People","sort_order":131,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F46E-1F3FB":{"unified":"1F46E-1F3FB","image":"1f46e-1f3fb.png","sheet_x":17,"sheet_y":0,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F46E-1F3FC":{"unified":"1F46E-1F3FC","image":"1f46e-1f3fc.png","sheet_x":17,"sheet_y":1,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F46E-1F3FD":{"unified":"1F46E-1F3FD","image":"1f46e-1f3fd.png","sheet_x":17,"sheet_y":2,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F46E-1F3FE":{"unified":"1F46E-1F3FE","image":"1f46e-1f3fe.png","sheet_x":17,"sheet_y":3,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F46E-1F3FF":{"unified":"1F46E-1F3FF","image":"1f46e-1f3ff.png","sheet_x":17,"sheet_y":4,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"WOMAN WITH BUNNY EARS","unified":"1F46F","variations":[],"docomo":null,"au":"EADB","softbank":"E429","google":"FE1A2","image":"1f46f.png","sheet_x":17,"sheet_y":5,"short_name":"dancers","short_names":["dancers"],"text":null,"texts":null,"category":"People","sort_order":142,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BRIDE WITH VEIL","unified":"1F470","variations":[],"docomo":null,"au":"EAE9","softbank":null,"google":"FE1A3","image":"1f470.png","sheet_x":17,"sheet_y":6,"short_name":"bride_with_veil","short_names":["bride_with_veil"],"text":null,"texts":null,"category":"People","sort_order":138,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F470-1F3FB":{"unified":"1F470-1F3FB","image":"1f470-1f3fb.png","sheet_x":17,"sheet_y":7,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F470-1F3FC":{"unified":"1F470-1F3FC","image":"1f470-1f3fc.png","sheet_x":17,"sheet_y":8,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F470-1F3FD":{"unified":"1F470-1F3FD","image":"1f470-1f3fd.png","sheet_x":17,"sheet_y":9,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F470-1F3FE":{"unified":"1F470-1F3FE","image":"1f470-1f3fe.png","sheet_x":17,"sheet_y":10,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F470-1F3FF":{"unified":"1F470-1F3FF","image":"1f470-1f3ff.png","sheet_x":17,"sheet_y":11,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"PERSON WITH BLOND HAIR","unified":"1F471","variations":[],"docomo":null,"au":"EB13","softbank":"E515","google":"FE1A4","image":"1f471.png","sheet_x":17,"sheet_y":12,"short_name":"person_with_blond_hair","short_names":["person_with_blond_hair"],"text":null,"texts":null,"category":"People","sort_order":126,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F471-1F3FB":{"unified":"1F471-1F3FB","image":"1f471-1f3fb.png","sheet_x":17,"sheet_y":13,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F471-1F3FC":{"unified":"1F471-1F3FC","image":"1f471-1f3fc.png","sheet_x":17,"sheet_y":14,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F471-1F3FD":{"unified":"1F471-1F3FD","image":"1f471-1f3fd.png","sheet_x":17,"sheet_y":15,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F471-1F3FE":{"unified":"1F471-1F3FE","image":"1f471-1f3fe.png","sheet_x":17,"sheet_y":16,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F471-1F3FF":{"unified":"1F471-1F3FF","image":"1f471-1f3ff.png","sheet_x":17,"sheet_y":17,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"MAN WITH GUA PI MAO","unified":"1F472","variations":[],"docomo":null,"au":"EB14","softbank":"E516","google":"FE1A5","image":"1f472.png","sheet_x":17,"sheet_y":18,"short_name":"man_with_gua_pi_mao","short_names":["man_with_gua_pi_mao"],"text":null,"texts":null,"category":"People","sort_order":129,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F472-1F3FB":{"unified":"1F472-1F3FB","image":"1f472-1f3fb.png","sheet_x":17,"sheet_y":19,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F472-1F3FC":{"unified":"1F472-1F3FC","image":"1f472-1f3fc.png","sheet_x":17,"sheet_y":20,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F472-1F3FD":{"unified":"1F472-1F3FD","image":"1f472-1f3fd.png","sheet_x":17,"sheet_y":21,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F472-1F3FE":{"unified":"1F472-1F3FE","image":"1f472-1f3fe.png","sheet_x":17,"sheet_y":22,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F472-1F3FF":{"unified":"1F472-1F3FF","image":"1f472-1f3ff.png","sheet_x":17,"sheet_y":23,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"MAN WITH TURBAN","unified":"1F473","variations":[],"docomo":null,"au":"EB15","softbank":"E517","google":"FE1A6","image":"1f473.png","sheet_x":17,"sheet_y":24,"short_name":"man_with_turban","short_names":["man_with_turban"],"text":null,"texts":null,"category":"People","sort_order":130,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F473-1F3FB":{"unified":"1F473-1F3FB","image":"1f473-1f3fb.png","sheet_x":17,"sheet_y":25,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F473-1F3FC":{"unified":"1F473-1F3FC","image":"1f473-1f3fc.png","sheet_x":17,"sheet_y":26,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F473-1F3FD":{"unified":"1F473-1F3FD","image":"1f473-1f3fd.png","sheet_x":17,"sheet_y":27,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F473-1F3FE":{"unified":"1F473-1F3FE","image":"1f473-1f3fe.png","sheet_x":17,"sheet_y":28,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F473-1F3FF":{"unified":"1F473-1F3FF","image":"1f473-1f3ff.png","sheet_x":17,"sheet_y":29,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"OLDER MAN","unified":"1F474","variations":[],"docomo":null,"au":"EB16","softbank":"E518","google":"FE1A7","image":"1f474.png","sheet_x":17,"sheet_y":30,"short_name":"older_man","short_names":["older_man"],"text":null,"texts":null,"category":"People","sort_order":127,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F474-1F3FB":{"unified":"1F474-1F3FB","image":"1f474-1f3fb.png","sheet_x":17,"sheet_y":31,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F474-1F3FC":{"unified":"1F474-1F3FC","image":"1f474-1f3fc.png","sheet_x":17,"sheet_y":32,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F474-1F3FD":{"unified":"1F474-1F3FD","image":"1f474-1f3fd.png","sheet_x":17,"sheet_y":33,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F474-1F3FE":{"unified":"1F474-1F3FE","image":"1f474-1f3fe.png","sheet_x":17,"sheet_y":34,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F474-1F3FF":{"unified":"1F474-1F3FF","image":"1f474-1f3ff.png","sheet_x":17,"sheet_y":35,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"OLDER WOMAN","unified":"1F475","variations":[],"docomo":null,"au":"EB17","softbank":"E519","google":"FE1A8","image":"1f475.png","sheet_x":17,"sheet_y":36,"short_name":"older_woman","short_names":["older_woman"],"text":null,"texts":null,"category":"People","sort_order":128,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F475-1F3FB":{"unified":"1F475-1F3FB","image":"1f475-1f3fb.png","sheet_x":17,"sheet_y":37,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F475-1F3FC":{"unified":"1F475-1F3FC","image":"1f475-1f3fc.png","sheet_x":17,"sheet_y":38,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F475-1F3FD":{"unified":"1F475-1F3FD","image":"1f475-1f3fd.png","sheet_x":17,"sheet_y":39,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F475-1F3FE":{"unified":"1F475-1F3FE","image":"1f475-1f3fe.png","sheet_x":17,"sheet_y":40,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F475-1F3FF":{"unified":"1F475-1F3FF","image":"1f475-1f3ff.png","sheet_x":18,"sheet_y":0,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"BABY","unified":"1F476","variations":[],"docomo":null,"au":"EB18","softbank":"E51A","google":"FE1A9","image":"1f476.png","sheet_x":18,"sheet_y":1,"short_name":"baby","short_names":["baby"],"text":null,"texts":null,"category":"People","sort_order":121,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F476-1F3FB":{"unified":"1F476-1F3FB","image":"1f476-1f3fb.png","sheet_x":18,"sheet_y":2,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F476-1F3FC":{"unified":"1F476-1F3FC","image":"1f476-1f3fc.png","sheet_x":18,"sheet_y":3,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F476-1F3FD":{"unified":"1F476-1F3FD","image":"1f476-1f3fd.png","sheet_x":18,"sheet_y":4,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F476-1F3FE":{"unified":"1F476-1F3FE","image":"1f476-1f3fe.png","sheet_x":18,"sheet_y":5,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F476-1F3FF":{"unified":"1F476-1F3FF","image":"1f476-1f3ff.png","sheet_x":18,"sheet_y":6,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"CONSTRUCTION WORKER","unified":"1F477","variations":[],"docomo":null,"au":"EB19","softbank":"E51B","google":"FE1AA","image":"1f477.png","sheet_x":18,"sheet_y":7,"short_name":"construction_worker","short_names":["construction_worker"],"text":null,"texts":null,"category":"People","sort_order":132,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F477-1F3FB":{"unified":"1F477-1F3FB","image":"1f477-1f3fb.png","sheet_x":18,"sheet_y":8,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F477-1F3FC":{"unified":"1F477-1F3FC","image":"1f477-1f3fc.png","sheet_x":18,"sheet_y":9,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F477-1F3FD":{"unified":"1F477-1F3FD","image":"1f477-1f3fd.png","sheet_x":18,"sheet_y":10,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F477-1F3FE":{"unified":"1F477-1F3FE","image":"1f477-1f3fe.png","sheet_x":18,"sheet_y":11,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F477-1F3FF":{"unified":"1F477-1F3FF","image":"1f477-1f3ff.png","sheet_x":18,"sheet_y":12,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"PRINCESS","unified":"1F478","variations":[],"docomo":null,"au":"EB1A","softbank":"E51C","google":"FE1AB","image":"1f478.png","sheet_x":18,"sheet_y":13,"short_name":"princess","short_names":["princess"],"text":null,"texts":null,"category":"People","sort_order":137,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F478-1F3FB":{"unified":"1F478-1F3FB","image":"1f478-1f3fb.png","sheet_x":18,"sheet_y":14,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F478-1F3FC":{"unified":"1F478-1F3FC","image":"1f478-1f3fc.png","sheet_x":18,"sheet_y":15,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F478-1F3FD":{"unified":"1F478-1F3FD","image":"1f478-1f3fd.png","sheet_x":18,"sheet_y":16,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F478-1F3FE":{"unified":"1F478-1F3FE","image":"1f478-1f3fe.png","sheet_x":18,"sheet_y":17,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F478-1F3FF":{"unified":"1F478-1F3FF","image":"1f478-1f3ff.png","sheet_x":18,"sheet_y":18,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"JAPANESE OGRE","unified":"1F479","variations":[],"docomo":null,"au":"EB44","softbank":null,"google":"FE1AC","image":"1f479.png","sheet_x":18,"sheet_y":19,"short_name":"japanese_ogre","short_names":["japanese_ogre"],"text":null,"texts":null,"category":"People","sort_order":73,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"JAPANESE GOBLIN","unified":"1F47A","variations":[],"docomo":null,"au":"EB45","softbank":null,"google":"FE1AD","image":"1f47a.png","sheet_x":18,"sheet_y":20,"short_name":"japanese_goblin","short_names":["japanese_goblin"],"text":null,"texts":null,"category":"People","sort_order":74,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GHOST","unified":"1F47B","variations":[],"docomo":null,"au":"E4CB","softbank":"E11B","google":"FE1AE","image":"1f47b.png","sheet_x":18,"sheet_y":21,"short_name":"ghost","short_names":["ghost"],"text":null,"texts":null,"category":"People","sort_order":76,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BABY ANGEL","unified":"1F47C","variations":[],"docomo":null,"au":"E5BF","softbank":"E04E","google":"FE1AF","image":"1f47c.png","sheet_x":18,"sheet_y":22,"short_name":"angel","short_names":["angel"],"text":null,"texts":null,"category":"People","sort_order":136,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F47C-1F3FB":{"unified":"1F47C-1F3FB","image":"1f47c-1f3fb.png","sheet_x":18,"sheet_y":23,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F47C-1F3FC":{"unified":"1F47C-1F3FC","image":"1f47c-1f3fc.png","sheet_x":18,"sheet_y":24,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F47C-1F3FD":{"unified":"1F47C-1F3FD","image":"1f47c-1f3fd.png","sheet_x":18,"sheet_y":25,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F47C-1F3FE":{"unified":"1F47C-1F3FE","image":"1f47c-1f3fe.png","sheet_x":18,"sheet_y":26,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F47C-1F3FF":{"unified":"1F47C-1F3FF","image":"1f47c-1f3ff.png","sheet_x":18,"sheet_y":27,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"EXTRATERRESTRIAL ALIEN","unified":"1F47D","variations":[],"docomo":null,"au":"E50E","softbank":"E10C","google":"FE1B0","image":"1f47d.png","sheet_x":18,"sheet_y":28,"short_name":"alien","short_names":["alien"],"text":null,"texts":null,"category":"People","sort_order":77,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ALIEN MONSTER","unified":"1F47E","variations":[],"docomo":null,"au":"E4EC","softbank":"E12B","google":"FE1B1","image":"1f47e.png","sheet_x":18,"sheet_y":29,"short_name":"space_invader","short_names":["space_invader"],"text":null,"texts":null,"category":"Activity","sort_order":53,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"IMP","unified":"1F47F","variations":[],"docomo":null,"au":"E4EF","softbank":"E11A","google":"FE1B2","image":"1f47f.png","sheet_x":18,"sheet_y":30,"short_name":"imp","short_names":["imp"],"text":null,"texts":null,"category":"People","sort_order":72,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SKULL","unified":"1F480","variations":[],"docomo":null,"au":"E4F8","softbank":"E11C","google":"FE1B3","image":"1f480.png","sheet_x":18,"sheet_y":31,"short_name":"skull","short_names":["skull"],"text":null,"texts":null,"category":"People","sort_order":75,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"INFORMATION DESK PERSON","unified":"1F481","variations":[],"docomo":null,"au":null,"softbank":"E253","google":"FE1B4","image":"1f481.png","sheet_x":18,"sheet_y":32,"short_name":"information_desk_person","short_names":["information_desk_person"],"text":null,"texts":null,"category":"People","sort_order":147,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F481-1F3FB":{"unified":"1F481-1F3FB","image":"1f481-1f3fb.png","sheet_x":18,"sheet_y":33,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F481-1F3FC":{"unified":"1F481-1F3FC","image":"1f481-1f3fc.png","sheet_x":18,"sheet_y":34,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F481-1F3FD":{"unified":"1F481-1F3FD","image":"1f481-1f3fd.png","sheet_x":18,"sheet_y":35,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F481-1F3FE":{"unified":"1F481-1F3FE","image":"1f481-1f3fe.png","sheet_x":18,"sheet_y":36,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F481-1F3FF":{"unified":"1F481-1F3FF","image":"1f481-1f3ff.png","sheet_x":18,"sheet_y":37,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"GUARDSMAN","unified":"1F482","variations":[],"docomo":null,"au":null,"softbank":"E51E","google":"FE1B5","image":"1f482.png","sheet_x":18,"sheet_y":38,"short_name":"guardsman","short_names":["guardsman"],"text":null,"texts":null,"category":"People","sort_order":133,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F482-1F3FB":{"unified":"1F482-1F3FB","image":"1f482-1f3fb.png","sheet_x":18,"sheet_y":39,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F482-1F3FC":{"unified":"1F482-1F3FC","image":"1f482-1f3fc.png","sheet_x":18,"sheet_y":40,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F482-1F3FD":{"unified":"1F482-1F3FD","image":"1f482-1f3fd.png","sheet_x":19,"sheet_y":0,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F482-1F3FE":{"unified":"1F482-1F3FE","image":"1f482-1f3fe.png","sheet_x":19,"sheet_y":1,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F482-1F3FF":{"unified":"1F482-1F3FF","image":"1f482-1f3ff.png","sheet_x":19,"sheet_y":2,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"DANCER","unified":"1F483","variations":[],"docomo":null,"au":"EB1C","softbank":"E51F","google":"FE1B6","image":"1f483.png","sheet_x":19,"sheet_y":3,"short_name":"dancer","short_names":["dancer"],"text":null,"texts":null,"category":"People","sort_order":141,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F483-1F3FB":{"unified":"1F483-1F3FB","image":"1f483-1f3fb.png","sheet_x":19,"sheet_y":4,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F483-1F3FC":{"unified":"1F483-1F3FC","image":"1f483-1f3fc.png","sheet_x":19,"sheet_y":5,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F483-1F3FD":{"unified":"1F483-1F3FD","image":"1f483-1f3fd.png","sheet_x":19,"sheet_y":6,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F483-1F3FE":{"unified":"1F483-1F3FE","image":"1f483-1f3fe.png","sheet_x":19,"sheet_y":7,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F483-1F3FF":{"unified":"1F483-1F3FF","image":"1f483-1f3ff.png","sheet_x":19,"sheet_y":8,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"LIPSTICK","unified":"1F484","variations":[],"docomo":"E710","au":"E509","softbank":"E31C","google":"FE195","image":"1f484.png","sheet_x":19,"sheet_y":9,"short_name":"lipstick","short_names":["lipstick"],"text":null,"texts":null,"category":"People","sort_order":183,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NAIL POLISH","unified":"1F485","variations":[],"docomo":null,"au":"EAA0","softbank":"E31D","google":"FE196","image":"1f485.png","sheet_x":19,"sheet_y":10,"short_name":"nail_care","short_names":["nail_care"],"text":null,"texts":null,"category":"People","sort_order":111,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F485-1F3FB":{"unified":"1F485-1F3FB","image":"1f485-1f3fb.png","sheet_x":19,"sheet_y":11,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F485-1F3FC":{"unified":"1F485-1F3FC","image":"1f485-1f3fc.png","sheet_x":19,"sheet_y":12,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F485-1F3FD":{"unified":"1F485-1F3FD","image":"1f485-1f3fd.png","sheet_x":19,"sheet_y":13,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F485-1F3FE":{"unified":"1F485-1F3FE","image":"1f485-1f3fe.png","sheet_x":19,"sheet_y":14,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F485-1F3FF":{"unified":"1F485-1F3FF","image":"1f485-1f3ff.png","sheet_x":19,"sheet_y":15,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"FACE MASSAGE","unified":"1F486","variations":[],"docomo":null,"au":"E50B","softbank":"E31E","google":"FE197","image":"1f486.png","sheet_x":19,"sheet_y":16,"short_name":"massage","short_names":["massage"],"text":null,"texts":null,"category":"People","sort_order":154,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F486-1F3FB":{"unified":"1F486-1F3FB","image":"1f486-1f3fb.png","sheet_x":19,"sheet_y":17,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F486-1F3FC":{"unified":"1F486-1F3FC","image":"1f486-1f3fc.png","sheet_x":19,"sheet_y":18,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F486-1F3FD":{"unified":"1F486-1F3FD","image":"1f486-1f3fd.png","sheet_x":19,"sheet_y":19,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F486-1F3FE":{"unified":"1F486-1F3FE","image":"1f486-1f3fe.png","sheet_x":19,"sheet_y":20,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F486-1F3FF":{"unified":"1F486-1F3FF","image":"1f486-1f3ff.png","sheet_x":19,"sheet_y":21,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"HAIRCUT","unified":"1F487","variations":[],"docomo":"E675","au":"EAA1","softbank":"E31F","google":"FE198","image":"1f487.png","sheet_x":19,"sheet_y":22,"short_name":"haircut","short_names":["haircut"],"text":null,"texts":null,"category":"People","sort_order":153,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F487-1F3FB":{"unified":"1F487-1F3FB","image":"1f487-1f3fb.png","sheet_x":19,"sheet_y":23,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F487-1F3FC":{"unified":"1F487-1F3FC","image":"1f487-1f3fc.png","sheet_x":19,"sheet_y":24,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F487-1F3FD":{"unified":"1F487-1F3FD","image":"1f487-1f3fd.png","sheet_x":19,"sheet_y":25,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F487-1F3FE":{"unified":"1F487-1F3FE","image":"1f487-1f3fe.png","sheet_x":19,"sheet_y":26,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F487-1F3FF":{"unified":"1F487-1F3FF","image":"1f487-1f3ff.png","sheet_x":19,"sheet_y":27,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"BARBER POLE","unified":"1F488","variations":[],"docomo":null,"au":"EAA2","softbank":"E320","google":"FE199","image":"1f488.png","sheet_x":19,"sheet_y":28,"short_name":"barber","short_names":["barber"],"text":null,"texts":null,"category":"Objects","sort_order":76,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SYRINGE","unified":"1F489","variations":[],"docomo":null,"au":"E510","softbank":"E13B","google":"FE509","image":"1f489.png","sheet_x":19,"sheet_y":29,"short_name":"syringe","short_names":["syringe"],"text":null,"texts":null,"category":"Objects","sort_order":82,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PILL","unified":"1F48A","variations":[],"docomo":null,"au":"EA9A","softbank":"E30F","google":"FE50A","image":"1f48a.png","sheet_x":19,"sheet_y":30,"short_name":"pill","short_names":["pill"],"text":null,"texts":null,"category":"Objects","sort_order":81,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KISS MARK","unified":"1F48B","variations":[],"docomo":"E6F9","au":"E4EB","softbank":"E003","google":"FE823","image":"1f48b.png","sheet_x":19,"sheet_y":31,"short_name":"kiss","short_names":["kiss"],"text":null,"texts":[":*",":-*"],"category":"People","sort_order":184,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LOVE LETTER","unified":"1F48C","variations":[],"docomo":"E717","au":"EB78","softbank":"E103-E328","google":"FE824","image":"1f48c.png","sheet_x":19,"sheet_y":32,"short_name":"love_letter","short_names":["love_letter"],"text":null,"texts":null,"category":"Objects","sort_order":115,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RING","unified":"1F48D","variations":[],"docomo":"E71B","au":"E514","softbank":"E034","google":"FE825","image":"1f48d.png","sheet_x":19,"sheet_y":33,"short_name":"ring","short_names":["ring"],"text":null,"texts":null,"category":"People","sort_order":203,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GEM STONE","unified":"1F48E","variations":[],"docomo":"E71B","au":"E514","softbank":"E035","google":"FE826","image":"1f48e.png","sheet_x":19,"sheet_y":34,"short_name":"gem","short_names":["gem"],"text":null,"texts":null,"category":"Objects","sort_order":53,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KISS","unified":"1F48F","variations":[],"docomo":"E6F9","au":"E5CA","softbank":"E111","google":"FE827","image":"1f48f.png","sheet_x":19,"sheet_y":35,"short_name":"couplekiss","short_names":["couplekiss"],"text":null,"texts":null,"category":"People","sort_order":158,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BOUQUET","unified":"1F490","variations":[],"docomo":null,"au":"EA95","softbank":"E306","google":"FE828","image":"1f490.png","sheet_x":19,"sheet_y":36,"short_name":"bouquet","short_names":["bouquet"],"text":null,"texts":null,"category":"Nature","sort_order":95,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"COUPLE WITH HEART","unified":"1F491","variations":[],"docomo":"E6ED","au":"EADA","softbank":"E425","google":"FE829","image":"1f491.png","sheet_x":19,"sheet_y":37,"short_name":"couple_with_heart","short_names":["couple_with_heart"],"text":null,"texts":null,"category":"People","sort_order":155,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WEDDING","unified":"1F492","variations":[],"docomo":null,"au":"E5BB","softbank":"E43D","google":"FE82A","image":"1f492.png","sheet_x":19,"sheet_y":38,"short_name":"wedding","short_names":["wedding"],"text":null,"texts":null,"category":"Places","sort_order":109,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BEATING HEART","unified":"1F493","variations":[],"docomo":"E6ED","au":"EB75","softbank":"E327","google":"FEB0D","image":"1f493.png","sheet_x":19,"sheet_y":39,"short_name":"heartbeat","short_names":["heartbeat"],"text":null,"texts":null,"category":"Symbols","sort_order":10,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BROKEN HEART","unified":"1F494","variations":[],"docomo":"E6EE","au":"E477","softbank":"E023","google":"FEB0E","image":"1f494.png","sheet_x":19,"sheet_y":40,"short_name":"broken_heart","short_names":["broken_heart"],"text":"<\/3","texts":["<\/3"],"category":"Symbols","sort_order":6,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TWO HEARTS","unified":"1F495","variations":[],"docomo":"E6EF","au":"E478","softbank":"E327","google":"FEB0F","image":"1f495.png","sheet_x":20,"sheet_y":0,"short_name":"two_hearts","short_names":["two_hearts"],"text":null,"texts":null,"category":"Symbols","sort_order":8,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPARKLING HEART","unified":"1F496","variations":[],"docomo":"E6EC","au":"EAA6","softbank":"E327","google":"FEB10","image":"1f496.png","sheet_x":20,"sheet_y":1,"short_name":"sparkling_heart","short_names":["sparkling_heart"],"text":null,"texts":null,"category":"Symbols","sort_order":12,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GROWING HEART","unified":"1F497","variations":[],"docomo":"E6ED","au":"EB75","softbank":"E328","google":"FEB11","image":"1f497.png","sheet_x":20,"sheet_y":2,"short_name":"heartpulse","short_names":["heartpulse"],"text":null,"texts":null,"category":"Symbols","sort_order":11,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HEART WITH ARROW","unified":"1F498","variations":[],"docomo":"E6EC","au":"E4EA","softbank":"E329","google":"FEB12","image":"1f498.png","sheet_x":20,"sheet_y":3,"short_name":"cupid","short_names":["cupid"],"text":null,"texts":null,"category":"Symbols","sort_order":13,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLUE HEART","unified":"1F499","variations":[],"docomo":"E6EC","au":"EAA7","softbank":"E32A","google":"FEB13","image":"1f499.png","sheet_x":20,"sheet_y":4,"short_name":"blue_heart","short_names":["blue_heart"],"text":"<3","texts":null,"category":"Symbols","sort_order":4,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GREEN HEART","unified":"1F49A","variations":[],"docomo":"E6EC","au":"EAA8","softbank":"E32B","google":"FEB14","image":"1f49a.png","sheet_x":20,"sheet_y":5,"short_name":"green_heart","short_names":["green_heart"],"text":"<3","texts":null,"category":"Symbols","sort_order":3,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"YELLOW HEART","unified":"1F49B","variations":[],"docomo":"E6EC","au":"EAA9","softbank":"E32C","google":"FEB15","image":"1f49b.png","sheet_x":20,"sheet_y":6,"short_name":"yellow_heart","short_names":["yellow_heart"],"text":"<3","texts":null,"category":"Symbols","sort_order":2,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PURPLE HEART","unified":"1F49C","variations":[],"docomo":"E6EC","au":"EAAA","softbank":"E32D","google":"FEB16","image":"1f49c.png","sheet_x":20,"sheet_y":7,"short_name":"purple_heart","short_names":["purple_heart"],"text":"<3","texts":null,"category":"Symbols","sort_order":5,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HEART WITH RIBBON","unified":"1F49D","variations":[],"docomo":"E6EC","au":"EB54","softbank":"E437","google":"FEB17","image":"1f49d.png","sheet_x":20,"sheet_y":8,"short_name":"gift_heart","short_names":["gift_heart"],"text":null,"texts":null,"category":"Symbols","sort_order":14,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REVOLVING HEARTS","unified":"1F49E","variations":[],"docomo":"E6ED","au":"E5AF","softbank":"E327","google":"FEB18","image":"1f49e.png","sheet_x":20,"sheet_y":9,"short_name":"revolving_hearts","short_names":["revolving_hearts"],"text":null,"texts":null,"category":"Symbols","sort_order":9,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HEART DECORATION","unified":"1F49F","variations":[],"docomo":"E6F8","au":"E595","softbank":"E204","google":"FEB19","image":"1f49f.png","sheet_x":20,"sheet_y":10,"short_name":"heart_decoration","short_names":["heart_decoration"],"text":null,"texts":null,"category":"Symbols","sort_order":15,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DIAMOND SHAPE WITH A DOT INSIDE","unified":"1F4A0","variations":[],"docomo":"E6F8","au":null,"softbank":null,"google":"FEB55","image":"1f4a0.png","sheet_x":20,"sheet_y":11,"short_name":"diamond_shape_with_a_dot_inside","short_names":["diamond_shape_with_a_dot_inside"],"text":null,"texts":null,"category":"Symbols","sort_order":104,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ELECTRIC LIGHT BULB","unified":"1F4A1","variations":[],"docomo":"E6FB","au":"E476","softbank":"E10F","google":"FEB56","image":"1f4a1.png","sheet_x":20,"sheet_y":12,"short_name":"bulb","short_names":["bulb"],"text":null,"texts":null,"category":"Objects","sort_order":41,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ANGER SYMBOL","unified":"1F4A2","variations":[],"docomo":"E6FC","au":"E4E5","softbank":"E334","google":"FEB57","image":"1f4a2.png","sheet_x":20,"sheet_y":13,"short_name":"anger","short_names":["anger"],"text":null,"texts":null,"category":"Symbols","sort_order":74,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BOMB","unified":"1F4A3","variations":[],"docomo":"E6FE","au":"E47A","softbank":"E311","google":"FEB58","image":"1f4a3.png","sheet_x":20,"sheet_y":14,"short_name":"bomb","short_names":["bomb"],"text":null,"texts":null,"category":"Objects","sort_order":64,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SLEEPING SYMBOL","unified":"1F4A4","variations":[],"docomo":"E701","au":"E475","softbank":"E13C","google":"FEB59","image":"1f4a4.png","sheet_x":20,"sheet_y":15,"short_name":"zzz","short_names":["zzz"],"text":null,"texts":null,"category":"People","sort_order":69,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"COLLISION SYMBOL","unified":"1F4A5","variations":[],"docomo":"E705","au":"E5B0","softbank":null,"google":"FEB5A","image":"1f4a5.png","sheet_x":20,"sheet_y":16,"short_name":"boom","short_names":["boom","collision"],"text":null,"texts":null,"category":"Nature","sort_order":134,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPLASHING SWEAT SYMBOL","unified":"1F4A6","variations":[],"docomo":"E706","au":"E5B1","softbank":"E331","google":"FEB5B","image":"1f4a6.png","sheet_x":20,"sheet_y":17,"short_name":"sweat_drops","short_names":["sweat_drops"],"text":null,"texts":null,"category":"Nature","sort_order":146,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DROPLET","unified":"1F4A7","variations":[],"docomo":"E707","au":"E4E6","softbank":"E331","google":"FEB5C","image":"1f4a7.png","sheet_x":20,"sheet_y":18,"short_name":"droplet","short_names":["droplet"],"text":null,"texts":null,"category":"Nature","sort_order":145,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DASH SYMBOL","unified":"1F4A8","variations":[],"docomo":"E708","au":"E4F4","softbank":"E330","google":"FEB5D","image":"1f4a8.png","sheet_x":20,"sheet_y":19,"short_name":"dash","short_names":["dash"],"text":null,"texts":null,"category":"Nature","sort_order":140,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PILE OF POO","unified":"1F4A9","variations":[],"docomo":null,"au":"E4F5","softbank":"E05A","google":"FE4F4","image":"1f4a9.png","sheet_x":20,"sheet_y":20,"short_name":"hankey","short_names":["hankey","poop","shit"],"text":null,"texts":null,"category":"People","sort_order":70,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FLEXED BICEPS","unified":"1F4AA","variations":[],"docomo":null,"au":"E4E9","softbank":"E14C","google":"FEB5E","image":"1f4aa.png","sheet_x":20,"sheet_y":21,"short_name":"muscle","short_names":["muscle"],"text":null,"texts":null,"category":"People","sort_order":99,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F4AA-1F3FB":{"unified":"1F4AA-1F3FB","image":"1f4aa-1f3fb.png","sheet_x":20,"sheet_y":22,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F4AA-1F3FC":{"unified":"1F4AA-1F3FC","image":"1f4aa-1f3fc.png","sheet_x":20,"sheet_y":23,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F4AA-1F3FD":{"unified":"1F4AA-1F3FD","image":"1f4aa-1f3fd.png","sheet_x":20,"sheet_y":24,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F4AA-1F3FE":{"unified":"1F4AA-1F3FE","image":"1f4aa-1f3fe.png","sheet_x":20,"sheet_y":25,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F4AA-1F3FF":{"unified":"1F4AA-1F3FF","image":"1f4aa-1f3ff.png","sheet_x":20,"sheet_y":26,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"DIZZY SYMBOL","unified":"1F4AB","variations":[],"docomo":null,"au":"EB5C","softbank":"E407","google":"FEB5F","image":"1f4ab.png","sheet_x":20,"sheet_y":27,"short_name":"dizzy","short_names":["dizzy"],"text":null,"texts":null,"category":"Nature","sort_order":120,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPEECH BALLOON","unified":"1F4AC","variations":[],"docomo":null,"au":"E4FD","softbank":null,"google":"FE532","image":"1f4ac.png","sheet_x":20,"sheet_y":28,"short_name":"speech_balloon","short_names":["speech_balloon"],"text":null,"texts":null,"category":"Symbols","sort_order":245,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"THOUGHT BALLOON","unified":"1F4AD","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4ad.png","sheet_x":20,"sheet_y":29,"short_name":"thought_balloon","short_names":["thought_balloon"],"text":null,"texts":null,"category":"Symbols","sort_order":243,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHITE FLOWER","unified":"1F4AE","variations":[],"docomo":null,"au":"E4F0","softbank":null,"google":"FEB7A","image":"1f4ae.png","sheet_x":20,"sheet_y":30,"short_name":"white_flower","short_names":["white_flower"],"text":null,"texts":null,"category":"Symbols","sort_order":56,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HUNDRED POINTS SYMBOL","unified":"1F4AF","variations":[],"docomo":null,"au":"E4F2","softbank":null,"google":"FEB7B","image":"1f4af.png","sheet_x":20,"sheet_y":31,"short_name":"100","short_names":["100"],"text":null,"texts":null,"category":"Symbols","sort_order":88,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MONEY BAG","unified":"1F4B0","variations":[],"docomo":"E715","au":"E4C7","softbank":"E12F","google":"FE4DD","image":"1f4b0.png","sheet_x":20,"sheet_y":32,"short_name":"moneybag","short_names":["moneybag"],"text":null,"texts":null,"category":"Objects","sort_order":51,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CURRENCY EXCHANGE","unified":"1F4B1","variations":[],"docomo":null,"au":null,"softbank":"E149","google":"FE4DE","image":"1f4b1.png","sheet_x":20,"sheet_y":33,"short_name":"currency_exchange","short_names":["currency_exchange"],"text":null,"texts":null,"category":"Symbols","sort_order":196,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HEAVY DOLLAR SIGN","unified":"1F4B2","variations":[],"docomo":"E715","au":"E579","softbank":"E12F","google":"FE4E0","image":"1f4b2.png","sheet_x":20,"sheet_y":34,"short_name":"heavy_dollar_sign","short_names":["heavy_dollar_sign"],"text":null,"texts":null,"category":"Symbols","sort_order":195,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CREDIT CARD","unified":"1F4B3","variations":[],"docomo":null,"au":"E57C","softbank":null,"google":"FE4E1","image":"1f4b3.png","sheet_x":20,"sheet_y":35,"short_name":"credit_card","short_names":["credit_card"],"text":null,"texts":null,"category":"Objects","sort_order":52,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BANKNOTE WITH YEN SIGN","unified":"1F4B4","variations":[],"docomo":"E6D6","au":"E57D","softbank":null,"google":"FE4E2","image":"1f4b4.png","sheet_x":20,"sheet_y":36,"short_name":"yen","short_names":["yen"],"text":null,"texts":null,"category":"Objects","sort_order":48,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BANKNOTE WITH DOLLAR SIGN","unified":"1F4B5","variations":[],"docomo":"E715","au":"E585","softbank":"E12F","google":"FE4E3","image":"1f4b5.png","sheet_x":20,"sheet_y":37,"short_name":"dollar","short_names":["dollar"],"text":null,"texts":null,"category":"Objects","sort_order":47,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BANKNOTE WITH EURO SIGN","unified":"1F4B6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4b6.png","sheet_x":20,"sheet_y":38,"short_name":"euro","short_names":["euro"],"text":null,"texts":null,"category":"Objects","sort_order":49,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BANKNOTE WITH POUND SIGN","unified":"1F4B7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4b7.png","sheet_x":20,"sheet_y":39,"short_name":"pound","short_names":["pound"],"text":null,"texts":null,"category":"Objects","sort_order":50,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MONEY WITH WINGS","unified":"1F4B8","variations":[],"docomo":null,"au":"EB5B","softbank":null,"google":"FE4E4","image":"1f4b8.png","sheet_x":20,"sheet_y":40,"short_name":"money_with_wings","short_names":["money_with_wings"],"text":null,"texts":null,"category":"Objects","sort_order":46,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CHART WITH UPWARDS TREND AND YEN SIGN","unified":"1F4B9","variations":[],"docomo":null,"au":"E5DC","softbank":"E14A","google":"FE4DF","image":"1f4b9.png","sheet_x":21,"sheet_y":0,"short_name":"chart","short_names":["chart"],"text":null,"texts":null,"category":"Symbols","sort_order":99,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SEAT","unified":"1F4BA","variations":[],"docomo":"E6B2","au":null,"softbank":"E11F","google":"FE537","image":"1f4ba.png","sheet_x":21,"sheet_y":1,"short_name":"seat","short_names":["seat"],"text":null,"texts":null,"category":"Places","sort_order":48,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PERSONAL COMPUTER","unified":"1F4BB","variations":[],"docomo":"E716","au":"E5B8","softbank":"E00C","google":"FE538","image":"1f4bb.png","sheet_x":21,"sheet_y":2,"short_name":"computer","short_names":["computer"],"text":null,"texts":null,"category":"Objects","sort_order":4,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BRIEFCASE","unified":"1F4BC","variations":[],"docomo":"E682","au":"E5CE","softbank":"E11E","google":"FE53B","image":"1f4bc.png","sheet_x":21,"sheet_y":3,"short_name":"briefcase","short_names":["briefcase"],"text":null,"texts":null,"category":"People","sort_order":200,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MINIDISC","unified":"1F4BD","variations":[],"docomo":null,"au":"E582","softbank":"E316","google":"FE53C","image":"1f4bd.png","sheet_x":21,"sheet_y":4,"short_name":"minidisc","short_names":["minidisc"],"text":null,"texts":null,"category":"Objects","sort_order":12,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FLOPPY DISK","unified":"1F4BE","variations":[],"docomo":null,"au":"E562","softbank":"E316","google":"FE53D","image":"1f4be.png","sheet_x":21,"sheet_y":5,"short_name":"floppy_disk","short_names":["floppy_disk"],"text":null,"texts":null,"category":"Objects","sort_order":13,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"OPTICAL DISC","unified":"1F4BF","variations":[],"docomo":"E68C","au":"E50C","softbank":"E126","google":"FE81D","image":"1f4bf.png","sheet_x":21,"sheet_y":6,"short_name":"cd","short_names":["cd"],"text":null,"texts":null,"category":"Objects","sort_order":14,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DVD","unified":"1F4C0","variations":[],"docomo":"E68C","au":"E50C","softbank":"E127","google":"FE81E","image":"1f4c0.png","sheet_x":21,"sheet_y":7,"short_name":"dvd","short_names":["dvd"],"text":null,"texts":null,"category":"Objects","sort_order":15,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FILE FOLDER","unified":"1F4C1","variations":[],"docomo":null,"au":"E58F","softbank":null,"google":"FE543","image":"1f4c1.png","sheet_x":21,"sheet_y":8,"short_name":"file_folder","short_names":["file_folder"],"text":null,"texts":null,"category":"Objects","sort_order":141,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"OPEN FILE FOLDER","unified":"1F4C2","variations":[],"docomo":null,"au":"E590","softbank":null,"google":"FE544","image":"1f4c2.png","sheet_x":21,"sheet_y":9,"short_name":"open_file_folder","short_names":["open_file_folder"],"text":null,"texts":null,"category":"Objects","sort_order":142,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PAGE WITH CURL","unified":"1F4C3","variations":[],"docomo":"E689","au":"E561","softbank":"E301","google":"FE540","image":"1f4c3.png","sheet_x":21,"sheet_y":10,"short_name":"page_with_curl","short_names":["page_with_curl"],"text":null,"texts":null,"category":"Objects","sort_order":126,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PAGE FACING UP","unified":"1F4C4","variations":[],"docomo":"E689","au":"E569","softbank":"E301","google":"FE541","image":"1f4c4.png","sheet_x":21,"sheet_y":11,"short_name":"page_facing_up","short_names":["page_facing_up"],"text":null,"texts":null,"category":"Objects","sort_order":131,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CALENDAR","unified":"1F4C5","variations":[],"docomo":null,"au":"E563","softbank":null,"google":"FE542","image":"1f4c5.png","sheet_x":21,"sheet_y":12,"short_name":"date","short_names":["date"],"text":null,"texts":null,"category":"Objects","sort_order":132,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TEAR-OFF CALENDAR","unified":"1F4C6","variations":[],"docomo":null,"au":"E56A","softbank":null,"google":"FE549","image":"1f4c6.png","sheet_x":21,"sheet_y":13,"short_name":"calendar","short_names":["calendar"],"text":null,"texts":null,"category":"Objects","sort_order":133,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CARD INDEX","unified":"1F4C7","variations":[],"docomo":"E683","au":"E56C","softbank":"E148","google":"FE54D","image":"1f4c7.png","sheet_x":21,"sheet_y":14,"short_name":"card_index","short_names":["card_index"],"text":null,"texts":null,"category":"Objects","sort_order":135,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CHART WITH UPWARDS TREND","unified":"1F4C8","variations":[],"docomo":null,"au":"E575","softbank":"E14A","google":"FE54B","image":"1f4c8.png","sheet_x":21,"sheet_y":15,"short_name":"chart_with_upwards_trend","short_names":["chart_with_upwards_trend"],"text":null,"texts":null,"category":"Objects","sort_order":129,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CHART WITH DOWNWARDS TREND","unified":"1F4C9","variations":[],"docomo":null,"au":"E576","softbank":null,"google":"FE54C","image":"1f4c9.png","sheet_x":21,"sheet_y":16,"short_name":"chart_with_downwards_trend","short_names":["chart_with_downwards_trend"],"text":null,"texts":null,"category":"Objects","sort_order":130,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BAR CHART","unified":"1F4CA","variations":[],"docomo":null,"au":"E574","softbank":"E14A","google":"FE54A","image":"1f4ca.png","sheet_x":21,"sheet_y":17,"short_name":"bar_chart","short_names":["bar_chart"],"text":null,"texts":null,"category":"Objects","sort_order":128,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLIPBOARD","unified":"1F4CB","variations":[],"docomo":"E689","au":"E564","softbank":"E301","google":"FE548","image":"1f4cb.png","sheet_x":21,"sheet_y":18,"short_name":"clipboard","short_names":["clipboard"],"text":null,"texts":null,"category":"Objects","sort_order":139,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PUSHPIN","unified":"1F4CC","variations":[],"docomo":null,"au":"E56D","softbank":null,"google":"FE54E","image":"1f4cc.png","sheet_x":21,"sheet_y":19,"short_name":"pushpin","short_names":["pushpin"],"text":null,"texts":null,"category":"Objects","sort_order":161,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ROUND PUSHPIN","unified":"1F4CD","variations":[],"docomo":null,"au":"E560","softbank":null,"google":"FE53F","image":"1f4cd.png","sheet_x":21,"sheet_y":20,"short_name":"round_pushpin","short_names":["round_pushpin"],"text":null,"texts":null,"category":"Objects","sort_order":162,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PAPERCLIP","unified":"1F4CE","variations":[],"docomo":"E730","au":"E4A0","softbank":null,"google":"FE53A","image":"1f4ce.png","sheet_x":21,"sheet_y":21,"short_name":"paperclip","short_names":["paperclip"],"text":null,"texts":null,"category":"Objects","sort_order":156,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"STRAIGHT RULER","unified":"1F4CF","variations":[],"docomo":null,"au":"E570","softbank":null,"google":"FE550","image":"1f4cf.png","sheet_x":21,"sheet_y":22,"short_name":"straight_ruler","short_names":["straight_ruler"],"text":null,"texts":null,"category":"Objects","sort_order":160,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TRIANGULAR RULER","unified":"1F4D0","variations":[],"docomo":null,"au":"E4A2","softbank":null,"google":"FE551","image":"1f4d0.png","sheet_x":21,"sheet_y":23,"short_name":"triangular_ruler","short_names":["triangular_ruler"],"text":null,"texts":null,"category":"Objects","sort_order":159,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BOOKMARK TABS","unified":"1F4D1","variations":[],"docomo":"E689","au":"EB0B","softbank":"E301","google":"FE552","image":"1f4d1.png","sheet_x":21,"sheet_y":24,"short_name":"bookmark_tabs","short_names":["bookmark_tabs"],"text":null,"texts":null,"category":"Objects","sort_order":127,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LEDGER","unified":"1F4D2","variations":[],"docomo":"E683","au":"E56E","softbank":"E148","google":"FE54F","image":"1f4d2.png","sheet_x":21,"sheet_y":25,"short_name":"ledger","short_names":["ledger"],"text":null,"texts":null,"category":"Objects","sort_order":152,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NOTEBOOK","unified":"1F4D3","variations":[],"docomo":"E683","au":"E56B","softbank":"E148","google":"FE545","image":"1f4d3.png","sheet_x":21,"sheet_y":26,"short_name":"notebook","short_names":["notebook"],"text":null,"texts":null,"category":"Objects","sort_order":146,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NOTEBOOK WITH DECORATIVE COVER","unified":"1F4D4","variations":[],"docomo":"E683","au":"E49D","softbank":"E148","google":"FE547","image":"1f4d4.png","sheet_x":21,"sheet_y":27,"short_name":"notebook_with_decorative_cover","short_names":["notebook_with_decorative_cover"],"text":null,"texts":null,"category":"Objects","sort_order":151,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOSED BOOK","unified":"1F4D5","variations":[],"docomo":"E683","au":"E568","softbank":"E148","google":"FE502","image":"1f4d5.png","sheet_x":21,"sheet_y":28,"short_name":"closed_book","short_names":["closed_book"],"text":null,"texts":null,"category":"Objects","sort_order":147,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"OPEN BOOK","unified":"1F4D6","variations":[],"docomo":"E683","au":"E49F","softbank":"E148","google":"FE546","image":"1f4d6.png","sheet_x":21,"sheet_y":29,"short_name":"book","short_names":["book","open_book"],"text":null,"texts":null,"category":"Objects","sort_order":154,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GREEN BOOK","unified":"1F4D7","variations":[],"docomo":"E683","au":"E565","softbank":"E148","google":"FE4FF","image":"1f4d7.png","sheet_x":21,"sheet_y":30,"short_name":"green_book","short_names":["green_book"],"text":null,"texts":null,"category":"Objects","sort_order":148,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLUE BOOK","unified":"1F4D8","variations":[],"docomo":"E683","au":"E566","softbank":"E148","google":"FE500","image":"1f4d8.png","sheet_x":21,"sheet_y":31,"short_name":"blue_book","short_names":["blue_book"],"text":null,"texts":null,"category":"Objects","sort_order":149,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ORANGE BOOK","unified":"1F4D9","variations":[],"docomo":"E683","au":"E567","softbank":"E148","google":"FE501","image":"1f4d9.png","sheet_x":21,"sheet_y":32,"short_name":"orange_book","short_names":["orange_book"],"text":null,"texts":null,"category":"Objects","sort_order":150,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BOOKS","unified":"1F4DA","variations":[],"docomo":"E683","au":"E56F","softbank":"E148","google":"FE503","image":"1f4da.png","sheet_x":21,"sheet_y":33,"short_name":"books","short_names":["books"],"text":null,"texts":null,"category":"Objects","sort_order":153,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NAME BADGE","unified":"1F4DB","variations":[],"docomo":null,"au":"E51D","softbank":null,"google":"FE504","image":"1f4db.png","sheet_x":21,"sheet_y":34,"short_name":"name_badge","short_names":["name_badge"],"text":null,"texts":null,"category":"Symbols","sort_order":70,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SCROLL","unified":"1F4DC","variations":[],"docomo":"E70A","au":"E55F","softbank":null,"google":"FE4FD","image":"1f4dc.png","sheet_x":21,"sheet_y":35,"short_name":"scroll","short_names":["scroll"],"text":null,"texts":null,"category":"Objects","sort_order":125,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MEMO","unified":"1F4DD","variations":[],"docomo":"E689","au":"EA92","softbank":"E301","google":"FE527","image":"1f4dd.png","sheet_x":21,"sheet_y":36,"short_name":"memo","short_names":["memo","pencil"],"text":null,"texts":null,"category":"Objects","sort_order":173,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TELEPHONE RECEIVER","unified":"1F4DE","variations":[],"docomo":"E687","au":"E51E","softbank":"E009","google":"FE524","image":"1f4de.png","sheet_x":21,"sheet_y":37,"short_name":"telephone_receiver","short_names":["telephone_receiver"],"text":null,"texts":null,"category":"Objects","sort_order":23,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PAGER","unified":"1F4DF","variations":[],"docomo":"E65A","au":"E59B","softbank":null,"google":"FE522","image":"1f4df.png","sheet_x":21,"sheet_y":38,"short_name":"pager","short_names":["pager"],"text":null,"texts":null,"category":"Objects","sort_order":25,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FAX MACHINE","unified":"1F4E0","variations":[],"docomo":"E6D0","au":"E520","softbank":"E00B","google":"FE528","image":"1f4e0.png","sheet_x":21,"sheet_y":39,"short_name":"fax","short_names":["fax"],"text":null,"texts":null,"category":"Objects","sort_order":26,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SATELLITE ANTENNA","unified":"1F4E1","variations":[],"docomo":null,"au":"E4A8","softbank":"E14B","google":"FE531","image":"1f4e1.png","sheet_x":21,"sheet_y":40,"short_name":"satellite","short_names":["satellite"],"text":null,"texts":null,"category":"Objects","sort_order":38,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PUBLIC ADDRESS LOUDSPEAKER","unified":"1F4E2","variations":[],"docomo":null,"au":"E511","softbank":"E142","google":"FE52F","image":"1f4e2.png","sheet_x":22,"sheet_y":0,"short_name":"loudspeaker","short_names":["loudspeaker"],"text":null,"texts":null,"category":"Symbols","sort_order":232,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CHEERING MEGAPHONE","unified":"1F4E3","variations":[],"docomo":null,"au":"E511","softbank":"E317","google":"FE530","image":"1f4e3.png","sheet_x":22,"sheet_y":1,"short_name":"mega","short_names":["mega"],"text":null,"texts":null,"category":"Symbols","sort_order":231,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"OUTBOX TRAY","unified":"1F4E4","variations":[],"docomo":null,"au":"E592","softbank":null,"google":"FE533","image":"1f4e4.png","sheet_x":22,"sheet_y":2,"short_name":"outbox_tray","short_names":["outbox_tray"],"text":null,"texts":null,"category":"Objects","sort_order":124,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"INBOX TRAY","unified":"1F4E5","variations":[],"docomo":null,"au":"E593","softbank":null,"google":"FE534","image":"1f4e5.png","sheet_x":22,"sheet_y":3,"short_name":"inbox_tray","short_names":["inbox_tray"],"text":null,"texts":null,"category":"Objects","sort_order":123,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PACKAGE","unified":"1F4E6","variations":[],"docomo":"E685","au":"E51F","softbank":"E112","google":"FE535","image":"1f4e6.png","sheet_x":22,"sheet_y":4,"short_name":"package","short_names":["package"],"text":null,"texts":null,"category":"Objects","sort_order":121,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"E-MAIL SYMBOL","unified":"1F4E7","variations":[],"docomo":"E6D3","au":"EB71","softbank":"E103","google":"FEB92","image":"1f4e7.png","sheet_x":22,"sheet_y":5,"short_name":"e-mail","short_names":["e-mail"],"text":null,"texts":null,"category":"Objects","sort_order":114,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"INCOMING ENVELOPE","unified":"1F4E8","variations":[],"docomo":"E6CF","au":"E591","softbank":"E103","google":"FE52A","image":"1f4e8.png","sheet_x":22,"sheet_y":6,"short_name":"incoming_envelope","short_names":["incoming_envelope"],"text":null,"texts":null,"category":"Objects","sort_order":113,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ENVELOPE WITH DOWNWARDS ARROW ABOVE","unified":"1F4E9","variations":[],"docomo":"E6CF","au":"EB62","softbank":"E103","google":"FE52B","image":"1f4e9.png","sheet_x":22,"sheet_y":7,"short_name":"envelope_with_arrow","short_names":["envelope_with_arrow"],"text":null,"texts":null,"category":"Objects","sort_order":112,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOSED MAILBOX WITH LOWERED FLAG","unified":"1F4EA","variations":[],"docomo":"E665","au":"E51B","softbank":"E101","google":"FE52C","image":"1f4ea.png","sheet_x":22,"sheet_y":8,"short_name":"mailbox_closed","short_names":["mailbox_closed"],"text":null,"texts":null,"category":"Objects","sort_order":117,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOSED MAILBOX WITH RAISED FLAG","unified":"1F4EB","variations":[],"docomo":"E665","au":"EB0A","softbank":"E101","google":"FE52D","image":"1f4eb.png","sheet_x":22,"sheet_y":9,"short_name":"mailbox","short_names":["mailbox"],"text":null,"texts":null,"category":"Objects","sort_order":118,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"OPEN MAILBOX WITH RAISED FLAG","unified":"1F4EC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4ec.png","sheet_x":22,"sheet_y":10,"short_name":"mailbox_with_mail","short_names":["mailbox_with_mail"],"text":null,"texts":null,"category":"Objects","sort_order":119,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"OPEN MAILBOX WITH LOWERED FLAG","unified":"1F4ED","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4ed.png","sheet_x":22,"sheet_y":11,"short_name":"mailbox_with_no_mail","short_names":["mailbox_with_no_mail"],"text":null,"texts":null,"category":"Objects","sort_order":120,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"POSTBOX","unified":"1F4EE","variations":[],"docomo":"E665","au":"E51B","softbank":"E102","google":"FE52E","image":"1f4ee.png","sheet_x":22,"sheet_y":12,"short_name":"postbox","short_names":["postbox"],"text":null,"texts":null,"category":"Objects","sort_order":116,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"POSTAL HORN","unified":"1F4EF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4ef.png","sheet_x":22,"sheet_y":13,"short_name":"postal_horn","short_names":["postal_horn"],"text":null,"texts":null,"category":"Objects","sort_order":122,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NEWSPAPER","unified":"1F4F0","variations":[],"docomo":null,"au":"E58B","softbank":null,"google":"FE822","image":"1f4f0.png","sheet_x":22,"sheet_y":14,"short_name":"newspaper","short_names":["newspaper"],"text":null,"texts":null,"category":"Objects","sort_order":145,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MOBILE PHONE","unified":"1F4F1","variations":[],"docomo":"E688","au":"E588","softbank":"E00A","google":"FE525","image":"1f4f1.png","sheet_x":22,"sheet_y":15,"short_name":"iphone","short_names":["iphone"],"text":null,"texts":null,"category":"Objects","sort_order":2,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MOBILE PHONE WITH RIGHTWARDS ARROW AT LEFT","unified":"1F4F2","variations":[],"docomo":"E6CE","au":"EB08","softbank":"E104","google":"FE526","image":"1f4f2.png","sheet_x":22,"sheet_y":16,"short_name":"calling","short_names":["calling"],"text":null,"texts":null,"category":"Objects","sort_order":3,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"VIBRATION MODE","unified":"1F4F3","variations":[],"docomo":null,"au":"EA90","softbank":"E250","google":"FE839","image":"1f4f3.png","sheet_x":22,"sheet_y":17,"short_name":"vibration_mode","short_names":["vibration_mode"],"text":null,"texts":null,"category":"Symbols","sort_order":47,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MOBILE PHONE OFF","unified":"1F4F4","variations":[],"docomo":null,"au":"EA91","softbank":"E251","google":"FE83A","image":"1f4f4.png","sheet_x":22,"sheet_y":18,"short_name":"mobile_phone_off","short_names":["mobile_phone_off"],"text":null,"texts":null,"category":"Symbols","sort_order":46,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NO MOBILE PHONES","unified":"1F4F5","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4f5.png","sheet_x":22,"sheet_y":19,"short_name":"no_mobile_phones","short_names":["no_mobile_phones"],"text":null,"texts":null,"category":"Symbols","sort_order":81,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ANTENNA WITH BARS","unified":"1F4F6","variations":[],"docomo":null,"au":"EA84","softbank":"E20B","google":"FE838","image":"1f4f6.png","sheet_x":22,"sheet_y":20,"short_name":"signal_strength","short_names":["signal_strength"],"text":null,"texts":null,"category":"Symbols","sort_order":126,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CAMERA","unified":"1F4F7","variations":[],"docomo":"E681","au":"E515","softbank":"E008","google":"FE4EF","image":"1f4f7.png","sheet_x":22,"sheet_y":21,"short_name":"camera","short_names":["camera"],"text":null,"texts":null,"category":"Objects","sort_order":17,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CAMERA WITH FLASH","unified":"1F4F8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4f8.png","sheet_x":22,"sheet_y":22,"short_name":"camera_with_flash","short_names":["camera_with_flash"],"text":null,"texts":null,"category":"Objects","sort_order":18,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"VIDEO CAMERA","unified":"1F4F9","variations":[],"docomo":"E677","au":"E57E","softbank":"E03D","google":"FE4F9","image":"1f4f9.png","sheet_x":22,"sheet_y":23,"short_name":"video_camera","short_names":["video_camera"],"text":null,"texts":null,"category":"Objects","sort_order":19,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TELEVISION","unified":"1F4FA","variations":[],"docomo":"E68A","au":"E502","softbank":"E12A","google":"FE81C","image":"1f4fa.png","sheet_x":22,"sheet_y":24,"short_name":"tv","short_names":["tv"],"text":null,"texts":null,"category":"Objects","sort_order":27,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RADIO","unified":"1F4FB","variations":[],"docomo":null,"au":"E5B9","softbank":"E128","google":"FE81F","image":"1f4fb.png","sheet_x":22,"sheet_y":25,"short_name":"radio","short_names":["radio"],"text":null,"texts":null,"category":"Objects","sort_order":28,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"VIDEOCASSETTE","unified":"1F4FC","variations":[],"docomo":null,"au":"E580","softbank":"E129","google":"FE820","image":"1f4fc.png","sheet_x":22,"sheet_y":26,"short_name":"vhs","short_names":["vhs"],"text":null,"texts":null,"category":"Objects","sort_order":16,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FILM PROJECTOR","unified":"1F4FD","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4fd.png","sheet_x":22,"sheet_y":27,"short_name":"film_projector","short_names":["film_projector"],"text":null,"texts":null,"category":"Objects","sort_order":21,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PRAYER BEADS","unified":"1F4FF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f4ff.png","sheet_x":22,"sheet_y":28,"short_name":"prayer_beads","short_names":["prayer_beads"],"text":null,"texts":null,"category":"Objects","sort_order":75,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TWISTED RIGHTWARDS ARROWS","unified":"1F500","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f500.png","sheet_x":22,"sheet_y":29,"short_name":"twisted_rightwards_arrows","short_names":["twisted_rightwards_arrows"],"text":null,"texts":null,"category":"Symbols","sort_order":155,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS","unified":"1F501","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f501.png","sheet_x":22,"sheet_y":30,"short_name":"repeat","short_names":["repeat"],"text":null,"texts":null,"category":"Symbols","sort_order":156,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS WITH CIRCLED ONE OVERLAY","unified":"1F502","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f502.png","sheet_x":22,"sheet_y":31,"short_name":"repeat_one","short_names":["repeat_one"],"text":null,"texts":null,"category":"Symbols","sort_order":157,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCKWISE DOWNWARDS AND UPWARDS OPEN CIRCLE ARROWS","unified":"1F503","variations":[],"docomo":"E735","au":"EB0D","softbank":null,"google":"FEB91","image":"1f503.png","sheet_x":22,"sheet_y":32,"short_name":"arrows_clockwise","short_names":["arrows_clockwise"],"text":null,"texts":null,"category":"Symbols","sort_order":190,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ANTICLOCKWISE DOWNWARDS AND UPWARDS OPEN CIRCLE ARROWS","unified":"1F504","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f504.png","sheet_x":22,"sheet_y":33,"short_name":"arrows_counterclockwise","short_names":["arrows_counterclockwise"],"text":null,"texts":null,"category":"Symbols","sort_order":173,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LOW BRIGHTNESS SYMBOL","unified":"1F505","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f505.png","sheet_x":22,"sheet_y":34,"short_name":"low_brightness","short_names":["low_brightness"],"text":null,"texts":null,"category":"Symbols","sort_order":89,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HIGH BRIGHTNESS SYMBOL","unified":"1F506","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f506.png","sheet_x":22,"sheet_y":35,"short_name":"high_brightness","short_names":["high_brightness"],"text":null,"texts":null,"category":"Symbols","sort_order":90,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPEAKER WITH CANCELLATION STROKE","unified":"1F507","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f507.png","sheet_x":22,"sheet_y":36,"short_name":"mute","short_names":["mute"],"text":null,"texts":null,"category":"Symbols","sort_order":230,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPEAKER","unified":"1F508","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f508.png","sheet_x":22,"sheet_y":37,"short_name":"speaker","short_names":["speaker"],"text":null,"texts":null,"category":"Symbols","sort_order":227,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPEAKER WITH ONE SOUND WAVE","unified":"1F509","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f509.png","sheet_x":22,"sheet_y":38,"short_name":"sound","short_names":["sound"],"text":null,"texts":null,"category":"Symbols","sort_order":228,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPEAKER WITH THREE SOUND WAVES","unified":"1F50A","variations":[],"docomo":null,"au":"E511","softbank":"E141","google":"FE821","image":"1f50a.png","sheet_x":22,"sheet_y":39,"short_name":"loud_sound","short_names":["loud_sound"],"text":null,"texts":null,"category":"Symbols","sort_order":229,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BATTERY","unified":"1F50B","variations":[],"docomo":null,"au":"E584","softbank":null,"google":"FE4FC","image":"1f50b.png","sheet_x":22,"sheet_y":40,"short_name":"battery","short_names":["battery"],"text":null,"texts":null,"category":"Objects","sort_order":39,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ELECTRIC PLUG","unified":"1F50C","variations":[],"docomo":null,"au":"E589","softbank":null,"google":"FE4FE","image":"1f50c.png","sheet_x":23,"sheet_y":0,"short_name":"electric_plug","short_names":["electric_plug"],"text":null,"texts":null,"category":"Objects","sort_order":40,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LEFT-POINTING MAGNIFYING GLASS","unified":"1F50D","variations":[],"docomo":"E6DC","au":"E518","softbank":"E114","google":"FEB85","image":"1f50d.png","sheet_x":23,"sheet_y":1,"short_name":"mag","short_names":["mag"],"text":null,"texts":null,"category":"Objects","sort_order":177,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RIGHT-POINTING MAGNIFYING GLASS","unified":"1F50E","variations":[],"docomo":"E6DC","au":"EB05","softbank":"E114","google":"FEB8D","image":"1f50e.png","sheet_x":23,"sheet_y":2,"short_name":"mag_right","short_names":["mag_right"],"text":null,"texts":null,"category":"Objects","sort_order":178,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LOCK WITH INK PEN","unified":"1F50F","variations":[],"docomo":"E6D9","au":"EB0C","softbank":"E144","google":"FEB90","image":"1f50f.png","sheet_x":23,"sheet_y":3,"short_name":"lock_with_ink_pen","short_names":["lock_with_ink_pen"],"text":null,"texts":null,"category":"Objects","sort_order":169,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOSED LOCK WITH KEY","unified":"1F510","variations":[],"docomo":"E6D9","au":"EAFC","softbank":"E144","google":"FEB8A","image":"1f510.png","sheet_x":23,"sheet_y":4,"short_name":"closed_lock_with_key","short_names":["closed_lock_with_key"],"text":null,"texts":null,"category":"Objects","sort_order":166,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KEY","unified":"1F511","variations":[],"docomo":"E6D9","au":"E519","softbank":"E03F","google":"FEB82","image":"1f511.png","sheet_x":23,"sheet_y":5,"short_name":"key","short_names":["key"],"text":null,"texts":null,"category":"Objects","sort_order":89,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LOCK","unified":"1F512","variations":[],"docomo":"E6D9","au":"E51C","softbank":"E144","google":"FEB86","image":"1f512.png","sheet_x":23,"sheet_y":6,"short_name":"lock","short_names":["lock"],"text":null,"texts":null,"category":"Objects","sort_order":167,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"OPEN LOCK","unified":"1F513","variations":[],"docomo":"E6D9","au":"E51C","softbank":"E145","google":"FEB87","image":"1f513.png","sheet_x":23,"sheet_y":7,"short_name":"unlock","short_names":["unlock"],"text":null,"texts":null,"category":"Objects","sort_order":168,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BELL","unified":"1F514","variations":[],"docomo":"E713","au":"E512","softbank":"E325","google":"FE4F2","image":"1f514.png","sheet_x":23,"sheet_y":8,"short_name":"bell","short_names":["bell"],"text":null,"texts":null,"category":"Symbols","sort_order":233,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BELL WITH CANCELLATION STROKE","unified":"1F515","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f515.png","sheet_x":23,"sheet_y":9,"short_name":"no_bell","short_names":["no_bell"],"text":null,"texts":null,"category":"Symbols","sort_order":234,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BOOKMARK","unified":"1F516","variations":[],"docomo":null,"au":"EB07","softbank":null,"google":"FEB8F","image":"1f516.png","sheet_x":23,"sheet_y":10,"short_name":"bookmark","short_names":["bookmark"],"text":null,"texts":null,"category":"Objects","sort_order":85,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LINK SYMBOL","unified":"1F517","variations":[],"docomo":null,"au":"E58A","softbank":null,"google":"FEB4B","image":"1f517.png","sheet_x":23,"sheet_y":11,"short_name":"link","short_names":["link"],"text":null,"texts":null,"category":"Objects","sort_order":155,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RADIO BUTTON","unified":"1F518","variations":[],"docomo":null,"au":"EB04","softbank":null,"google":"FEB8C","image":"1f518.png","sheet_x":23,"sheet_y":12,"short_name":"radio_button","short_names":["radio_button"],"text":null,"texts":null,"category":"Symbols","sort_order":206,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BACK WITH LEFTWARDS ARROW ABOVE","unified":"1F519","variations":[],"docomo":null,"au":"EB06","softbank":"E235","google":"FEB8E","image":"1f519.png","sheet_x":23,"sheet_y":13,"short_name":"back","short_names":["back"],"text":null,"texts":null,"category":"Symbols","sort_order":201,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"END WITH LEFTWARDS ARROW ABOVE","unified":"1F51A","variations":[],"docomo":"E6B9","au":null,"softbank":null,"google":"FE01A","image":"1f51a.png","sheet_x":23,"sheet_y":14,"short_name":"end","short_names":["end"],"text":null,"texts":null,"category":"Symbols","sort_order":200,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ON WITH EXCLAMATION MARK WITH LEFT RIGHT ARROW ABOVE","unified":"1F51B","variations":[],"docomo":"E6B8","au":null,"softbank":null,"google":"FE019","image":"1f51b.png","sheet_x":23,"sheet_y":15,"short_name":"on","short_names":["on"],"text":null,"texts":null,"category":"Symbols","sort_order":202,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SOON WITH RIGHTWARDS ARROW ABOVE","unified":"1F51C","variations":[],"docomo":"E6B7","au":null,"softbank":null,"google":"FE018","image":"1f51c.png","sheet_x":23,"sheet_y":16,"short_name":"soon","short_names":["soon"],"text":null,"texts":null,"category":"Symbols","sort_order":204,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TOP WITH UPWARDS ARROW ABOVE","unified":"1F51D","variations":[],"docomo":null,"au":null,"softbank":"E24C","google":"FEB42","image":"1f51d.png","sheet_x":23,"sheet_y":17,"short_name":"top","short_names":["top"],"text":null,"texts":null,"category":"Symbols","sort_order":203,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NO ONE UNDER EIGHTEEN SYMBOL","unified":"1F51E","variations":[],"docomo":null,"au":"EA83","softbank":"E207","google":"FEB25","image":"1f51e.png","sheet_x":23,"sheet_y":18,"short_name":"underage","short_names":["underage"],"text":null,"texts":null,"category":"Symbols","sort_order":80,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KEYCAP TEN","unified":"1F51F","variations":[],"docomo":null,"au":"E52B","softbank":null,"google":"FE83B","image":"1f51f.png","sheet_x":23,"sheet_y":19,"short_name":"keycap_ten","short_names":["keycap_ten"],"text":null,"texts":null,"category":"Symbols","sort_order":144,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"INPUT SYMBOL FOR LATIN CAPITAL LETTERS","unified":"1F520","variations":[],"docomo":null,"au":"EAFD","softbank":null,"google":"FEB7C","image":"1f520.png","sheet_x":23,"sheet_y":20,"short_name":"capital_abcd","short_names":["capital_abcd"],"text":null,"texts":null,"category":"Symbols","sort_order":183,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"INPUT SYMBOL FOR LATIN SMALL LETTERS","unified":"1F521","variations":[],"docomo":null,"au":"EAFE","softbank":null,"google":"FEB7D","image":"1f521.png","sheet_x":23,"sheet_y":21,"short_name":"abcd","short_names":["abcd"],"text":null,"texts":null,"category":"Symbols","sort_order":182,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"INPUT SYMBOL FOR NUMBERS","unified":"1F522","variations":[],"docomo":null,"au":"EAFF","softbank":null,"google":"FEB7E","image":"1f522.png","sheet_x":23,"sheet_y":22,"short_name":"1234","short_names":["1234"],"text":null,"texts":null,"category":"Symbols","sort_order":145,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"INPUT SYMBOL FOR SYMBOLS","unified":"1F523","variations":[],"docomo":null,"au":"EB00","softbank":null,"google":"FEB7F","image":"1f523.png","sheet_x":23,"sheet_y":23,"short_name":"symbols","short_names":["symbols"],"text":null,"texts":null,"category":"Symbols","sort_order":184,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"INPUT SYMBOL FOR LATIN LETTERS","unified":"1F524","variations":[],"docomo":null,"au":"EB55","softbank":null,"google":"FEB80","image":"1f524.png","sheet_x":23,"sheet_y":24,"short_name":"abc","short_names":["abc"],"text":null,"texts":null,"category":"Symbols","sort_order":181,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FIRE","unified":"1F525","variations":[],"docomo":null,"au":"E47B","softbank":"E11D","google":"FE4F6","image":"1f525.png","sheet_x":23,"sheet_y":25,"short_name":"fire","short_names":["fire"],"text":null,"texts":null,"category":"Nature","sort_order":133,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ELECTRIC TORCH","unified":"1F526","variations":[],"docomo":"E6FB","au":"E583","softbank":null,"google":"FE4FB","image":"1f526.png","sheet_x":23,"sheet_y":26,"short_name":"flashlight","short_names":["flashlight"],"text":null,"texts":null,"category":"Objects","sort_order":42,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WRENCH","unified":"1F527","variations":[],"docomo":"E718","au":"E587","softbank":null,"google":"FE4C9","image":"1f527.png","sheet_x":23,"sheet_y":27,"short_name":"wrench","short_names":["wrench"],"text":null,"texts":null,"category":"Objects","sort_order":55,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HAMMER","unified":"1F528","variations":[],"docomo":null,"au":"E5CB","softbank":"E116","google":"FE4CA","image":"1f528.png","sheet_x":23,"sheet_y":28,"short_name":"hammer","short_names":["hammer"],"text":null,"texts":null,"category":"Objects","sort_order":56,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NUT AND BOLT","unified":"1F529","variations":[],"docomo":null,"au":"E581","softbank":null,"google":"FE4CB","image":"1f529.png","sheet_x":23,"sheet_y":29,"short_name":"nut_and_bolt","short_names":["nut_and_bolt"],"text":null,"texts":null,"category":"Objects","sort_order":60,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HOCHO","unified":"1F52A","variations":[],"docomo":null,"au":"E57F","softbank":null,"google":"FE4FA","image":"1f52a.png","sheet_x":23,"sheet_y":30,"short_name":"hocho","short_names":["hocho","knife"],"text":null,"texts":null,"category":"Objects","sort_order":65,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PISTOL","unified":"1F52B","variations":[],"docomo":null,"au":"E50A","softbank":"E113","google":"FE4F5","image":"1f52b.png","sheet_x":23,"sheet_y":31,"short_name":"gun","short_names":["gun"],"text":null,"texts":null,"category":"Objects","sort_order":63,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MICROSCOPE","unified":"1F52C","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f52c.png","sheet_x":23,"sheet_y":32,"short_name":"microscope","short_names":["microscope"],"text":null,"texts":null,"category":"Objects","sort_order":79,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TELESCOPE","unified":"1F52D","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f52d.png","sheet_x":23,"sheet_y":33,"short_name":"telescope","short_names":["telescope"],"text":null,"texts":null,"category":"Objects","sort_order":78,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CRYSTAL BALL","unified":"1F52E","variations":[],"docomo":null,"au":"EA8F","softbank":"E23E","google":"FE4F7","image":"1f52e.png","sheet_x":23,"sheet_y":34,"short_name":"crystal_ball","short_names":["crystal_ball"],"text":null,"texts":null,"category":"Objects","sort_order":74,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SIX POINTED STAR WITH MIDDLE DOT","unified":"1F52F","variations":[],"docomo":null,"au":"EA8F","softbank":"E23E","google":"FE4F8","image":"1f52f.png","sheet_x":23,"sheet_y":35,"short_name":"six_pointed_star","short_names":["six_pointed_star"],"text":null,"texts":null,"category":"Symbols","sort_order":22,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"JAPANESE SYMBOL FOR BEGINNER","unified":"1F530","variations":[],"docomo":null,"au":"E480","softbank":"E209","google":"FE044","image":"1f530.png","sheet_x":23,"sheet_y":36,"short_name":"beginner","short_names":["beginner"],"text":null,"texts":null,"category":"Symbols","sort_order":96,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TRIDENT EMBLEM","unified":"1F531","variations":[],"docomo":"E71A","au":"E5C9","softbank":"E031","google":"FE4D2","image":"1f531.png","sheet_x":23,"sheet_y":37,"short_name":"trident","short_names":["trident"],"text":null,"texts":null,"category":"Symbols","sort_order":91,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BLACK SQUARE BUTTON","unified":"1F532","variations":[],"docomo":"E69C","au":"E54B","softbank":"E21A","google":"FEB64","image":"1f532.png","sheet_x":23,"sheet_y":38,"short_name":"black_square_button","short_names":["black_square_button"],"text":null,"texts":null,"category":"Symbols","sort_order":225,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WHITE SQUARE BUTTON","unified":"1F533","variations":[],"docomo":"E69C","au":"E54B","softbank":"E21B","google":"FEB67","image":"1f533.png","sheet_x":23,"sheet_y":39,"short_name":"white_square_button","short_names":["white_square_button"],"text":null,"texts":null,"category":"Symbols","sort_order":226,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LARGE RED CIRCLE","unified":"1F534","variations":[],"docomo":"E69C","au":"E54A","softbank":"E219","google":"FEB63","image":"1f534.png","sheet_x":23,"sheet_y":40,"short_name":"red_circle","short_names":["red_circle"],"text":null,"texts":null,"category":"Symbols","sort_order":209,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LARGE BLUE CIRCLE","unified":"1F535","variations":[],"docomo":"E69C","au":"E54B","softbank":"E21A","google":"FEB64","image":"1f535.png","sheet_x":24,"sheet_y":0,"short_name":"large_blue_circle","short_names":["large_blue_circle"],"text":null,"texts":null,"category":"Symbols","sort_order":210,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LARGE ORANGE DIAMOND","unified":"1F536","variations":[],"docomo":null,"au":"E546","softbank":"E21B","google":"FEB73","image":"1f536.png","sheet_x":24,"sheet_y":1,"short_name":"large_orange_diamond","short_names":["large_orange_diamond"],"text":null,"texts":null,"category":"Symbols","sort_order":213,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LARGE BLUE DIAMOND","unified":"1F537","variations":[],"docomo":null,"au":"E547","softbank":"E21B","google":"FEB74","image":"1f537.png","sheet_x":24,"sheet_y":2,"short_name":"large_blue_diamond","short_names":["large_blue_diamond"],"text":null,"texts":null,"category":"Symbols","sort_order":214,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SMALL ORANGE DIAMOND","unified":"1F538","variations":[],"docomo":null,"au":"E536","softbank":"E21B","google":"FEB75","image":"1f538.png","sheet_x":24,"sheet_y":3,"short_name":"small_orange_diamond","short_names":["small_orange_diamond"],"text":null,"texts":null,"category":"Symbols","sort_order":211,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SMALL BLUE DIAMOND","unified":"1F539","variations":[],"docomo":null,"au":"E537","softbank":"E21B","google":"FEB76","image":"1f539.png","sheet_x":24,"sheet_y":4,"short_name":"small_blue_diamond","short_names":["small_blue_diamond"],"text":null,"texts":null,"category":"Symbols","sort_order":212,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"UP-POINTING RED TRIANGLE","unified":"1F53A","variations":[],"docomo":null,"au":"E55A","softbank":null,"google":"FEB78","image":"1f53a.png","sheet_x":24,"sheet_y":5,"short_name":"small_red_triangle","short_names":["small_red_triangle"],"text":null,"texts":null,"category":"Symbols","sort_order":215,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DOWN-POINTING RED TRIANGLE","unified":"1F53B","variations":[],"docomo":null,"au":"E55B","softbank":null,"google":"FEB79","image":"1f53b.png","sheet_x":24,"sheet_y":6,"short_name":"small_red_triangle_down","short_names":["small_red_triangle_down"],"text":null,"texts":null,"category":"Symbols","sort_order":220,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"UP-POINTING SMALL RED TRIANGLE","unified":"1F53C","variations":[],"docomo":null,"au":"E543","softbank":null,"google":"FEB01","image":"1f53c.png","sheet_x":24,"sheet_y":7,"short_name":"arrow_up_small","short_names":["arrow_up_small"],"text":null,"texts":null,"category":"Symbols","sort_order":159,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DOWN-POINTING SMALL RED TRIANGLE","unified":"1F53D","variations":[],"docomo":null,"au":"E542","softbank":null,"google":"FEB00","image":"1f53d.png","sheet_x":24,"sheet_y":8,"short_name":"arrow_down_small","short_names":["arrow_down_small"],"text":null,"texts":null,"category":"Symbols","sort_order":160,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"OM SYMBOL","unified":"1F549","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f549.png","sheet_x":24,"sheet_y":9,"short_name":"om_symbol","short_names":["om_symbol"],"text":null,"texts":null,"category":"Symbols","sort_order":19,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DOVE OF PEACE","unified":"1F54A","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f54a.png","sheet_x":24,"sheet_y":10,"short_name":"dove_of_peace","short_names":["dove_of_peace"],"text":null,"texts":null,"category":"Nature","sort_order":65,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KAABA","unified":"1F54B","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f54b.png","sheet_x":24,"sheet_y":11,"short_name":"kaaba","short_names":["kaaba"],"text":null,"texts":null,"category":"Places","sort_order":114,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MOSQUE","unified":"1F54C","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f54c.png","sheet_x":24,"sheet_y":12,"short_name":"mosque","short_names":["mosque"],"text":null,"texts":null,"category":"Places","sort_order":112,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SYNAGOGUE","unified":"1F54D","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f54d.png","sheet_x":24,"sheet_y":13,"short_name":"synagogue","short_names":["synagogue"],"text":null,"texts":null,"category":"Places","sort_order":113,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MENORAH WITH NINE BRANCHES","unified":"1F54E","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f54e.png","sheet_x":24,"sheet_y":14,"short_name":"menorah_with_nine_branches","short_names":["menorah_with_nine_branches"],"text":null,"texts":null,"category":"Symbols","sort_order":23,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE ONE OCLOCK","unified":"1F550","variations":[],"docomo":"E6BA","au":"E594","softbank":"E024","google":"FE01E","image":"1f550.png","sheet_x":24,"sheet_y":15,"short_name":"clock1","short_names":["clock1"],"text":null,"texts":null,"category":"Symbols","sort_order":246,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE TWO OCLOCK","unified":"1F551","variations":[],"docomo":"E6BA","au":"E594","softbank":"E025","google":"FE01F","image":"1f551.png","sheet_x":24,"sheet_y":16,"short_name":"clock2","short_names":["clock2"],"text":null,"texts":null,"category":"Symbols","sort_order":247,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE THREE OCLOCK","unified":"1F552","variations":[],"docomo":"E6BA","au":"E594","softbank":"E026","google":"FE020","image":"1f552.png","sheet_x":24,"sheet_y":17,"short_name":"clock3","short_names":["clock3"],"text":null,"texts":null,"category":"Symbols","sort_order":248,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE FOUR OCLOCK","unified":"1F553","variations":[],"docomo":"E6BA","au":"E594","softbank":"E027","google":"FE021","image":"1f553.png","sheet_x":24,"sheet_y":18,"short_name":"clock4","short_names":["clock4"],"text":null,"texts":null,"category":"Symbols","sort_order":249,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE FIVE OCLOCK","unified":"1F554","variations":[],"docomo":"E6BA","au":"E594","softbank":"E028","google":"FE022","image":"1f554.png","sheet_x":24,"sheet_y":19,"short_name":"clock5","short_names":["clock5"],"text":null,"texts":null,"category":"Symbols","sort_order":250,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE SIX OCLOCK","unified":"1F555","variations":[],"docomo":"E6BA","au":"E594","softbank":"E029","google":"FE023","image":"1f555.png","sheet_x":24,"sheet_y":20,"short_name":"clock6","short_names":["clock6"],"text":null,"texts":null,"category":"Symbols","sort_order":251,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE SEVEN OCLOCK","unified":"1F556","variations":[],"docomo":"E6BA","au":"E594","softbank":"E02A","google":"FE024","image":"1f556.png","sheet_x":24,"sheet_y":21,"short_name":"clock7","short_names":["clock7"],"text":null,"texts":null,"category":"Symbols","sort_order":252,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE EIGHT OCLOCK","unified":"1F557","variations":[],"docomo":"E6BA","au":"E594","softbank":"E02B","google":"FE025","image":"1f557.png","sheet_x":24,"sheet_y":22,"short_name":"clock8","short_names":["clock8"],"text":null,"texts":null,"category":"Symbols","sort_order":253,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE NINE OCLOCK","unified":"1F558","variations":[],"docomo":"E6BA","au":"E594","softbank":"E02C","google":"FE026","image":"1f558.png","sheet_x":24,"sheet_y":23,"short_name":"clock9","short_names":["clock9"],"text":null,"texts":null,"category":"Symbols","sort_order":254,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE TEN OCLOCK","unified":"1F559","variations":[],"docomo":"E6BA","au":"E594","softbank":"E02D","google":"FE027","image":"1f559.png","sheet_x":24,"sheet_y":24,"short_name":"clock10","short_names":["clock10"],"text":null,"texts":null,"category":"Symbols","sort_order":255,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE ELEVEN OCLOCK","unified":"1F55A","variations":[],"docomo":"E6BA","au":"E594","softbank":"E02E","google":"FE028","image":"1f55a.png","sheet_x":24,"sheet_y":25,"short_name":"clock11","short_names":["clock11"],"text":null,"texts":null,"category":"Symbols","sort_order":256,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE TWELVE OCLOCK","unified":"1F55B","variations":[],"docomo":"E6BA","au":"E594","softbank":"E02F","google":"FE029","image":"1f55b.png","sheet_x":24,"sheet_y":26,"short_name":"clock12","short_names":["clock12"],"text":null,"texts":null,"category":"Symbols","sort_order":257,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE ONE-THIRTY","unified":"1F55C","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f55c.png","sheet_x":24,"sheet_y":27,"short_name":"clock130","short_names":["clock130"],"text":null,"texts":null,"category":"Symbols","sort_order":258,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE TWO-THIRTY","unified":"1F55D","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f55d.png","sheet_x":24,"sheet_y":28,"short_name":"clock230","short_names":["clock230"],"text":null,"texts":null,"category":"Symbols","sort_order":259,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE THREE-THIRTY","unified":"1F55E","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f55e.png","sheet_x":24,"sheet_y":29,"short_name":"clock330","short_names":["clock330"],"text":null,"texts":null,"category":"Symbols","sort_order":260,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE FOUR-THIRTY","unified":"1F55F","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f55f.png","sheet_x":24,"sheet_y":30,"short_name":"clock430","short_names":["clock430"],"text":null,"texts":null,"category":"Symbols","sort_order":261,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE FIVE-THIRTY","unified":"1F560","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f560.png","sheet_x":24,"sheet_y":31,"short_name":"clock530","short_names":["clock530"],"text":null,"texts":null,"category":"Symbols","sort_order":262,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE SIX-THIRTY","unified":"1F561","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f561.png","sheet_x":24,"sheet_y":32,"short_name":"clock630","short_names":["clock630"],"text":null,"texts":null,"category":"Symbols","sort_order":263,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE SEVEN-THIRTY","unified":"1F562","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f562.png","sheet_x":24,"sheet_y":33,"short_name":"clock730","short_names":["clock730"],"text":null,"texts":null,"category":"Symbols","sort_order":264,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE EIGHT-THIRTY","unified":"1F563","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f563.png","sheet_x":24,"sheet_y":34,"short_name":"clock830","short_names":["clock830"],"text":null,"texts":null,"category":"Symbols","sort_order":265,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE NINE-THIRTY","unified":"1F564","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f564.png","sheet_x":24,"sheet_y":35,"short_name":"clock930","short_names":["clock930"],"text":null,"texts":null,"category":"Symbols","sort_order":266,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE TEN-THIRTY","unified":"1F565","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f565.png","sheet_x":24,"sheet_y":36,"short_name":"clock1030","short_names":["clock1030"],"text":null,"texts":null,"category":"Symbols","sort_order":267,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE ELEVEN-THIRTY","unified":"1F566","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f566.png","sheet_x":24,"sheet_y":37,"short_name":"clock1130","short_names":["clock1130"],"text":null,"texts":null,"category":"Symbols","sort_order":268,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CLOCK FACE TWELVE-THIRTY","unified":"1F567","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f567.png","sheet_x":24,"sheet_y":38,"short_name":"clock1230","short_names":["clock1230"],"text":null,"texts":null,"category":"Symbols","sort_order":269,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CANDLE","unified":"1F56F","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f56f.png","sheet_x":24,"sheet_y":39,"short_name":"candle","short_names":["candle"],"text":null,"texts":null,"category":"Objects","sort_order":43,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MANTELPIECE CLOCK","unified":"1F570","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f570.png","sheet_x":24,"sheet_y":40,"short_name":"mantelpiece_clock","short_names":["mantelpiece_clock"],"text":null,"texts":null,"category":"Objects","sort_order":35,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HOLE","unified":"1F573","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f573.png","sheet_x":25,"sheet_y":0,"short_name":"hole","short_names":["hole"],"text":null,"texts":null,"category":"Objects","sort_order":80,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MAN IN BUSINESS SUIT LEVITATING","unified":"1F574","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f574.png","sheet_x":25,"sheet_y":1,"short_name":"man_in_business_suit_levitating","short_names":["man_in_business_suit_levitating"],"text":null,"texts":null,"category":"Activity","sort_order":31,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SLEUTH OR SPY","unified":"1F575","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f575.png","sheet_x":25,"sheet_y":2,"short_name":"sleuth_or_spy","short_names":["sleuth_or_spy"],"text":null,"texts":null,"category":"People","sort_order":134,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DARK SUNGLASSES","unified":"1F576","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f576.png","sheet_x":25,"sheet_y":3,"short_name":"dark_sunglasses","short_names":["dark_sunglasses"],"text":null,"texts":null,"category":"People","sort_order":202,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPIDER","unified":"1F577","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f577.png","sheet_x":25,"sheet_y":4,"short_name":"spider","short_names":["spider"],"text":null,"texts":null,"category":"Nature","sort_order":36,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPIDER WEB","unified":"1F578","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f578.png","sheet_x":25,"sheet_y":5,"short_name":"spider_web","short_names":["spider_web"],"text":null,"texts":null,"category":"Nature","sort_order":100,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"JOYSTICK","unified":"1F579","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f579.png","sheet_x":25,"sheet_y":6,"short_name":"joystick","short_names":["joystick"],"text":null,"texts":null,"category":"Objects","sort_order":10,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LINKED PAPERCLIPS","unified":"1F587","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f587.png","sheet_x":25,"sheet_y":7,"short_name":"linked_paperclips","short_names":["linked_paperclips"],"text":null,"texts":null,"category":"Objects","sort_order":157,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LOWER LEFT BALLPOINT PEN","unified":"1F58A","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f58a.png","sheet_x":25,"sheet_y":8,"short_name":"lower_left_ballpoint_pen","short_names":["lower_left_ballpoint_pen"],"text":null,"texts":null,"category":"Objects","sort_order":170,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LOWER LEFT FOUNTAIN PEN","unified":"1F58B","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f58b.png","sheet_x":25,"sheet_y":9,"short_name":"lower_left_fountain_pen","short_names":["lower_left_fountain_pen"],"text":null,"texts":null,"category":"Objects","sort_order":171,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LOWER LEFT PAINTBRUSH","unified":"1F58C","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f58c.png","sheet_x":25,"sheet_y":10,"short_name":"lower_left_paintbrush","short_names":["lower_left_paintbrush"],"text":null,"texts":null,"category":"Objects","sort_order":176,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LOWER LEFT CRAYON","unified":"1F58D","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f58d.png","sheet_x":25,"sheet_y":11,"short_name":"lower_left_crayon","short_names":["lower_left_crayon"],"text":null,"texts":null,"category":"Objects","sort_order":175,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RAISED HAND WITH FINGERS SPLAYED","unified":"1F590","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f590.png","sheet_x":25,"sheet_y":12,"short_name":"raised_hand_with_fingers_splayed","short_names":["raised_hand_with_fingers_splayed"],"text":null,"texts":null,"category":"People","sort_order":107,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F590-1F3FB":{"unified":"1F590-1F3FB","image":"1f590-1f3fb.png","sheet_x":25,"sheet_y":13,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F590-1F3FC":{"unified":"1F590-1F3FC","image":"1f590-1f3fc.png","sheet_x":25,"sheet_y":14,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F590-1F3FD":{"unified":"1F590-1F3FD","image":"1f590-1f3fd.png","sheet_x":25,"sheet_y":15,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F590-1F3FE":{"unified":"1F590-1F3FE","image":"1f590-1f3fe.png","sheet_x":25,"sheet_y":16,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F590-1F3FF":{"unified":"1F590-1F3FF","image":"1f590-1f3ff.png","sheet_x":25,"sheet_y":17,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"REVERSED HAND WITH MIDDLE FINGER EXTENDED","unified":"1F595","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f595.png","sheet_x":25,"sheet_y":18,"short_name":"middle_finger","short_names":["middle_finger","reversed_hand_with_middle_finger_extended"],"text":null,"texts":null,"category":"People","sort_order":106,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F595-1F3FB":{"unified":"1F595-1F3FB","image":"1f595-1f3fb.png","sheet_x":25,"sheet_y":19,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F595-1F3FC":{"unified":"1F595-1F3FC","image":"1f595-1f3fc.png","sheet_x":25,"sheet_y":20,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F595-1F3FD":{"unified":"1F595-1F3FD","image":"1f595-1f3fd.png","sheet_x":25,"sheet_y":21,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F595-1F3FE":{"unified":"1F595-1F3FE","image":"1f595-1f3fe.png","sheet_x":25,"sheet_y":22,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F595-1F3FF":{"unified":"1F595-1F3FF","image":"1f595-1f3ff.png","sheet_x":25,"sheet_y":23,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"RAISED HAND WITH PART BETWEEN MIDDLE AND RING FINGERS","unified":"1F596","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f596.png","sheet_x":25,"sheet_y":24,"short_name":"spock-hand","short_names":["spock-hand"],"text":null,"texts":null,"category":"People","sort_order":109,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F596-1F3FB":{"unified":"1F596-1F3FB","image":"1f596-1f3fb.png","sheet_x":25,"sheet_y":25,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F596-1F3FC":{"unified":"1F596-1F3FC","image":"1f596-1f3fc.png","sheet_x":25,"sheet_y":26,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F596-1F3FD":{"unified":"1F596-1F3FD","image":"1f596-1f3fd.png","sheet_x":25,"sheet_y":27,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F596-1F3FE":{"unified":"1F596-1F3FE","image":"1f596-1f3fe.png","sheet_x":25,"sheet_y":28,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F596-1F3FF":{"unified":"1F596-1F3FF","image":"1f596-1f3ff.png","sheet_x":25,"sheet_y":29,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"DESKTOP COMPUTER","unified":"1F5A5","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5a5.png","sheet_x":25,"sheet_y":30,"short_name":"desktop_computer","short_names":["desktop_computer"],"text":null,"texts":null,"category":"Objects","sort_order":6,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PRINTER","unified":"1F5A8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5a8.png","sheet_x":25,"sheet_y":31,"short_name":"printer","short_names":["printer"],"text":null,"texts":null,"category":"Objects","sort_order":7,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"THREE BUTTON MOUSE","unified":"1F5B1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5b1.png","sheet_x":25,"sheet_y":32,"short_name":"three_button_mouse","short_names":["three_button_mouse"],"text":null,"texts":null,"category":"Objects","sort_order":8,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TRACKBALL","unified":"1F5B2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5b2.png","sheet_x":25,"sheet_y":33,"short_name":"trackball","short_names":["trackball"],"text":null,"texts":null,"category":"Objects","sort_order":9,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FRAME WITH PICTURE","unified":"1F5BC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5bc.png","sheet_x":25,"sheet_y":34,"short_name":"frame_with_picture","short_names":["frame_with_picture"],"text":null,"texts":null,"category":"Objects","sort_order":96,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CARD INDEX DIVIDERS","unified":"1F5C2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5c2.png","sheet_x":25,"sheet_y":35,"short_name":"card_index_dividers","short_names":["card_index_dividers"],"text":null,"texts":null,"category":"Objects","sort_order":143,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CARD FILE BOX","unified":"1F5C3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5c3.png","sheet_x":25,"sheet_y":36,"short_name":"card_file_box","short_names":["card_file_box"],"text":null,"texts":null,"category":"Objects","sort_order":136,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FILE CABINET","unified":"1F5C4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5c4.png","sheet_x":25,"sheet_y":37,"short_name":"file_cabinet","short_names":["file_cabinet"],"text":null,"texts":null,"category":"Objects","sort_order":138,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WASTEBASKET","unified":"1F5D1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5d1.png","sheet_x":25,"sheet_y":38,"short_name":"wastebasket","short_names":["wastebasket"],"text":null,"texts":null,"category":"Objects","sort_order":44,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPIRAL NOTE PAD","unified":"1F5D2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5d2.png","sheet_x":25,"sheet_y":39,"short_name":"spiral_note_pad","short_names":["spiral_note_pad"],"text":null,"texts":null,"category":"Objects","sort_order":140,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPIRAL CALENDAR PAD","unified":"1F5D3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5d3.png","sheet_x":25,"sheet_y":40,"short_name":"spiral_calendar_pad","short_names":["spiral_calendar_pad"],"text":null,"texts":null,"category":"Objects","sort_order":134,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"COMPRESSION","unified":"1F5DC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5dc.png","sheet_x":26,"sheet_y":0,"short_name":"compression","short_names":["compression"],"text":null,"texts":null,"category":"Objects","sort_order":11,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"OLD KEY","unified":"1F5DD","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5dd.png","sheet_x":26,"sheet_y":1,"short_name":"old_key","short_names":["old_key"],"text":null,"texts":null,"category":"Objects","sort_order":90,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ROLLED-UP NEWSPAPER","unified":"1F5DE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5de.png","sheet_x":26,"sheet_y":2,"short_name":"rolled_up_newspaper","short_names":["rolled_up_newspaper"],"text":null,"texts":null,"category":"Objects","sort_order":144,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DAGGER KNIFE","unified":"1F5E1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5e1.png","sheet_x":26,"sheet_y":3,"short_name":"dagger_knife","short_names":["dagger_knife"],"text":null,"texts":null,"category":"Objects","sort_order":66,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPEAKING HEAD IN SILHOUETTE","unified":"1F5E3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5e3.png","sheet_x":26,"sheet_y":4,"short_name":"speaking_head_in_silhouette","short_names":["speaking_head_in_silhouette"],"text":null,"texts":null,"category":"People","sort_order":120,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LEFT SPEECH BUBBLE","unified":"1F5E8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5e8.png","sheet_x":26,"sheet_y":5,"short_name":"left_speech_bubble","short_names":["left_speech_bubble"],"text":null,"texts":null,"category":null,"sort_order":null,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":false},{"name":"RIGHT ANGER BUBBLE","unified":"1F5EF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5ef.png","sheet_x":26,"sheet_y":6,"short_name":"right_anger_bubble","short_names":["right_anger_bubble"],"text":null,"texts":null,"category":"Symbols","sort_order":244,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BALLOT BOX WITH BALLOT","unified":"1F5F3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5f3.png","sheet_x":26,"sheet_y":7,"short_name":"ballot_box_with_ballot","short_names":["ballot_box_with_ballot"],"text":null,"texts":null,"category":"Objects","sort_order":137,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WORLD MAP","unified":"1F5FA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f5fa.png","sheet_x":26,"sheet_y":8,"short_name":"world_map","short_names":["world_map"],"text":null,"texts":null,"category":"Objects","sort_order":97,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MOUNT FUJI","unified":"1F5FB","variations":[],"docomo":"E740","au":"E5BD","softbank":"E03B","google":"FE4C3","image":"1f5fb.png","sheet_x":26,"sheet_y":9,"short_name":"mount_fuji","short_names":["mount_fuji"],"text":null,"texts":null,"category":"Places","sort_order":68,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TOKYO TOWER","unified":"1F5FC","variations":[],"docomo":null,"au":"E4C0","softbank":"E509","google":"FE4C4","image":"1f5fc.png","sheet_x":26,"sheet_y":10,"short_name":"tokyo_tower","short_names":["tokyo_tower"],"text":null,"texts":null,"category":"Places","sort_order":62,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"STATUE OF LIBERTY","unified":"1F5FD","variations":[],"docomo":null,"au":null,"softbank":"E51D","google":"FE4C6","image":"1f5fd.png","sheet_x":26,"sheet_y":11,"short_name":"statue_of_liberty","short_names":["statue_of_liberty"],"text":null,"texts":null,"category":"Places","sort_order":95,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SILHOUETTE OF JAPAN","unified":"1F5FE","variations":[],"docomo":null,"au":"E572","softbank":null,"google":"FE4C7","image":"1f5fe.png","sheet_x":26,"sheet_y":12,"short_name":"japan","short_names":["japan"],"text":null,"texts":null,"category":"Places","sort_order":70,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MOYAI","unified":"1F5FF","variations":[],"docomo":null,"au":"EB6C","softbank":null,"google":"FE4C8","image":"1f5ff.png","sheet_x":26,"sheet_y":13,"short_name":"moyai","short_names":["moyai"],"text":null,"texts":null,"category":"Objects","sort_order":99,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GRINNING FACE","unified":"1F600","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f600.png","sheet_x":26,"sheet_y":14,"short_name":"grinning","short_names":["grinning"],"text":":D","texts":null,"category":"People","sort_order":1,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GRINNING FACE WITH SMILING EYES","unified":"1F601","variations":[],"docomo":"E753","au":"EB80","softbank":"E404","google":"FE333","image":"1f601.png","sheet_x":26,"sheet_y":15,"short_name":"grin","short_names":["grin"],"text":null,"texts":null,"category":"People","sort_order":3,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACE WITH TEARS OF JOY","unified":"1F602","variations":[],"docomo":"E72A","au":"EB64","softbank":"E412","google":"FE334","image":"1f602.png","sheet_x":26,"sheet_y":16,"short_name":"joy","short_names":["joy"],"text":null,"texts":null,"category":"People","sort_order":4,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SMILING FACE WITH OPEN MOUTH","unified":"1F603","variations":[],"docomo":"E6F0","au":"E471","softbank":"E057","google":"FE330","image":"1f603.png","sheet_x":26,"sheet_y":17,"short_name":"smiley","short_names":["smiley"],"text":":)","texts":["=)","=-)"],"category":"People","sort_order":5,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SMILING FACE WITH OPEN MOUTH AND SMILING EYES","unified":"1F604","variations":[],"docomo":"E6F0","au":"E471","softbank":"E415","google":"FE338","image":"1f604.png","sheet_x":26,"sheet_y":18,"short_name":"smile","short_names":["smile"],"text":":)","texts":["C:","c:",":D",":-D"],"category":"People","sort_order":6,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SMILING FACE WITH OPEN MOUTH AND COLD SWEAT","unified":"1F605","variations":[],"docomo":"E722","au":"E471-E5B1","softbank":"E415-E331","google":"FE331","image":"1f605.png","sheet_x":26,"sheet_y":19,"short_name":"sweat_smile","short_names":["sweat_smile"],"text":null,"texts":null,"category":"People","sort_order":7,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SMILING FACE WITH OPEN MOUTH AND TIGHTLY-CLOSED EYES","unified":"1F606","variations":[],"docomo":"E72A","au":"EAC5","softbank":"E40A","google":"FE332","image":"1f606.png","sheet_x":26,"sheet_y":20,"short_name":"laughing","short_names":["laughing","satisfied"],"text":null,"texts":[":>",":->"],"category":"People","sort_order":8,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SMILING FACE WITH HALO","unified":"1F607","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f607.png","sheet_x":26,"sheet_y":21,"short_name":"innocent","short_names":["innocent"],"text":null,"texts":null,"category":"People","sort_order":9,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SMILING FACE WITH HORNS","unified":"1F608","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f608.png","sheet_x":26,"sheet_y":22,"short_name":"smiling_imp","short_names":["smiling_imp"],"text":null,"texts":null,"category":"People","sort_order":71,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WINKING FACE","unified":"1F609","variations":[],"docomo":"E729","au":"E5C3","softbank":"E405","google":"FE347","image":"1f609.png","sheet_x":26,"sheet_y":23,"short_name":"wink","short_names":["wink"],"text":";)","texts":[";)",";-)"],"category":"People","sort_order":10,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SMILING FACE WITH SMILING EYES","unified":"1F60A","variations":[],"docomo":"E6F0","au":"EACD","softbank":"E056","google":"FE335","image":"1f60a.png","sheet_x":26,"sheet_y":24,"short_name":"blush","short_names":["blush"],"text":":)","texts":[":)","(:",":-)"],"category":"People","sort_order":11,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACE SAVOURING DELICIOUS FOOD","unified":"1F60B","variations":[],"docomo":"E752","au":"EACD","softbank":"E056","google":"FE32B","image":"1f60b.png","sheet_x":26,"sheet_y":25,"short_name":"yum","short_names":["yum"],"text":null,"texts":null,"category":"People","sort_order":15,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RELIEVED FACE","unified":"1F60C","variations":[],"docomo":"E721","au":"EAC5","softbank":"E40A","google":"FE33E","image":"1f60c.png","sheet_x":26,"sheet_y":26,"short_name":"relieved","short_names":["relieved"],"text":null,"texts":null,"category":"People","sort_order":16,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SMILING FACE WITH HEART-SHAPED EYES","unified":"1F60D","variations":[],"docomo":"E726","au":"E5C4","softbank":"E106","google":"FE327","image":"1f60d.png","sheet_x":26,"sheet_y":27,"short_name":"heart_eyes","short_names":["heart_eyes"],"text":null,"texts":null,"category":"People","sort_order":17,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SMILING FACE WITH SUNGLASSES","unified":"1F60E","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f60e.png","sheet_x":26,"sheet_y":28,"short_name":"sunglasses","short_names":["sunglasses"],"text":null,"texts":["8)"],"category":"People","sort_order":27,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SMIRKING FACE","unified":"1F60F","variations":[],"docomo":"E72C","au":"EABF","softbank":"E402","google":"FE343","image":"1f60f.png","sheet_x":26,"sheet_y":29,"short_name":"smirk","short_names":["smirk"],"text":null,"texts":null,"category":"People","sort_order":29,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NEUTRAL FACE","unified":"1F610","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f610.png","sheet_x":26,"sheet_y":30,"short_name":"neutral_face","short_names":["neutral_face"],"text":null,"texts":[":|",":-|"],"category":"People","sort_order":31,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"EXPRESSIONLESS FACE","unified":"1F611","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f611.png","sheet_x":26,"sheet_y":31,"short_name":"expressionless","short_names":["expressionless"],"text":null,"texts":null,"category":"People","sort_order":32,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"UNAMUSED FACE","unified":"1F612","variations":[],"docomo":"E725","au":"EAC9","softbank":"E40E","google":"FE326","image":"1f612.png","sheet_x":26,"sheet_y":32,"short_name":"unamused","short_names":["unamused"],"text":":(","texts":null,"category":"People","sort_order":33,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACE WITH COLD SWEAT","unified":"1F613","variations":[],"docomo":"E723","au":"E5C6","softbank":"E108","google":"FE344","image":"1f613.png","sheet_x":26,"sheet_y":33,"short_name":"sweat","short_names":["sweat"],"text":null,"texts":null,"category":"People","sort_order":60,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PENSIVE FACE","unified":"1F614","variations":[],"docomo":"E720","au":"EAC0","softbank":"E403","google":"FE340","image":"1f614.png","sheet_x":26,"sheet_y":34,"short_name":"pensive","short_names":["pensive"],"text":null,"texts":null,"category":"People","sort_order":41,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CONFUSED FACE","unified":"1F615","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f615.png","sheet_x":26,"sheet_y":35,"short_name":"confused","short_names":["confused"],"text":null,"texts":[":\\",":-\\",":\/",":-\/"],"category":"People","sort_order":42,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CONFOUNDED FACE","unified":"1F616","variations":[],"docomo":"E6F3","au":"EAC3","softbank":"E407","google":"FE33F","image":"1f616.png","sheet_x":26,"sheet_y":36,"short_name":"confounded","short_names":["confounded"],"text":null,"texts":null,"category":"People","sort_order":46,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KISSING FACE","unified":"1F617","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f617.png","sheet_x":26,"sheet_y":37,"short_name":"kissing","short_names":["kissing"],"text":null,"texts":null,"category":"People","sort_order":19,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACE THROWING A KISS","unified":"1F618","variations":[],"docomo":"E726","au":"EACF","softbank":"E418","google":"FE32C","image":"1f618.png","sheet_x":26,"sheet_y":38,"short_name":"kissing_heart","short_names":["kissing_heart"],"text":null,"texts":null,"category":"People","sort_order":18,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KISSING FACE WITH SMILING EYES","unified":"1F619","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f619.png","sheet_x":26,"sheet_y":39,"short_name":"kissing_smiling_eyes","short_names":["kissing_smiling_eyes"],"text":null,"texts":null,"category":"People","sort_order":20,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KISSING FACE WITH CLOSED EYES","unified":"1F61A","variations":[],"docomo":"E726","au":"EACE","softbank":"E417","google":"FE32D","image":"1f61a.png","sheet_x":26,"sheet_y":40,"short_name":"kissing_closed_eyes","short_names":["kissing_closed_eyes"],"text":null,"texts":null,"category":"People","sort_order":21,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACE WITH STUCK-OUT TONGUE","unified":"1F61B","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f61b.png","sheet_x":27,"sheet_y":0,"short_name":"stuck_out_tongue","short_names":["stuck_out_tongue"],"text":":p","texts":[":p",":-p",":P",":-P",":b",":-b"],"category":"People","sort_order":24,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACE WITH STUCK-OUT TONGUE AND WINKING EYE","unified":"1F61C","variations":[],"docomo":"E728","au":"E4E7","softbank":"E105","google":"FE329","image":"1f61c.png","sheet_x":27,"sheet_y":1,"short_name":"stuck_out_tongue_winking_eye","short_names":["stuck_out_tongue_winking_eye"],"text":";p","texts":[";p",";-p",";b",";-b",";P",";-P"],"category":"People","sort_order":22,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES","unified":"1F61D","variations":[],"docomo":"E728","au":"E4E7","softbank":"E409","google":"FE32A","image":"1f61d.png","sheet_x":27,"sheet_y":2,"short_name":"stuck_out_tongue_closed_eyes","short_names":["stuck_out_tongue_closed_eyes"],"text":null,"texts":null,"category":"People","sort_order":23,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DISAPPOINTED FACE","unified":"1F61E","variations":[],"docomo":"E6F2","au":"EAC0","softbank":"E058","google":"FE323","image":"1f61e.png","sheet_x":27,"sheet_y":3,"short_name":"disappointed","short_names":["disappointed"],"text":":(","texts":["):",":(",":-("],"category":"People","sort_order":37,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WORRIED FACE","unified":"1F61F","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f61f.png","sheet_x":27,"sheet_y":4,"short_name":"worried","short_names":["worried"],"text":null,"texts":null,"category":"People","sort_order":38,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ANGRY FACE","unified":"1F620","variations":[],"docomo":"E6F1","au":"E472","softbank":"E059","google":"FE320","image":"1f620.png","sheet_x":27,"sheet_y":5,"short_name":"angry","short_names":["angry"],"text":null,"texts":[">:(",">:-("],"category":"People","sort_order":39,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"POUTING FACE","unified":"1F621","variations":[],"docomo":"E724","au":"EB5D","softbank":"E416","google":"FE33D","image":"1f621.png","sheet_x":27,"sheet_y":6,"short_name":"rage","short_names":["rage"],"text":null,"texts":null,"category":"People","sort_order":40,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CRYING FACE","unified":"1F622","variations":[],"docomo":"E72E","au":"EB69","softbank":"E413","google":"FE339","image":"1f622.png","sheet_x":27,"sheet_y":7,"short_name":"cry","short_names":["cry"],"text":":'(","texts":[":'("],"category":"People","sort_order":57,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PERSEVERING FACE","unified":"1F623","variations":[],"docomo":"E72B","au":"EAC2","softbank":"E406","google":"FE33C","image":"1f623.png","sheet_x":27,"sheet_y":8,"short_name":"persevere","short_names":["persevere"],"text":null,"texts":null,"category":"People","sort_order":45,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACE WITH LOOK OF TRIUMPH","unified":"1F624","variations":[],"docomo":"E753","au":"EAC1","softbank":"E404","google":"FE328","image":"1f624.png","sheet_x":27,"sheet_y":9,"short_name":"triumph","short_names":["triumph"],"text":null,"texts":null,"category":"People","sort_order":49,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DISAPPOINTED BUT RELIEVED FACE","unified":"1F625","variations":[],"docomo":"E723","au":"E5C6","softbank":"E401","google":"FE345","image":"1f625.png","sheet_x":27,"sheet_y":10,"short_name":"disappointed_relieved","short_names":["disappointed_relieved"],"text":null,"texts":null,"category":"People","sort_order":58,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FROWNING FACE WITH OPEN MOUTH","unified":"1F626","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f626.png","sheet_x":27,"sheet_y":11,"short_name":"frowning","short_names":["frowning"],"text":null,"texts":null,"category":"People","sort_order":55,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ANGUISHED FACE","unified":"1F627","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f627.png","sheet_x":27,"sheet_y":12,"short_name":"anguished","short_names":["anguished"],"text":null,"texts":["D:"],"category":"People","sort_order":56,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FEARFUL FACE","unified":"1F628","variations":[],"docomo":"E757","au":"EAC6","softbank":"E40B","google":"FE33B","image":"1f628.png","sheet_x":27,"sheet_y":13,"short_name":"fearful","short_names":["fearful"],"text":null,"texts":null,"category":"People","sort_order":52,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WEARY FACE","unified":"1F629","variations":[],"docomo":"E6F3","au":"EB67","softbank":"E403","google":"FE321","image":"1f629.png","sheet_x":27,"sheet_y":14,"short_name":"weary","short_names":["weary"],"text":null,"texts":null,"category":"People","sort_order":48,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SLEEPY FACE","unified":"1F62A","variations":[],"docomo":"E701","au":"EAC4","softbank":"E408","google":"FE342","image":"1f62a.png","sheet_x":27,"sheet_y":15,"short_name":"sleepy","short_names":["sleepy"],"text":null,"texts":null,"category":"People","sort_order":59,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TIRED FACE","unified":"1F62B","variations":[],"docomo":"E72B","au":"E474","softbank":"E406","google":"FE346","image":"1f62b.png","sheet_x":27,"sheet_y":16,"short_name":"tired_face","short_names":["tired_face"],"text":null,"texts":null,"category":"People","sort_order":47,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GRIMACING FACE","unified":"1F62C","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f62c.png","sheet_x":27,"sheet_y":17,"short_name":"grimacing","short_names":["grimacing"],"text":null,"texts":null,"category":"People","sort_order":2,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LOUDLY CRYING FACE","unified":"1F62D","variations":[],"docomo":"E72D","au":"E473","softbank":"E411","google":"FE33A","image":"1f62d.png","sheet_x":27,"sheet_y":18,"short_name":"sob","short_names":["sob"],"text":":'(","texts":null,"category":"People","sort_order":61,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACE WITH OPEN MOUTH","unified":"1F62E","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f62e.png","sheet_x":27,"sheet_y":19,"short_name":"open_mouth","short_names":["open_mouth"],"text":null,"texts":[":o",":-o"],"category":"People","sort_order":50,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HUSHED FACE","unified":"1F62F","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f62f.png","sheet_x":27,"sheet_y":20,"short_name":"hushed","short_names":["hushed"],"text":null,"texts":null,"category":"People","sort_order":54,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACE WITH OPEN MOUTH AND COLD SWEAT","unified":"1F630","variations":[],"docomo":"E723","au":"EACB","softbank":"E40F","google":"FE325","image":"1f630.png","sheet_x":27,"sheet_y":21,"short_name":"cold_sweat","short_names":["cold_sweat"],"text":null,"texts":null,"category":"People","sort_order":53,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACE SCREAMING IN FEAR","unified":"1F631","variations":[],"docomo":"E757","au":"E5C5","softbank":"E107","google":"FE341","image":"1f631.png","sheet_x":27,"sheet_y":22,"short_name":"scream","short_names":["scream"],"text":null,"texts":null,"category":"People","sort_order":51,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ASTONISHED FACE","unified":"1F632","variations":[],"docomo":"E6F4","au":"EACA","softbank":"E410","google":"FE322","image":"1f632.png","sheet_x":27,"sheet_y":23,"short_name":"astonished","short_names":["astonished"],"text":null,"texts":null,"category":"People","sort_order":63,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FLUSHED FACE","unified":"1F633","variations":[],"docomo":"E72A","au":"EAC8","softbank":"E40D","google":"FE32F","image":"1f633.png","sheet_x":27,"sheet_y":24,"short_name":"flushed","short_names":["flushed"],"text":null,"texts":null,"category":"People","sort_order":36,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SLEEPING FACE","unified":"1F634","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f634.png","sheet_x":27,"sheet_y":25,"short_name":"sleeping","short_names":["sleeping"],"text":null,"texts":null,"category":"People","sort_order":68,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DIZZY FACE","unified":"1F635","variations":[],"docomo":"E6F4","au":"E5AE","softbank":"E406","google":"FE324","image":"1f635.png","sheet_x":27,"sheet_y":26,"short_name":"dizzy_face","short_names":["dizzy_face"],"text":null,"texts":null,"category":"People","sort_order":62,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACE WITHOUT MOUTH","unified":"1F636","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f636.png","sheet_x":27,"sheet_y":27,"short_name":"no_mouth","short_names":["no_mouth"],"text":null,"texts":null,"category":"People","sort_order":30,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACE WITH MEDICAL MASK","unified":"1F637","variations":[],"docomo":null,"au":"EAC7","softbank":"E40C","google":"FE32E","image":"1f637.png","sheet_x":27,"sheet_y":28,"short_name":"mask","short_names":["mask"],"text":null,"texts":null,"category":"People","sort_order":65,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"GRINNING CAT FACE WITH SMILING EYES","unified":"1F638","variations":[],"docomo":"E753","au":"EB7F","softbank":"E404","google":"FE349","image":"1f638.png","sheet_x":27,"sheet_y":29,"short_name":"smile_cat","short_names":["smile_cat"],"text":null,"texts":null,"category":"People","sort_order":80,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CAT FACE WITH TEARS OF JOY","unified":"1F639","variations":[],"docomo":"E72A","au":"EB63","softbank":"E412","google":"FE34A","image":"1f639.png","sheet_x":27,"sheet_y":30,"short_name":"joy_cat","short_names":["joy_cat"],"text":null,"texts":null,"category":"People","sort_order":81,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SMILING CAT FACE WITH OPEN MOUTH","unified":"1F63A","variations":[],"docomo":"E6F0","au":"EB61","softbank":"E057","google":"FE348","image":"1f63a.png","sheet_x":27,"sheet_y":31,"short_name":"smiley_cat","short_names":["smiley_cat"],"text":null,"texts":null,"category":"People","sort_order":79,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SMILING CAT FACE WITH HEART-SHAPED EYES","unified":"1F63B","variations":[],"docomo":"E726","au":"EB65","softbank":"E106","google":"FE34C","image":"1f63b.png","sheet_x":27,"sheet_y":32,"short_name":"heart_eyes_cat","short_names":["heart_eyes_cat"],"text":null,"texts":null,"category":"People","sort_order":82,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CAT FACE WITH WRY SMILE","unified":"1F63C","variations":[],"docomo":"E753","au":"EB6A","softbank":"E404","google":"FE34F","image":"1f63c.png","sheet_x":27,"sheet_y":33,"short_name":"smirk_cat","short_names":["smirk_cat"],"text":null,"texts":null,"category":"People","sort_order":83,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KISSING CAT FACE WITH CLOSED EYES","unified":"1F63D","variations":[],"docomo":"E726","au":"EB60","softbank":"E418","google":"FE34B","image":"1f63d.png","sheet_x":27,"sheet_y":34,"short_name":"kissing_cat","short_names":["kissing_cat"],"text":null,"texts":null,"category":"People","sort_order":84,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"POUTING CAT FACE","unified":"1F63E","variations":[],"docomo":"E724","au":"EB5E","softbank":"E416","google":"FE34E","image":"1f63e.png","sheet_x":27,"sheet_y":35,"short_name":"pouting_cat","short_names":["pouting_cat"],"text":null,"texts":null,"category":"People","sort_order":87,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CRYING CAT FACE","unified":"1F63F","variations":[],"docomo":"E72E","au":"EB68","softbank":"E413","google":"FE34D","image":"1f63f.png","sheet_x":27,"sheet_y":36,"short_name":"crying_cat_face","short_names":["crying_cat_face"],"text":null,"texts":null,"category":"People","sort_order":86,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WEARY CAT FACE","unified":"1F640","variations":[],"docomo":"E6F3","au":"EB66","softbank":"E403","google":"FE350","image":"1f640.png","sheet_x":27,"sheet_y":37,"short_name":"scream_cat","short_names":["scream_cat"],"text":null,"texts":null,"category":"People","sort_order":85,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SLIGHTLY FROWNING FACE","unified":"1F641","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f641.png","sheet_x":27,"sheet_y":38,"short_name":"slightly_frowning_face","short_names":["slightly_frowning_face"],"text":null,"texts":null,"category":"People","sort_order":43,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SLIGHTLY SMILING FACE","unified":"1F642","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f642.png","sheet_x":27,"sheet_y":39,"short_name":"slightly_smiling_face","short_names":["slightly_smiling_face"],"text":null,"texts":null,"category":"People","sort_order":12,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"UPSIDE-DOWN FACE","unified":"1F643","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f643.png","sheet_x":27,"sheet_y":40,"short_name":"upside_down_face","short_names":["upside_down_face"],"text":null,"texts":null,"category":"People","sort_order":13,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACE WITH ROLLING EYES","unified":"1F644","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f644.png","sheet_x":28,"sheet_y":0,"short_name":"face_with_rolling_eyes","short_names":["face_with_rolling_eyes"],"text":null,"texts":null,"category":"People","sort_order":34,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACE WITH NO GOOD GESTURE","unified":"1F645","variations":[],"docomo":"E72F","au":"EAD7","softbank":"E423","google":"FE351","image":"1f645.png","sheet_x":28,"sheet_y":1,"short_name":"no_good","short_names":["no_good"],"text":null,"texts":null,"category":"People","sort_order":148,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F645-1F3FB":{"unified":"1F645-1F3FB","image":"1f645-1f3fb.png","sheet_x":28,"sheet_y":2,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F645-1F3FC":{"unified":"1F645-1F3FC","image":"1f645-1f3fc.png","sheet_x":28,"sheet_y":3,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F645-1F3FD":{"unified":"1F645-1F3FD","image":"1f645-1f3fd.png","sheet_x":28,"sheet_y":4,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F645-1F3FE":{"unified":"1F645-1F3FE","image":"1f645-1f3fe.png","sheet_x":28,"sheet_y":5,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F645-1F3FF":{"unified":"1F645-1F3FF","image":"1f645-1f3ff.png","sheet_x":28,"sheet_y":6,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"FACE WITH OK GESTURE","unified":"1F646","variations":[],"docomo":"E70B","au":"EAD8","softbank":"E424","google":"FE352","image":"1f646.png","sheet_x":28,"sheet_y":7,"short_name":"ok_woman","short_names":["ok_woman"],"text":null,"texts":null,"category":"People","sort_order":149,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F646-1F3FB":{"unified":"1F646-1F3FB","image":"1f646-1f3fb.png","sheet_x":28,"sheet_y":8,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F646-1F3FC":{"unified":"1F646-1F3FC","image":"1f646-1f3fc.png","sheet_x":28,"sheet_y":9,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F646-1F3FD":{"unified":"1F646-1F3FD","image":"1f646-1f3fd.png","sheet_x":28,"sheet_y":10,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F646-1F3FE":{"unified":"1F646-1F3FE","image":"1f646-1f3fe.png","sheet_x":28,"sheet_y":11,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F646-1F3FF":{"unified":"1F646-1F3FF","image":"1f646-1f3ff.png","sheet_x":28,"sheet_y":12,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"PERSON BOWING DEEPLY","unified":"1F647","variations":[],"docomo":null,"au":"EAD9","softbank":"E426","google":"FE353","image":"1f647.png","sheet_x":28,"sheet_y":13,"short_name":"bow","short_names":["bow"],"text":null,"texts":null,"category":"People","sort_order":146,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F647-1F3FB":{"unified":"1F647-1F3FB","image":"1f647-1f3fb.png","sheet_x":28,"sheet_y":14,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F647-1F3FC":{"unified":"1F647-1F3FC","image":"1f647-1f3fc.png","sheet_x":28,"sheet_y":15,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F647-1F3FD":{"unified":"1F647-1F3FD","image":"1f647-1f3fd.png","sheet_x":28,"sheet_y":16,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F647-1F3FE":{"unified":"1F647-1F3FE","image":"1f647-1f3fe.png","sheet_x":28,"sheet_y":17,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F647-1F3FF":{"unified":"1F647-1F3FF","image":"1f647-1f3ff.png","sheet_x":28,"sheet_y":18,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"SEE-NO-EVIL MONKEY","unified":"1F648","variations":[],"docomo":null,"au":"EB50","softbank":null,"google":"FE354","image":"1f648.png","sheet_x":28,"sheet_y":19,"short_name":"see_no_evil","short_names":["see_no_evil"],"text":null,"texts":null,"category":"Nature","sort_order":17,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HEAR-NO-EVIL MONKEY","unified":"1F649","variations":[],"docomo":null,"au":"EB52","softbank":null,"google":"FE356","image":"1f649.png","sheet_x":28,"sheet_y":20,"short_name":"hear_no_evil","short_names":["hear_no_evil"],"text":null,"texts":null,"category":"Nature","sort_order":18,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SPEAK-NO-EVIL MONKEY","unified":"1F64A","variations":[],"docomo":null,"au":"EB51","softbank":null,"google":"FE355","image":"1f64a.png","sheet_x":28,"sheet_y":21,"short_name":"speak_no_evil","short_names":["speak_no_evil"],"text":null,"texts":null,"category":"Nature","sort_order":19,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HAPPY PERSON RAISING ONE HAND","unified":"1F64B","variations":[],"docomo":null,"au":"EB85","softbank":"E012","google":"FE357","image":"1f64b.png","sheet_x":28,"sheet_y":22,"short_name":"raising_hand","short_names":["raising_hand"],"text":null,"texts":null,"category":"People","sort_order":150,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F64B-1F3FB":{"unified":"1F64B-1F3FB","image":"1f64b-1f3fb.png","sheet_x":28,"sheet_y":23,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64B-1F3FC":{"unified":"1F64B-1F3FC","image":"1f64b-1f3fc.png","sheet_x":28,"sheet_y":24,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64B-1F3FD":{"unified":"1F64B-1F3FD","image":"1f64b-1f3fd.png","sheet_x":28,"sheet_y":25,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64B-1F3FE":{"unified":"1F64B-1F3FE","image":"1f64b-1f3fe.png","sheet_x":28,"sheet_y":26,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64B-1F3FF":{"unified":"1F64B-1F3FF","image":"1f64b-1f3ff.png","sheet_x":28,"sheet_y":27,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"PERSON RAISING BOTH HANDS IN CELEBRATION","unified":"1F64C","variations":[],"docomo":null,"au":"EB86","softbank":"E427","google":"FE358","image":"1f64c.png","sheet_x":28,"sheet_y":28,"short_name":"raised_hands","short_names":["raised_hands"],"text":null,"texts":null,"category":"People","sort_order":88,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F64C-1F3FB":{"unified":"1F64C-1F3FB","image":"1f64c-1f3fb.png","sheet_x":28,"sheet_y":29,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64C-1F3FC":{"unified":"1F64C-1F3FC","image":"1f64c-1f3fc.png","sheet_x":28,"sheet_y":30,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64C-1F3FD":{"unified":"1F64C-1F3FD","image":"1f64c-1f3fd.png","sheet_x":28,"sheet_y":31,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64C-1F3FE":{"unified":"1F64C-1F3FE","image":"1f64c-1f3fe.png","sheet_x":28,"sheet_y":32,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64C-1F3FF":{"unified":"1F64C-1F3FF","image":"1f64c-1f3ff.png","sheet_x":28,"sheet_y":33,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"PERSON FROWNING","unified":"1F64D","variations":[],"docomo":"E6F3","au":"EB87","softbank":"E403","google":"FE359","image":"1f64d.png","sheet_x":28,"sheet_y":34,"short_name":"person_frowning","short_names":["person_frowning"],"text":null,"texts":null,"category":"People","sort_order":152,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F64D-1F3FB":{"unified":"1F64D-1F3FB","image":"1f64d-1f3fb.png","sheet_x":28,"sheet_y":35,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64D-1F3FC":{"unified":"1F64D-1F3FC","image":"1f64d-1f3fc.png","sheet_x":28,"sheet_y":36,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64D-1F3FD":{"unified":"1F64D-1F3FD","image":"1f64d-1f3fd.png","sheet_x":28,"sheet_y":37,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64D-1F3FE":{"unified":"1F64D-1F3FE","image":"1f64d-1f3fe.png","sheet_x":28,"sheet_y":38,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64D-1F3FF":{"unified":"1F64D-1F3FF","image":"1f64d-1f3ff.png","sheet_x":28,"sheet_y":39,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"PERSON WITH POUTING FACE","unified":"1F64E","variations":[],"docomo":"E6F1","au":"EB88","softbank":"E416","google":"FE35A","image":"1f64e.png","sheet_x":28,"sheet_y":40,"short_name":"person_with_pouting_face","short_names":["person_with_pouting_face"],"text":null,"texts":null,"category":"People","sort_order":151,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F64E-1F3FB":{"unified":"1F64E-1F3FB","image":"1f64e-1f3fb.png","sheet_x":29,"sheet_y":0,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64E-1F3FC":{"unified":"1F64E-1F3FC","image":"1f64e-1f3fc.png","sheet_x":29,"sheet_y":1,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64E-1F3FD":{"unified":"1F64E-1F3FD","image":"1f64e-1f3fd.png","sheet_x":29,"sheet_y":2,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64E-1F3FE":{"unified":"1F64E-1F3FE","image":"1f64e-1f3fe.png","sheet_x":29,"sheet_y":3,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64E-1F3FF":{"unified":"1F64E-1F3FF","image":"1f64e-1f3ff.png","sheet_x":29,"sheet_y":4,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"PERSON WITH FOLDED HANDS","unified":"1F64F","variations":[],"docomo":null,"au":"EAD2","softbank":"E41D","google":"FE35B","image":"1f64f.png","sheet_x":29,"sheet_y":5,"short_name":"pray","short_names":["pray"],"text":null,"texts":null,"category":"People","sort_order":100,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F64F-1F3FB":{"unified":"1F64F-1F3FB","image":"1f64f-1f3fb.png","sheet_x":29,"sheet_y":6,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64F-1F3FC":{"unified":"1F64F-1F3FC","image":"1f64f-1f3fc.png","sheet_x":29,"sheet_y":7,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64F-1F3FD":{"unified":"1F64F-1F3FD","image":"1f64f-1f3fd.png","sheet_x":29,"sheet_y":8,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64F-1F3FE":{"unified":"1F64F-1F3FE","image":"1f64f-1f3fe.png","sheet_x":29,"sheet_y":9,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F64F-1F3FF":{"unified":"1F64F-1F3FF","image":"1f64f-1f3ff.png","sheet_x":29,"sheet_y":10,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"ROCKET","unified":"1F680","variations":[],"docomo":null,"au":"E5C8","softbank":"E10D","google":"FE7ED","image":"1f680.png","sheet_x":29,"sheet_y":11,"short_name":"rocket","short_names":["rocket"],"text":null,"texts":null,"category":"Places","sort_order":46,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HELICOPTER","unified":"1F681","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f681.png","sheet_x":29,"sheet_y":12,"short_name":"helicopter","short_names":["helicopter"],"text":null,"texts":null,"category":"Places","sort_order":36,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"STEAM LOCOMOTIVE","unified":"1F682","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f682.png","sheet_x":29,"sheet_y":13,"short_name":"steam_locomotive","short_names":["steam_locomotive"],"text":null,"texts":null,"category":"Places","sort_order":31,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RAILWAY CAR","unified":"1F683","variations":[],"docomo":"E65B","au":"E4B5","softbank":"E01E","google":"FE7DF","image":"1f683.png","sheet_x":29,"sheet_y":14,"short_name":"railway_car","short_names":["railway_car"],"text":null,"texts":null,"category":"Places","sort_order":24,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HIGH-SPEED TRAIN","unified":"1F684","variations":[],"docomo":"E65D","au":"E4B0","softbank":"E435","google":"FE7E2","image":"1f684.png","sheet_x":29,"sheet_y":15,"short_name":"bullettrain_side","short_names":["bullettrain_side"],"text":null,"texts":null,"category":"Places","sort_order":27,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HIGH-SPEED TRAIN WITH BULLET NOSE","unified":"1F685","variations":[],"docomo":"E65D","au":"E4B0","softbank":"E01F","google":"FE7E3","image":"1f685.png","sheet_x":29,"sheet_y":16,"short_name":"bullettrain_front","short_names":["bullettrain_front"],"text":null,"texts":null,"category":"Places","sort_order":28,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TRAIN","unified":"1F686","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f686.png","sheet_x":29,"sheet_y":17,"short_name":"train2","short_names":["train2"],"text":null,"texts":null,"category":"Places","sort_order":32,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"METRO","unified":"1F687","variations":[],"docomo":"E65C","au":"E5BC","softbank":"E434","google":"FE7E0","image":"1f687.png","sheet_x":29,"sheet_y":18,"short_name":"metro","short_names":["metro"],"text":null,"texts":null,"category":"Places","sort_order":33,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LIGHT RAIL","unified":"1F688","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f688.png","sheet_x":29,"sheet_y":19,"short_name":"light_rail","short_names":["light_rail"],"text":null,"texts":null,"category":"Places","sort_order":29,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"STATION","unified":"1F689","variations":[],"docomo":null,"au":"EB6D","softbank":"E039","google":"FE7EC","image":"1f689.png","sheet_x":29,"sheet_y":20,"short_name":"station","short_names":["station"],"text":null,"texts":null,"category":"Places","sort_order":35,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TRAM","unified":"1F68A","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f68a.png","sheet_x":29,"sheet_y":21,"short_name":"tram","short_names":["tram"],"text":null,"texts":null,"category":"Places","sort_order":34,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TRAM CAR","unified":"1F68B","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f68b.png","sheet_x":29,"sheet_y":22,"short_name":"train","short_names":["train"],"text":null,"texts":null,"category":"Places","sort_order":25,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BUS","unified":"1F68C","variations":[],"docomo":"E660","au":"E4AF","softbank":"E159","google":"FE7E6","image":"1f68c.png","sheet_x":29,"sheet_y":23,"short_name":"bus","short_names":["bus"],"text":null,"texts":null,"category":"Places","sort_order":4,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ONCOMING BUS","unified":"1F68D","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f68d.png","sheet_x":29,"sheet_y":24,"short_name":"oncoming_bus","short_names":["oncoming_bus"],"text":null,"texts":null,"category":"Places","sort_order":18,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TROLLEYBUS","unified":"1F68E","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f68e.png","sheet_x":29,"sheet_y":25,"short_name":"trolleybus","short_names":["trolleybus"],"text":null,"texts":null,"category":"Places","sort_order":5,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BUS STOP","unified":"1F68F","variations":[],"docomo":null,"au":"E4A7","softbank":"E150","google":"FE7E7","image":"1f68f.png","sheet_x":29,"sheet_y":26,"short_name":"busstop","short_names":["busstop"],"text":null,"texts":null,"category":"Places","sort_order":52,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MINIBUS","unified":"1F690","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f690.png","sheet_x":29,"sheet_y":27,"short_name":"minibus","short_names":["minibus"],"text":null,"texts":null,"category":"Places","sort_order":10,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"AMBULANCE","unified":"1F691","variations":[],"docomo":null,"au":"EAE0","softbank":"E431","google":"FE7F3","image":"1f691.png","sheet_x":29,"sheet_y":28,"short_name":"ambulance","short_names":["ambulance"],"text":null,"texts":null,"category":"Places","sort_order":8,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FIRE ENGINE","unified":"1F692","variations":[],"docomo":null,"au":"EADF","softbank":"E430","google":"FE7F2","image":"1f692.png","sheet_x":29,"sheet_y":29,"short_name":"fire_engine","short_names":["fire_engine"],"text":null,"texts":null,"category":"Places","sort_order":9,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"POLICE CAR","unified":"1F693","variations":[],"docomo":null,"au":"EAE1","softbank":"E432","google":"FE7F4","image":"1f693.png","sheet_x":29,"sheet_y":30,"short_name":"police_car","short_names":["police_car"],"text":null,"texts":null,"category":"Places","sort_order":7,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ONCOMING POLICE CAR","unified":"1F694","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f694.png","sheet_x":29,"sheet_y":31,"short_name":"oncoming_police_car","short_names":["oncoming_police_car"],"text":null,"texts":null,"category":"Places","sort_order":17,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TAXI","unified":"1F695","variations":[],"docomo":"E65E","au":"E4B1","softbank":"E15A","google":"FE7EF","image":"1f695.png","sheet_x":29,"sheet_y":32,"short_name":"taxi","short_names":["taxi"],"text":null,"texts":null,"category":"Places","sort_order":2,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ONCOMING TAXI","unified":"1F696","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f696.png","sheet_x":29,"sheet_y":33,"short_name":"oncoming_taxi","short_names":["oncoming_taxi"],"text":null,"texts":null,"category":"Places","sort_order":20,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"AUTOMOBILE","unified":"1F697","variations":[],"docomo":"E65E","au":"E4B1","softbank":"E01B","google":"FE7E4","image":"1f697.png","sheet_x":29,"sheet_y":34,"short_name":"car","short_names":["car","red_car"],"text":null,"texts":null,"category":"Places","sort_order":1,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ONCOMING AUTOMOBILE","unified":"1F698","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f698.png","sheet_x":29,"sheet_y":35,"short_name":"oncoming_automobile","short_names":["oncoming_automobile"],"text":null,"texts":null,"category":"Places","sort_order":19,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RECREATIONAL VEHICLE","unified":"1F699","variations":[],"docomo":"E65F","au":"E4B1","softbank":"E42E","google":"FE7E5","image":"1f699.png","sheet_x":29,"sheet_y":36,"short_name":"blue_car","short_names":["blue_car"],"text":null,"texts":null,"category":"Places","sort_order":3,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DELIVERY TRUCK","unified":"1F69A","variations":[],"docomo":null,"au":"E4B2","softbank":"E42F","google":"FE7F1","image":"1f69a.png","sheet_x":29,"sheet_y":37,"short_name":"truck","short_names":["truck"],"text":null,"texts":null,"category":"Places","sort_order":11,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ARTICULATED LORRY","unified":"1F69B","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f69b.png","sheet_x":29,"sheet_y":38,"short_name":"articulated_lorry","short_names":["articulated_lorry"],"text":null,"texts":null,"category":"Places","sort_order":12,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TRACTOR","unified":"1F69C","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f69c.png","sheet_x":29,"sheet_y":39,"short_name":"tractor","short_names":["tractor"],"text":null,"texts":null,"category":"Places","sort_order":13,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MONORAIL","unified":"1F69D","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f69d.png","sheet_x":29,"sheet_y":40,"short_name":"monorail","short_names":["monorail"],"text":null,"texts":null,"category":"Places","sort_order":26,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MOUNTAIN RAILWAY","unified":"1F69E","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f69e.png","sheet_x":30,"sheet_y":0,"short_name":"mountain_railway","short_names":["mountain_railway"],"text":null,"texts":null,"category":"Places","sort_order":30,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SUSPENSION RAILWAY","unified":"1F69F","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f69f.png","sheet_x":30,"sheet_y":1,"short_name":"suspension_railway","short_names":["suspension_railway"],"text":null,"texts":null,"category":"Places","sort_order":23,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MOUNTAIN CABLEWAY","unified":"1F6A0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6a0.png","sheet_x":30,"sheet_y":2,"short_name":"mountain_cableway","short_names":["mountain_cableway"],"text":null,"texts":null,"category":"Places","sort_order":22,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"AERIAL TRAMWAY","unified":"1F6A1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6a1.png","sheet_x":30,"sheet_y":3,"short_name":"aerial_tramway","short_names":["aerial_tramway"],"text":null,"texts":null,"category":"Places","sort_order":21,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SHIP","unified":"1F6A2","variations":[],"docomo":"E661","au":"EA82","softbank":"E202","google":"FE7E8","image":"1f6a2.png","sheet_x":30,"sheet_y":4,"short_name":"ship","short_names":["ship"],"text":null,"texts":null,"category":"Places","sort_order":56,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ROWBOAT","unified":"1F6A3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6a3.png","sheet_x":30,"sheet_y":5,"short_name":"rowboat","short_names":["rowboat"],"text":null,"texts":null,"category":"Activity","sort_order":22,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F6A3-1F3FB":{"unified":"1F6A3-1F3FB","image":"1f6a3-1f3fb.png","sheet_x":30,"sheet_y":6,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6A3-1F3FC":{"unified":"1F6A3-1F3FC","image":"1f6a3-1f3fc.png","sheet_x":30,"sheet_y":7,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6A3-1F3FD":{"unified":"1F6A3-1F3FD","image":"1f6a3-1f3fd.png","sheet_x":30,"sheet_y":8,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6A3-1F3FE":{"unified":"1F6A3-1F3FE","image":"1f6a3-1f3fe.png","sheet_x":30,"sheet_y":9,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6A3-1F3FF":{"unified":"1F6A3-1F3FF","image":"1f6a3-1f3ff.png","sheet_x":30,"sheet_y":10,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"SPEEDBOAT","unified":"1F6A4","variations":[],"docomo":"E6A3","au":"E4B4","softbank":"E135","google":"FE7EE","image":"1f6a4.png","sheet_x":30,"sheet_y":11,"short_name":"speedboat","short_names":["speedboat"],"text":null,"texts":null,"category":"Places","sort_order":43,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HORIZONTAL TRAFFIC LIGHT","unified":"1F6A5","variations":[],"docomo":"E66D","au":"E46A","softbank":"E14E","google":"FE7F7","image":"1f6a5.png","sheet_x":30,"sheet_y":12,"short_name":"traffic_light","short_names":["traffic_light"],"text":null,"texts":null,"category":"Places","sort_order":54,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"VERTICAL TRAFFIC LIGHT","unified":"1F6A6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6a6.png","sheet_x":30,"sheet_y":13,"short_name":"vertical_traffic_light","short_names":["vertical_traffic_light"],"text":null,"texts":null,"category":"Places","sort_order":53,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CONSTRUCTION SIGN","unified":"1F6A7","variations":[],"docomo":null,"au":"E5D7","softbank":"E137","google":"FE7F8","image":"1f6a7.png","sheet_x":30,"sheet_y":14,"short_name":"construction","short_names":["construction"],"text":null,"texts":null,"category":"Places","sort_order":50,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"POLICE CARS REVOLVING LIGHT","unified":"1F6A8","variations":[],"docomo":null,"au":"EB73","softbank":"E432","google":"FE7F9","image":"1f6a8.png","sheet_x":30,"sheet_y":15,"short_name":"rotating_light","short_names":["rotating_light"],"text":null,"texts":null,"category":"Places","sort_order":16,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TRIANGULAR FLAG ON POST","unified":"1F6A9","variations":[],"docomo":"E6DE","au":"EB2C","softbank":null,"google":"FEB22","image":"1f6a9.png","sheet_x":30,"sheet_y":16,"short_name":"triangular_flag_on_post","short_names":["triangular_flag_on_post"],"text":null,"texts":null,"category":"Objects","sort_order":163,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DOOR","unified":"1F6AA","variations":[],"docomo":"E714","au":null,"softbank":null,"google":"FE4F3","image":"1f6aa.png","sheet_x":30,"sheet_y":17,"short_name":"door","short_names":["door"],"text":null,"texts":null,"category":"Objects","sort_order":94,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NO ENTRY SIGN","unified":"1F6AB","variations":[],"docomo":"E738","au":"E541","softbank":null,"google":"FEB48","image":"1f6ab.png","sheet_x":30,"sheet_y":18,"short_name":"no_entry_sign","short_names":["no_entry_sign"],"text":null,"texts":null,"category":"Symbols","sort_order":71,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SMOKING SYMBOL","unified":"1F6AC","variations":[],"docomo":"E67F","au":"E47D","softbank":"E30E","google":"FEB1E","image":"1f6ac.png","sheet_x":30,"sheet_y":19,"short_name":"smoking","short_names":["smoking"],"text":null,"texts":null,"category":"Objects","sort_order":69,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NO SMOKING SYMBOL","unified":"1F6AD","variations":[],"docomo":"E680","au":"E47E","softbank":"E208","google":"FEB1F","image":"1f6ad.png","sheet_x":30,"sheet_y":20,"short_name":"no_smoking","short_names":["no_smoking"],"text":null,"texts":null,"category":"Symbols","sort_order":116,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PUT LITTER IN ITS PLACE SYMBOL","unified":"1F6AE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6ae.png","sheet_x":30,"sheet_y":21,"short_name":"put_litter_in_its_place","short_names":["put_litter_in_its_place"],"text":null,"texts":null,"category":"Symbols","sort_order":124,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"DO NOT LITTER SYMBOL","unified":"1F6AF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6af.png","sheet_x":30,"sheet_y":22,"short_name":"do_not_litter","short_names":["do_not_litter"],"text":null,"texts":null,"category":"Symbols","sort_order":77,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"POTABLE WATER SYMBOL","unified":"1F6B0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b0.png","sheet_x":30,"sheet_y":23,"short_name":"potable_water","short_names":["potable_water"],"text":null,"texts":null,"category":"Symbols","sort_order":119,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NON-POTABLE WATER SYMBOL","unified":"1F6B1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b1.png","sheet_x":30,"sheet_y":24,"short_name":"non-potable_water","short_names":["non-potable_water"],"text":null,"texts":null,"category":"Symbols","sort_order":79,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BICYCLE","unified":"1F6B2","variations":[],"docomo":"E71D","au":"E4AE","softbank":"E136","google":"FE7EB","image":"1f6b2.png","sheet_x":30,"sheet_y":25,"short_name":"bike","short_names":["bike"],"text":null,"texts":null,"category":"Places","sort_order":15,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NO BICYCLES","unified":"1F6B3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b3.png","sheet_x":30,"sheet_y":26,"short_name":"no_bicycles","short_names":["no_bicycles"],"text":null,"texts":null,"category":"Symbols","sort_order":78,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BICYCLIST","unified":"1F6B4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b4.png","sheet_x":30,"sheet_y":27,"short_name":"bicyclist","short_names":["bicyclist"],"text":null,"texts":null,"category":"Activity","sort_order":28,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F6B4-1F3FB":{"unified":"1F6B4-1F3FB","image":"1f6b4-1f3fb.png","sheet_x":30,"sheet_y":28,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6B4-1F3FC":{"unified":"1F6B4-1F3FC","image":"1f6b4-1f3fc.png","sheet_x":30,"sheet_y":29,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6B4-1F3FD":{"unified":"1F6B4-1F3FD","image":"1f6b4-1f3fd.png","sheet_x":30,"sheet_y":30,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6B4-1F3FE":{"unified":"1F6B4-1F3FE","image":"1f6b4-1f3fe.png","sheet_x":30,"sheet_y":31,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6B4-1F3FF":{"unified":"1F6B4-1F3FF","image":"1f6b4-1f3ff.png","sheet_x":30,"sheet_y":32,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"MOUNTAIN BICYCLIST","unified":"1F6B5","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b5.png","sheet_x":30,"sheet_y":33,"short_name":"mountain_bicyclist","short_names":["mountain_bicyclist"],"text":null,"texts":null,"category":"Activity","sort_order":29,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F6B5-1F3FB":{"unified":"1F6B5-1F3FB","image":"1f6b5-1f3fb.png","sheet_x":30,"sheet_y":34,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6B5-1F3FC":{"unified":"1F6B5-1F3FC","image":"1f6b5-1f3fc.png","sheet_x":30,"sheet_y":35,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6B5-1F3FD":{"unified":"1F6B5-1F3FD","image":"1f6b5-1f3fd.png","sheet_x":30,"sheet_y":36,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6B5-1F3FE":{"unified":"1F6B5-1F3FE","image":"1f6b5-1f3fe.png","sheet_x":30,"sheet_y":37,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6B5-1F3FF":{"unified":"1F6B5-1F3FF","image":"1f6b5-1f3ff.png","sheet_x":30,"sheet_y":38,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"PEDESTRIAN","unified":"1F6B6","variations":[],"docomo":"E733","au":"EB72","softbank":"E201","google":"FE7F0","image":"1f6b6.png","sheet_x":30,"sheet_y":39,"short_name":"walking","short_names":["walking"],"text":null,"texts":null,"category":"People","sort_order":139,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F6B6-1F3FB":{"unified":"1F6B6-1F3FB","image":"1f6b6-1f3fb.png","sheet_x":30,"sheet_y":40,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6B6-1F3FC":{"unified":"1F6B6-1F3FC","image":"1f6b6-1f3fc.png","sheet_x":31,"sheet_y":0,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6B6-1F3FD":{"unified":"1F6B6-1F3FD","image":"1f6b6-1f3fd.png","sheet_x":31,"sheet_y":1,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6B6-1F3FE":{"unified":"1F6B6-1F3FE","image":"1f6b6-1f3fe.png","sheet_x":31,"sheet_y":2,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6B6-1F3FF":{"unified":"1F6B6-1F3FF","image":"1f6b6-1f3ff.png","sheet_x":31,"sheet_y":3,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"NO PEDESTRIANS","unified":"1F6B7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b7.png","sheet_x":31,"sheet_y":4,"short_name":"no_pedestrians","short_names":["no_pedestrians"],"text":null,"texts":null,"category":"Symbols","sort_order":76,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CHILDREN CROSSING","unified":"1F6B8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6b8.png","sheet_x":31,"sheet_y":5,"short_name":"children_crossing","short_names":["children_crossing"],"text":null,"texts":null,"category":"Symbols","sort_order":95,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MENS SYMBOL","unified":"1F6B9","variations":[],"docomo":null,"au":null,"softbank":"E138","google":"FEB33","image":"1f6b9.png","sheet_x":31,"sheet_y":6,"short_name":"mens","short_names":["mens"],"text":null,"texts":null,"category":"Symbols","sort_order":120,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WOMENS SYMBOL","unified":"1F6BA","variations":[],"docomo":null,"au":null,"softbank":"E139","google":"FEB34","image":"1f6ba.png","sheet_x":31,"sheet_y":7,"short_name":"womens","short_names":["womens"],"text":null,"texts":null,"category":"Symbols","sort_order":121,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RESTROOM","unified":"1F6BB","variations":[],"docomo":"E66E","au":"E4A5","softbank":"E151","google":"FE506","image":"1f6bb.png","sheet_x":31,"sheet_y":8,"short_name":"restroom","short_names":["restroom"],"text":null,"texts":null,"category":"Symbols","sort_order":123,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BABY SYMBOL","unified":"1F6BC","variations":[],"docomo":null,"au":"EB18","softbank":"E13A","google":"FEB35","image":"1f6bc.png","sheet_x":31,"sheet_y":9,"short_name":"baby_symbol","short_names":["baby_symbol"],"text":null,"texts":null,"category":"Symbols","sort_order":122,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TOILET","unified":"1F6BD","variations":[],"docomo":"E66E","au":"E4A5","softbank":"E140","google":"FE507","image":"1f6bd.png","sheet_x":31,"sheet_y":10,"short_name":"toilet","short_names":["toilet"],"text":null,"texts":null,"category":"Objects","sort_order":86,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"WATER CLOSET","unified":"1F6BE","variations":[],"docomo":"E66E","au":"E4A5","softbank":"E309","google":"FE508","image":"1f6be.png","sheet_x":31,"sheet_y":11,"short_name":"wc","short_names":["wc"],"text":null,"texts":null,"category":"Symbols","sort_order":117,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SHOWER","unified":"1F6BF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6bf.png","sheet_x":31,"sheet_y":12,"short_name":"shower","short_names":["shower"],"text":null,"texts":null,"category":"Objects","sort_order":87,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BATH","unified":"1F6C0","variations":[],"docomo":"E6F7","au":"E5D8","softbank":"E13F","google":"FE505","image":"1f6c0.png","sheet_x":31,"sheet_y":13,"short_name":"bath","short_names":["bath"],"text":null,"texts":null,"category":"Activity","sort_order":25,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F6C0-1F3FB":{"unified":"1F6C0-1F3FB","image":"1f6c0-1f3fb.png","sheet_x":31,"sheet_y":14,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6C0-1F3FC":{"unified":"1F6C0-1F3FC","image":"1f6c0-1f3fc.png","sheet_x":31,"sheet_y":15,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6C0-1F3FD":{"unified":"1F6C0-1F3FD","image":"1f6c0-1f3fd.png","sheet_x":31,"sheet_y":16,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6C0-1F3FE":{"unified":"1F6C0-1F3FE","image":"1f6c0-1f3fe.png","sheet_x":31,"sheet_y":17,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F6C0-1F3FF":{"unified":"1F6C0-1F3FF","image":"1f6c0-1f3ff.png","sheet_x":31,"sheet_y":18,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"BATHTUB","unified":"1F6C1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6c1.png","sheet_x":31,"sheet_y":19,"short_name":"bathtub","short_names":["bathtub"],"text":null,"texts":null,"category":"Objects","sort_order":88,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PASSPORT CONTROL","unified":"1F6C2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6c2.png","sheet_x":31,"sheet_y":20,"short_name":"passport_control","short_names":["passport_control"],"text":null,"texts":null,"category":"Symbols","sort_order":111,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CUSTOMS","unified":"1F6C3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6c3.png","sheet_x":31,"sheet_y":21,"short_name":"customs","short_names":["customs"],"text":null,"texts":null,"category":"Symbols","sort_order":112,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BAGGAGE CLAIM","unified":"1F6C4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6c4.png","sheet_x":31,"sheet_y":22,"short_name":"baggage_claim","short_names":["baggage_claim"],"text":null,"texts":null,"category":"Symbols","sort_order":113,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LEFT LUGGAGE","unified":"1F6C5","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6c5.png","sheet_x":31,"sheet_y":23,"short_name":"left_luggage","short_names":["left_luggage"],"text":null,"texts":null,"category":"Symbols","sort_order":114,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"COUCH AND LAMP","unified":"1F6CB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6cb.png","sheet_x":31,"sheet_y":24,"short_name":"couch_and_lamp","short_names":["couch_and_lamp"],"text":null,"texts":null,"category":"Objects","sort_order":91,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SLEEPING ACCOMMODATION","unified":"1F6CC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6cc.png","sheet_x":31,"sheet_y":25,"short_name":"sleeping_accommodation","short_names":["sleeping_accommodation"],"text":null,"texts":null,"category":"Objects","sort_order":92,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SHOPPING BAGS","unified":"1F6CD","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6cd.png","sheet_x":31,"sheet_y":26,"short_name":"shopping_bags","short_names":["shopping_bags"],"text":null,"texts":null,"category":"Objects","sort_order":100,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BELLHOP BELL","unified":"1F6CE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6ce.png","sheet_x":31,"sheet_y":27,"short_name":"bellhop_bell","short_names":["bellhop_bell"],"text":null,"texts":null,"category":"Objects","sort_order":95,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"BED","unified":"1F6CF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6cf.png","sheet_x":31,"sheet_y":28,"short_name":"bed","short_names":["bed"],"text":null,"texts":null,"category":"Objects","sort_order":93,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PLACE OF WORSHIP","unified":"1F6D0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6d0.png","sheet_x":31,"sheet_y":29,"short_name":"place_of_worship","short_names":["place_of_worship"],"text":null,"texts":null,"category":"Symbols","sort_order":26,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HAMMER AND WRENCH","unified":"1F6E0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6e0.png","sheet_x":31,"sheet_y":30,"short_name":"hammer_and_wrench","short_names":["hammer_and_wrench"],"text":null,"texts":null,"category":"Objects","sort_order":58,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SHIELD","unified":"1F6E1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6e1.png","sheet_x":31,"sheet_y":31,"short_name":"shield","short_names":["shield"],"text":null,"texts":null,"category":"Objects","sort_order":68,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"OIL DRUM","unified":"1F6E2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6e2.png","sheet_x":31,"sheet_y":32,"short_name":"oil_drum","short_names":["oil_drum"],"text":null,"texts":null,"category":"Objects","sort_order":45,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MOTORWAY","unified":"1F6E3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6e3.png","sheet_x":31,"sheet_y":33,"short_name":"motorway","short_names":["motorway"],"text":null,"texts":null,"category":"Places","sort_order":74,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"RAILWAY TRACK","unified":"1F6E4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6e4.png","sheet_x":31,"sheet_y":34,"short_name":"railway_track","short_names":["railway_track"],"text":null,"texts":null,"category":"Places","sort_order":75,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MOTOR BOAT","unified":"1F6E5","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6e5.png","sheet_x":31,"sheet_y":35,"short_name":"motor_boat","short_names":["motor_boat"],"text":null,"texts":null,"category":"Places","sort_order":42,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SMALL AIRPLANE","unified":"1F6E9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6e9.png","sheet_x":31,"sheet_y":36,"short_name":"small_airplane","short_names":["small_airplane"],"text":null,"texts":null,"category":"Places","sort_order":37,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"AIRPLANE DEPARTURE","unified":"1F6EB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6eb.png","sheet_x":31,"sheet_y":37,"short_name":"airplane_departure","short_names":["airplane_departure"],"text":null,"texts":null,"category":"Places","sort_order":39,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"AIRPLANE ARRIVING","unified":"1F6EC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6ec.png","sheet_x":31,"sheet_y":38,"short_name":"airplane_arriving","short_names":["airplane_arriving"],"text":null,"texts":null,"category":"Places","sort_order":40,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SATELLITE","unified":"1F6F0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6f0.png","sheet_x":31,"sheet_y":39,"short_name":"satellite","short_names":["satellite"],"text":null,"texts":null,"category":"Places","sort_order":47,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"PASSENGER SHIP","unified":"1F6F3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f6f3.png","sheet_x":31,"sheet_y":40,"short_name":"passenger_ship","short_names":["passenger_ship"],"text":null,"texts":null,"category":"Places","sort_order":45,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ZIPPER-MOUTH FACE","unified":"1F910","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f910.png","sheet_x":32,"sheet_y":0,"short_name":"zipper_mouth_face","short_names":["zipper_mouth_face"],"text":null,"texts":null,"category":"People","sort_order":64,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"MONEY-MOUTH FACE","unified":"1F911","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f911.png","sheet_x":32,"sheet_y":1,"short_name":"money_mouth_face","short_names":["money_mouth_face"],"text":null,"texts":null,"category":"People","sort_order":25,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACE WITH THERMOMETER","unified":"1F912","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f912.png","sheet_x":32,"sheet_y":2,"short_name":"face_with_thermometer","short_names":["face_with_thermometer"],"text":null,"texts":null,"category":"People","sort_order":66,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"NERD FACE","unified":"1F913","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f913.png","sheet_x":32,"sheet_y":3,"short_name":"nerd_face","short_names":["nerd_face"],"text":null,"texts":null,"category":"People","sort_order":26,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"THINKING FACE","unified":"1F914","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f914.png","sheet_x":32,"sheet_y":4,"short_name":"thinking_face","short_names":["thinking_face"],"text":null,"texts":null,"category":"People","sort_order":35,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"FACE WITH HEAD-BANDAGE","unified":"1F915","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f915.png","sheet_x":32,"sheet_y":5,"short_name":"face_with_head_bandage","short_names":["face_with_head_bandage"],"text":null,"texts":null,"category":"People","sort_order":67,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"ROBOT FACE","unified":"1F916","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f916.png","sheet_x":32,"sheet_y":6,"short_name":"robot_face","short_names":["robot_face"],"text":null,"texts":null,"category":"People","sort_order":78,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HUGGING FACE","unified":"1F917","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f917.png","sheet_x":32,"sheet_y":7,"short_name":"hugging_face","short_names":["hugging_face"],"text":null,"texts":null,"category":"People","sort_order":28,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SIGN OF THE HORNS","unified":"1F918","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f918.png","sheet_x":32,"sheet_y":8,"short_name":"the_horns","short_names":["the_horns","sign_of_the_horns"],"text":null,"texts":null,"category":"People","sort_order":108,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true,"skin_variations":{"1F918-1F3FB":{"unified":"1F918-1F3FB","image":"1f918-1f3fb.png","sheet_x":32,"sheet_y":9,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F918-1F3FC":{"unified":"1F918-1F3FC","image":"1f918-1f3fc.png","sheet_x":32,"sheet_y":10,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F918-1F3FD":{"unified":"1F918-1F3FD","image":"1f918-1f3fd.png","sheet_x":32,"sheet_y":11,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F918-1F3FE":{"unified":"1F918-1F3FE","image":"1f918-1f3fe.png","sheet_x":32,"sheet_y":12,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},"1F918-1F3FF":{"unified":"1F918-1F3FF","image":"1f918-1f3ff.png","sheet_x":32,"sheet_y":13,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true}}},{"name":"CRAB","unified":"1F980","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f980.png","sheet_x":32,"sheet_y":14,"short_name":"crab","short_names":["crab"],"text":null,"texts":null,"category":"Nature","sort_order":38,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"LION FACE","unified":"1F981","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f981.png","sheet_x":32,"sheet_y":15,"short_name":"lion_face","short_names":["lion_face"],"text":null,"texts":null,"category":"Nature","sort_order":10,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"SCORPION","unified":"1F982","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f982.png","sheet_x":32,"sheet_y":16,"short_name":"scorpion","short_names":["scorpion"],"text":null,"texts":null,"category":"Nature","sort_order":37,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"TURKEY","unified":"1F983","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f983.png","sheet_x":32,"sheet_y":17,"short_name":"turkey","short_names":["turkey"],"text":null,"texts":null,"category":"Nature","sort_order":64,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"UNICORN FACE","unified":"1F984","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f984.png","sheet_x":32,"sheet_y":18,"short_name":"unicorn_face","short_names":["unicorn_face"],"text":null,"texts":null,"category":"Nature","sort_order":30,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"CHEESE WEDGE","unified":"1F9C0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f9c0.png","sheet_x":32,"sheet_y":19,"short_name":"cheese_wedge","short_names":["cheese_wedge"],"text":null,"texts":null,"category":"Foods","sort_order":21,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"HASH KEY","unified":"0023-20E3","variations":["0023-FE0F-20E3"],"docomo":"E6E0","au":"EB84","softbank":"E210","google":"FE82C","image":"0023-20e3.png","sheet_x":32,"sheet_y":20,"short_name":"hash","short_names":["hash"],"text":null,"texts":null,"category":"Symbols","sort_order":178,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":null,"unified":"002A-20E3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"002a-20e3.png","sheet_x":32,"sheet_y":21,"short_name":"keycap_star","short_names":["keycap_star"],"text":null,"texts":null,"category":null,"sort_order":null,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KEYCAP 0","unified":"0030-20E3","variations":["0030-FE0F-20E3"],"docomo":"E6EB","au":"E5AC","softbank":"E225","google":"FE837","image":"0030-20e3.png","sheet_x":32,"sheet_y":22,"short_name":"zero","short_names":["zero"],"text":null,"texts":null,"category":"Symbols","sort_order":134,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KEYCAP 1","unified":"0031-20E3","variations":["0031-FE0F-20E3"],"docomo":"E6E2","au":"E522","softbank":"E21C","google":"FE82E","image":"0031-20e3.png","sheet_x":32,"sheet_y":23,"short_name":"one","short_names":["one"],"text":null,"texts":null,"category":"Symbols","sort_order":135,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KEYCAP 2","unified":"0032-20E3","variations":["0032-FE0F-20E3"],"docomo":"E6E3","au":"E523","softbank":"E21D","google":"FE82F","image":"0032-20e3.png","sheet_x":32,"sheet_y":24,"short_name":"two","short_names":["two"],"text":null,"texts":null,"category":"Symbols","sort_order":136,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KEYCAP 3","unified":"0033-20E3","variations":["0033-FE0F-20E3"],"docomo":"E6E4","au":"E524","softbank":"E21E","google":"FE830","image":"0033-20e3.png","sheet_x":32,"sheet_y":25,"short_name":"three","short_names":["three"],"text":null,"texts":null,"category":"Symbols","sort_order":137,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KEYCAP 4","unified":"0034-20E3","variations":["0034-FE0F-20E3"],"docomo":"E6E5","au":"E525","softbank":"E21F","google":"FE831","image":"0034-20e3.png","sheet_x":32,"sheet_y":26,"short_name":"four","short_names":["four"],"text":null,"texts":null,"category":"Symbols","sort_order":138,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KEYCAP 5","unified":"0035-20E3","variations":["0035-FE0F-20E3"],"docomo":"E6E6","au":"E526","softbank":"E220","google":"FE832","image":"0035-20e3.png","sheet_x":32,"sheet_y":27,"short_name":"five","short_names":["five"],"text":null,"texts":null,"category":"Symbols","sort_order":139,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KEYCAP 6","unified":"0036-20E3","variations":["0036-FE0F-20E3"],"docomo":"E6E7","au":"E527","softbank":"E221","google":"FE833","image":"0036-20e3.png","sheet_x":32,"sheet_y":28,"short_name":"six","short_names":["six"],"text":null,"texts":null,"category":"Symbols","sort_order":140,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KEYCAP 7","unified":"0037-20E3","variations":["0037-FE0F-20E3"],"docomo":"E6E8","au":"E528","softbank":"E222","google":"FE834","image":"0037-20e3.png","sheet_x":32,"sheet_y":29,"short_name":"seven","short_names":["seven"],"text":null,"texts":null,"category":"Symbols","sort_order":141,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KEYCAP 8","unified":"0038-20E3","variations":["0038-FE0F-20E3"],"docomo":"E6E9","au":"E529","softbank":"E223","google":"FE835","image":"0038-20e3.png","sheet_x":32,"sheet_y":30,"short_name":"eight","short_names":["eight"],"text":null,"texts":null,"category":"Symbols","sort_order":142,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"KEYCAP 9","unified":"0039-20E3","variations":["0039-FE0F-20E3"],"docomo":"E6EA","au":"E52A","softbank":"E224","google":"FE836","image":"0039-20e3.png","sheet_x":32,"sheet_y":31,"short_name":"nine","short_names":["nine"],"text":null,"texts":null,"category":"Symbols","sort_order":143,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS AC","unified":"1F1E6-1F1E8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1e8.png","sheet_x":32,"sheet_y":32,"short_name":"flag-ac","short_names":["flag-ac"],"text":null,"texts":null,"category":null,"sort_order":null,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS AD","unified":"1F1E6-1F1E9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1e9.png","sheet_x":32,"sheet_y":33,"short_name":"flag-ad","short_names":["flag-ad"],"text":null,"texts":null,"category":"Flags","sort_order":6,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS AE","unified":"1F1E6-1F1EA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1ea.png","sheet_x":32,"sheet_y":34,"short_name":"flag-ae","short_names":["flag-ae"],"text":null,"texts":null,"category":"Flags","sort_order":233,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS AF","unified":"1F1E6-1F1EB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1eb.png","sheet_x":32,"sheet_y":35,"short_name":"flag-af","short_names":["flag-af"],"text":null,"texts":null,"category":"Flags","sort_order":1,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS AG","unified":"1F1E6-1F1EC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1ec.png","sheet_x":32,"sheet_y":36,"short_name":"flag-ag","short_names":["flag-ag"],"text":null,"texts":null,"category":"Flags","sort_order":10,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS AI","unified":"1F1E6-1F1EE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1ee.png","sheet_x":32,"sheet_y":37,"short_name":"flag-ai","short_names":["flag-ai"],"text":null,"texts":null,"category":"Flags","sort_order":8,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS AL","unified":"1F1E6-1F1F1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1f1.png","sheet_x":32,"sheet_y":38,"short_name":"flag-al","short_names":["flag-al"],"text":null,"texts":null,"category":"Flags","sort_order":3,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS AM","unified":"1F1E6-1F1F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1f2.png","sheet_x":32,"sheet_y":39,"short_name":"flag-am","short_names":["flag-am"],"text":null,"texts":null,"category":"Flags","sort_order":12,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS AO","unified":"1F1E6-1F1F4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1f4.png","sheet_x":32,"sheet_y":40,"short_name":"flag-ao","short_names":["flag-ao"],"text":null,"texts":null,"category":"Flags","sort_order":7,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS AQ","unified":"1F1E6-1F1F6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1f6.png","sheet_x":33,"sheet_y":0,"short_name":"flag-aq","short_names":["flag-aq"],"text":null,"texts":null,"category":"Flags","sort_order":9,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS AR","unified":"1F1E6-1F1F7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1f7.png","sheet_x":33,"sheet_y":1,"short_name":"flag-ar","short_names":["flag-ar"],"text":null,"texts":null,"category":"Flags","sort_order":11,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS AS","unified":"1F1E6-1F1F8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1f8.png","sheet_x":33,"sheet_y":2,"short_name":"flag-as","short_names":["flag-as"],"text":null,"texts":null,"category":"Flags","sort_order":5,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS AT","unified":"1F1E6-1F1F9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1f9.png","sheet_x":33,"sheet_y":3,"short_name":"flag-at","short_names":["flag-at"],"text":null,"texts":null,"category":"Flags","sort_order":15,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS AU","unified":"1F1E6-1F1FA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1fa.png","sheet_x":33,"sheet_y":4,"short_name":"flag-au","short_names":["flag-au"],"text":null,"texts":null,"category":"Flags","sort_order":14,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS AW","unified":"1F1E6-1F1FC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1fc.png","sheet_x":33,"sheet_y":5,"short_name":"flag-aw","short_names":["flag-aw"],"text":null,"texts":null,"category":"Flags","sort_order":13,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS AX","unified":"1F1E6-1F1FD","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1fd.png","sheet_x":33,"sheet_y":6,"short_name":"flag-ax","short_names":["flag-ax"],"text":null,"texts":null,"category":"Flags","sort_order":2,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS AZ","unified":"1F1E6-1F1FF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e6-1f1ff.png","sheet_x":33,"sheet_y":7,"short_name":"flag-az","short_names":["flag-az"],"text":null,"texts":null,"category":"Flags","sort_order":16,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BA","unified":"1F1E7-1F1E6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1e6.png","sheet_x":33,"sheet_y":8,"short_name":"flag-ba","short_names":["flag-ba"],"text":null,"texts":null,"category":"Flags","sort_order":29,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BB","unified":"1F1E7-1F1E7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1e7.png","sheet_x":33,"sheet_y":9,"short_name":"flag-bb","short_names":["flag-bb"],"text":null,"texts":null,"category":"Flags","sort_order":20,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BD","unified":"1F1E7-1F1E9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1e9.png","sheet_x":33,"sheet_y":10,"short_name":"flag-bd","short_names":["flag-bd"],"text":null,"texts":null,"category":"Flags","sort_order":19,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BE","unified":"1F1E7-1F1EA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1ea.png","sheet_x":33,"sheet_y":11,"short_name":"flag-be","short_names":["flag-be"],"text":null,"texts":null,"category":"Flags","sort_order":22,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BF","unified":"1F1E7-1F1EB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1eb.png","sheet_x":33,"sheet_y":12,"short_name":"flag-bf","short_names":["flag-bf"],"text":null,"texts":null,"category":"Flags","sort_order":36,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BG","unified":"1F1E7-1F1EC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1ec.png","sheet_x":33,"sheet_y":13,"short_name":"flag-bg","short_names":["flag-bg"],"text":null,"texts":null,"category":"Flags","sort_order":35,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BH","unified":"1F1E7-1F1ED","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1ed.png","sheet_x":33,"sheet_y":14,"short_name":"flag-bh","short_names":["flag-bh"],"text":null,"texts":null,"category":"Flags","sort_order":18,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BI","unified":"1F1E7-1F1EE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1ee.png","sheet_x":33,"sheet_y":15,"short_name":"flag-bi","short_names":["flag-bi"],"text":null,"texts":null,"category":"Flags","sort_order":37,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BJ","unified":"1F1E7-1F1EF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1ef.png","sheet_x":33,"sheet_y":16,"short_name":"flag-bj","short_names":["flag-bj"],"text":null,"texts":null,"category":"Flags","sort_order":24,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BL","unified":"1F1E7-1F1F1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1f1.png","sheet_x":33,"sheet_y":17,"short_name":"flag-bl","short_names":["flag-bl"],"text":null,"texts":null,"category":"Flags","sort_order":185,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BM","unified":"1F1E7-1F1F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1f2.png","sheet_x":33,"sheet_y":18,"short_name":"flag-bm","short_names":["flag-bm"],"text":null,"texts":null,"category":"Flags","sort_order":25,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BN","unified":"1F1E7-1F1F3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1f3.png","sheet_x":33,"sheet_y":19,"short_name":"flag-bn","short_names":["flag-bn"],"text":null,"texts":null,"category":"Flags","sort_order":34,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BO","unified":"1F1E7-1F1F4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1f4.png","sheet_x":33,"sheet_y":20,"short_name":"flag-bo","short_names":["flag-bo"],"text":null,"texts":null,"category":"Flags","sort_order":27,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BQ","unified":"1F1E7-1F1F6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1f6.png","sheet_x":33,"sheet_y":21,"short_name":"flag-bq","short_names":["flag-bq"],"text":null,"texts":null,"category":"Flags","sort_order":28,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BR","unified":"1F1E7-1F1F7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1f7.png","sheet_x":33,"sheet_y":22,"short_name":"flag-br","short_names":["flag-br"],"text":null,"texts":null,"category":"Flags","sort_order":31,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BS","unified":"1F1E7-1F1F8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1f8.png","sheet_x":33,"sheet_y":23,"short_name":"flag-bs","short_names":["flag-bs"],"text":null,"texts":null,"category":"Flags","sort_order":17,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BT","unified":"1F1E7-1F1F9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1f9.png","sheet_x":33,"sheet_y":24,"short_name":"flag-bt","short_names":["flag-bt"],"text":null,"texts":null,"category":"Flags","sort_order":26,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BV","unified":"1F1E7-1F1FB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1fb.png","sheet_x":33,"sheet_y":25,"short_name":"flag-bv","short_names":["flag-bv"],"text":null,"texts":null,"category":null,"sort_order":null,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BW","unified":"1F1E7-1F1FC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1fc.png","sheet_x":33,"sheet_y":26,"short_name":"flag-bw","short_names":["flag-bw"],"text":null,"texts":null,"category":"Flags","sort_order":30,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BY","unified":"1F1E7-1F1FE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1fe.png","sheet_x":33,"sheet_y":27,"short_name":"flag-by","short_names":["flag-by"],"text":null,"texts":null,"category":"Flags","sort_order":21,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS BZ","unified":"1F1E7-1F1FF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e7-1f1ff.png","sheet_x":33,"sheet_y":28,"short_name":"flag-bz","short_names":["flag-bz"],"text":null,"texts":null,"category":"Flags","sort_order":23,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CA","unified":"1F1E8-1F1E6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1e6.png","sheet_x":33,"sheet_y":29,"short_name":"flag-ca","short_names":["flag-ca"],"text":null,"texts":null,"category":"Flags","sort_order":41,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CC","unified":"1F1E8-1F1E8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1e8.png","sheet_x":33,"sheet_y":30,"short_name":"flag-cc","short_names":["flag-cc"],"text":null,"texts":null,"category":"Flags","sort_order":49,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CD","unified":"1F1E8-1F1E9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1e9.png","sheet_x":33,"sheet_y":31,"short_name":"flag-cd","short_names":["flag-cd"],"text":null,"texts":null,"category":"Flags","sort_order":53,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CF","unified":"1F1E8-1F1EB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1eb.png","sheet_x":33,"sheet_y":32,"short_name":"flag-cf","short_names":["flag-cf"],"text":null,"texts":null,"category":"Flags","sort_order":44,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CG","unified":"1F1E8-1F1EC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1ec.png","sheet_x":33,"sheet_y":33,"short_name":"flag-cg","short_names":["flag-cg"],"text":null,"texts":null,"category":"Flags","sort_order":52,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CH","unified":"1F1E8-1F1ED","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1ed.png","sheet_x":33,"sheet_y":34,"short_name":"flag-ch","short_names":["flag-ch"],"text":null,"texts":null,"category":"Flags","sort_order":215,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CI","unified":"1F1E8-1F1EE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1ee.png","sheet_x":33,"sheet_y":35,"short_name":"flag-ci","short_names":["flag-ci"],"text":null,"texts":null,"category":"Flags","sort_order":110,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CK","unified":"1F1E8-1F1F0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1f0.png","sheet_x":33,"sheet_y":36,"short_name":"flag-ck","short_names":["flag-ck"],"text":null,"texts":null,"category":"Flags","sort_order":54,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CL","unified":"1F1E8-1F1F1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1f1.png","sheet_x":33,"sheet_y":37,"short_name":"flag-cl","short_names":["flag-cl"],"text":null,"texts":null,"category":"Flags","sort_order":46,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CM","unified":"1F1E8-1F1F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1f2.png","sheet_x":33,"sheet_y":38,"short_name":"flag-cm","short_names":["flag-cm"],"text":null,"texts":null,"category":"Flags","sort_order":40,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CN","unified":"1F1E8-1F1F3","variations":[],"docomo":null,"au":"EB11","softbank":"E513","google":"FE4ED","image":"1f1e8-1f1f3.png","sheet_x":33,"sheet_y":39,"short_name":"flag-cn","short_names":["flag-cn","cn"],"text":null,"texts":null,"category":"Flags","sort_order":47,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CO","unified":"1F1E8-1F1F4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1f4.png","sheet_x":33,"sheet_y":40,"short_name":"flag-co","short_names":["flag-co"],"text":null,"texts":null,"category":"Flags","sort_order":50,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CP","unified":"1F1E8-1F1F5","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1f5.png","sheet_x":34,"sheet_y":0,"short_name":"flag-cp","short_names":["flag-cp"],"text":null,"texts":null,"category":null,"sort_order":null,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CR","unified":"1F1E8-1F1F7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1f7.png","sheet_x":34,"sheet_y":1,"short_name":"flag-cr","short_names":["flag-cr"],"text":null,"texts":null,"category":"Flags","sort_order":55,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CU","unified":"1F1E8-1F1FA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1fa.png","sheet_x":34,"sheet_y":2,"short_name":"flag-cu","short_names":["flag-cu"],"text":null,"texts":null,"category":"Flags","sort_order":57,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CV","unified":"1F1E8-1F1FB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1fb.png","sheet_x":34,"sheet_y":3,"short_name":"flag-cv","short_names":["flag-cv"],"text":null,"texts":null,"category":"Flags","sort_order":38,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CW","unified":"1F1E8-1F1FC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1fc.png","sheet_x":34,"sheet_y":4,"short_name":"flag-cw","short_names":["flag-cw"],"text":null,"texts":null,"category":"Flags","sort_order":58,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CX","unified":"1F1E8-1F1FD","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1fd.png","sheet_x":34,"sheet_y":5,"short_name":"flag-cx","short_names":["flag-cx"],"text":null,"texts":null,"category":"Flags","sort_order":48,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CY","unified":"1F1E8-1F1FE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1fe.png","sheet_x":34,"sheet_y":6,"short_name":"flag-cy","short_names":["flag-cy"],"text":null,"texts":null,"category":"Flags","sort_order":59,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS CZ","unified":"1F1E8-1F1FF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e8-1f1ff.png","sheet_x":34,"sheet_y":7,"short_name":"flag-cz","short_names":["flag-cz"],"text":null,"texts":null,"category":"Flags","sort_order":60,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS DE","unified":"1F1E9-1F1EA","variations":[],"docomo":null,"au":"EB0E","softbank":"E50E","google":"FE4E8","image":"1f1e9-1f1ea.png","sheet_x":34,"sheet_y":8,"short_name":"flag-de","short_names":["flag-de","de"],"text":null,"texts":null,"category":"Flags","sort_order":84,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS DG","unified":"1F1E9-1F1EC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e9-1f1ec.png","sheet_x":34,"sheet_y":9,"short_name":"flag-dg","short_names":["flag-dg"],"text":null,"texts":null,"category":null,"sort_order":null,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS DJ","unified":"1F1E9-1F1EF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e9-1f1ef.png","sheet_x":34,"sheet_y":10,"short_name":"flag-dj","short_names":["flag-dj"],"text":null,"texts":null,"category":"Flags","sort_order":62,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS DK","unified":"1F1E9-1F1F0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e9-1f1f0.png","sheet_x":34,"sheet_y":11,"short_name":"flag-dk","short_names":["flag-dk"],"text":null,"texts":null,"category":"Flags","sort_order":61,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS DM","unified":"1F1E9-1F1F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e9-1f1f2.png","sheet_x":34,"sheet_y":12,"short_name":"flag-dm","short_names":["flag-dm"],"text":null,"texts":null,"category":"Flags","sort_order":63,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS DO","unified":"1F1E9-1F1F4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e9-1f1f4.png","sheet_x":34,"sheet_y":13,"short_name":"flag-do","short_names":["flag-do"],"text":null,"texts":null,"category":"Flags","sort_order":64,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS DZ","unified":"1F1E9-1F1FF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1e9-1f1ff.png","sheet_x":34,"sheet_y":14,"short_name":"flag-dz","short_names":["flag-dz"],"text":null,"texts":null,"category":"Flags","sort_order":4,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS EA","unified":"1F1EA-1F1E6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ea-1f1e6.png","sheet_x":34,"sheet_y":15,"short_name":"flag-ea","short_names":["flag-ea"],"text":null,"texts":null,"category":null,"sort_order":null,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS EC","unified":"1F1EA-1F1E8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ea-1f1e8.png","sheet_x":34,"sheet_y":16,"short_name":"flag-ec","short_names":["flag-ec"],"text":null,"texts":null,"category":"Flags","sort_order":65,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS EE","unified":"1F1EA-1F1EA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ea-1f1ea.png","sheet_x":34,"sheet_y":17,"short_name":"flag-ee","short_names":["flag-ee"],"text":null,"texts":null,"category":"Flags","sort_order":70,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS EG","unified":"1F1EA-1F1EC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ea-1f1ec.png","sheet_x":34,"sheet_y":18,"short_name":"flag-eg","short_names":["flag-eg"],"text":null,"texts":null,"category":"Flags","sort_order":66,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS EH","unified":"1F1EA-1F1ED","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ea-1f1ed.png","sheet_x":34,"sheet_y":19,"short_name":"flag-eh","short_names":["flag-eh"],"text":null,"texts":null,"category":"Flags","sort_order":244,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS ER","unified":"1F1EA-1F1F7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ea-1f1f7.png","sheet_x":34,"sheet_y":20,"short_name":"flag-er","short_names":["flag-er"],"text":null,"texts":null,"category":"Flags","sort_order":69,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS ES","unified":"1F1EA-1F1F8","variations":[],"docomo":null,"au":"E5D5","softbank":"E511","google":"FE4EB","image":"1f1ea-1f1f8.png","sheet_x":34,"sheet_y":21,"short_name":"flag-es","short_names":["flag-es","es"],"text":null,"texts":null,"category":"Flags","sort_order":209,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS ET","unified":"1F1EA-1F1F9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ea-1f1f9.png","sheet_x":34,"sheet_y":22,"short_name":"flag-et","short_names":["flag-et"],"text":null,"texts":null,"category":"Flags","sort_order":71,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS EU","unified":"1F1EA-1F1FA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ea-1f1fa.png","sheet_x":34,"sheet_y":23,"short_name":"flag-eu","short_names":["flag-eu"],"text":null,"texts":null,"category":"Flags","sort_order":72,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS FI","unified":"1F1EB-1F1EE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1eb-1f1ee.png","sheet_x":34,"sheet_y":24,"short_name":"flag-fi","short_names":["flag-fi"],"text":null,"texts":null,"category":"Flags","sort_order":76,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS FJ","unified":"1F1EB-1F1EF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1eb-1f1ef.png","sheet_x":34,"sheet_y":25,"short_name":"flag-fj","short_names":["flag-fj"],"text":null,"texts":null,"category":"Flags","sort_order":75,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS FK","unified":"1F1EB-1F1F0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1eb-1f1f0.png","sheet_x":34,"sheet_y":26,"short_name":"flag-fk","short_names":["flag-fk"],"text":null,"texts":null,"category":"Flags","sort_order":73,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS FM","unified":"1F1EB-1F1F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1eb-1f1f2.png","sheet_x":34,"sheet_y":27,"short_name":"flag-fm","short_names":["flag-fm"],"text":null,"texts":null,"category":"Flags","sort_order":144,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS FO","unified":"1F1EB-1F1F4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1eb-1f1f4.png","sheet_x":34,"sheet_y":28,"short_name":"flag-fo","short_names":["flag-fo"],"text":null,"texts":null,"category":"Flags","sort_order":74,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS FR","unified":"1F1EB-1F1F7","variations":[],"docomo":null,"au":"EAFA","softbank":"E50D","google":"FE4E7","image":"1f1eb-1f1f7.png","sheet_x":34,"sheet_y":29,"short_name":"flag-fr","short_names":["flag-fr","fr"],"text":null,"texts":null,"category":"Flags","sort_order":77,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GA","unified":"1F1EC-1F1E6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1e6.png","sheet_x":34,"sheet_y":30,"short_name":"flag-ga","short_names":["flag-ga"],"text":null,"texts":null,"category":"Flags","sort_order":81,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GB","unified":"1F1EC-1F1E7","variations":[],"docomo":null,"au":"EB10","softbank":"E510","google":"FE4EA","image":"1f1ec-1f1e7.png","sheet_x":34,"sheet_y":31,"short_name":"flag-gb","short_names":["flag-gb","gb","uk"],"text":null,"texts":null,"category":"Flags","sort_order":234,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GD","unified":"1F1EC-1F1E9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1e9.png","sheet_x":34,"sheet_y":32,"short_name":"flag-gd","short_names":["flag-gd"],"text":null,"texts":null,"category":"Flags","sort_order":89,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GE","unified":"1F1EC-1F1EA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1ea.png","sheet_x":34,"sheet_y":33,"short_name":"flag-ge","short_names":["flag-ge"],"text":null,"texts":null,"category":"Flags","sort_order":83,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GF","unified":"1F1EC-1F1EB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1eb.png","sheet_x":34,"sheet_y":34,"short_name":"flag-gf","short_names":["flag-gf"],"text":null,"texts":null,"category":"Flags","sort_order":78,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GG","unified":"1F1EC-1F1EC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1ec.png","sheet_x":34,"sheet_y":35,"short_name":"flag-gg","short_names":["flag-gg"],"text":null,"texts":null,"category":"Flags","sort_order":93,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GH","unified":"1F1EC-1F1ED","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1ed.png","sheet_x":34,"sheet_y":36,"short_name":"flag-gh","short_names":["flag-gh"],"text":null,"texts":null,"category":"Flags","sort_order":85,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GI","unified":"1F1EC-1F1EE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1ee.png","sheet_x":34,"sheet_y":37,"short_name":"flag-gi","short_names":["flag-gi"],"text":null,"texts":null,"category":"Flags","sort_order":86,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GL","unified":"1F1EC-1F1F1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1f1.png","sheet_x":34,"sheet_y":38,"short_name":"flag-gl","short_names":["flag-gl"],"text":null,"texts":null,"category":"Flags","sort_order":88,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GM","unified":"1F1EC-1F1F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1f2.png","sheet_x":34,"sheet_y":39,"short_name":"flag-gm","short_names":["flag-gm"],"text":null,"texts":null,"category":"Flags","sort_order":82,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GN","unified":"1F1EC-1F1F3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1f3.png","sheet_x":34,"sheet_y":40,"short_name":"flag-gn","short_names":["flag-gn"],"text":null,"texts":null,"category":"Flags","sort_order":94,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GP","unified":"1F1EC-1F1F5","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1f5.png","sheet_x":35,"sheet_y":0,"short_name":"flag-gp","short_names":["flag-gp"],"text":null,"texts":null,"category":"Flags","sort_order":90,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GQ","unified":"1F1EC-1F1F6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1f6.png","sheet_x":35,"sheet_y":1,"short_name":"flag-gq","short_names":["flag-gq"],"text":null,"texts":null,"category":"Flags","sort_order":68,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GR","unified":"1F1EC-1F1F7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1f7.png","sheet_x":35,"sheet_y":2,"short_name":"flag-gr","short_names":["flag-gr"],"text":null,"texts":null,"category":"Flags","sort_order":87,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GS","unified":"1F1EC-1F1F8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1f8.png","sheet_x":35,"sheet_y":3,"short_name":"flag-gs","short_names":["flag-gs"],"text":null,"texts":null,"category":"Flags","sort_order":206,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GT","unified":"1F1EC-1F1F9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1f9.png","sheet_x":35,"sheet_y":4,"short_name":"flag-gt","short_names":["flag-gt"],"text":null,"texts":null,"category":"Flags","sort_order":92,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GU","unified":"1F1EC-1F1FA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1fa.png","sheet_x":35,"sheet_y":5,"short_name":"flag-gu","short_names":["flag-gu"],"text":null,"texts":null,"category":"Flags","sort_order":91,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GW","unified":"1F1EC-1F1FC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1fc.png","sheet_x":35,"sheet_y":6,"short_name":"flag-gw","short_names":["flag-gw"],"text":null,"texts":null,"category":"Flags","sort_order":95,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS GY","unified":"1F1EC-1F1FE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ec-1f1fe.png","sheet_x":35,"sheet_y":7,"short_name":"flag-gy","short_names":["flag-gy"],"text":null,"texts":null,"category":"Flags","sort_order":96,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS HK","unified":"1F1ED-1F1F0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ed-1f1f0.png","sheet_x":35,"sheet_y":8,"short_name":"flag-hk","short_names":["flag-hk"],"text":null,"texts":null,"category":"Flags","sort_order":99,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS HM","unified":"1F1ED-1F1F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ed-1f1f2.png","sheet_x":35,"sheet_y":9,"short_name":"flag-hm","short_names":["flag-hm"],"text":null,"texts":null,"category":null,"sort_order":null,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS HN","unified":"1F1ED-1F1F3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ed-1f1f3.png","sheet_x":35,"sheet_y":10,"short_name":"flag-hn","short_names":["flag-hn"],"text":null,"texts":null,"category":"Flags","sort_order":98,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS HR","unified":"1F1ED-1F1F7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ed-1f1f7.png","sheet_x":35,"sheet_y":11,"short_name":"flag-hr","short_names":["flag-hr"],"text":null,"texts":null,"category":"Flags","sort_order":56,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS HT","unified":"1F1ED-1F1F9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ed-1f1f9.png","sheet_x":35,"sheet_y":12,"short_name":"flag-ht","short_names":["flag-ht"],"text":null,"texts":null,"category":"Flags","sort_order":97,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS HU","unified":"1F1ED-1F1FA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ed-1f1fa.png","sheet_x":35,"sheet_y":13,"short_name":"flag-hu","short_names":["flag-hu"],"text":null,"texts":null,"category":"Flags","sort_order":100,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS IC","unified":"1F1EE-1F1E8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1e8.png","sheet_x":35,"sheet_y":14,"short_name":"flag-ic","short_names":["flag-ic"],"text":null,"texts":null,"category":"Flags","sort_order":42,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS ID","unified":"1F1EE-1F1E9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1e9.png","sheet_x":35,"sheet_y":15,"short_name":"flag-id","short_names":["flag-id"],"text":null,"texts":null,"category":"Flags","sort_order":103,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS IE","unified":"1F1EE-1F1EA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1ea.png","sheet_x":35,"sheet_y":16,"short_name":"flag-ie","short_names":["flag-ie"],"text":null,"texts":null,"category":"Flags","sort_order":106,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS IL","unified":"1F1EE-1F1F1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1f1.png","sheet_x":35,"sheet_y":17,"short_name":"flag-il","short_names":["flag-il"],"text":null,"texts":null,"category":"Flags","sort_order":108,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS IM","unified":"1F1EE-1F1F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1f2.png","sheet_x":35,"sheet_y":18,"short_name":"flag-im","short_names":["flag-im"],"text":null,"texts":null,"category":"Flags","sort_order":107,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS IN","unified":"1F1EE-1F1F3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1f3.png","sheet_x":35,"sheet_y":19,"short_name":"flag-in","short_names":["flag-in"],"text":null,"texts":null,"category":"Flags","sort_order":102,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS IO","unified":"1F1EE-1F1F4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1f4.png","sheet_x":35,"sheet_y":20,"short_name":"flag-io","short_names":["flag-io"],"text":null,"texts":null,"category":"Flags","sort_order":32,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS IQ","unified":"1F1EE-1F1F6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1f6.png","sheet_x":35,"sheet_y":21,"short_name":"flag-iq","short_names":["flag-iq"],"text":null,"texts":null,"category":"Flags","sort_order":105,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS IR","unified":"1F1EE-1F1F7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1f7.png","sheet_x":35,"sheet_y":22,"short_name":"flag-ir","short_names":["flag-ir"],"text":null,"texts":null,"category":"Flags","sort_order":104,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS IS","unified":"1F1EE-1F1F8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ee-1f1f8.png","sheet_x":35,"sheet_y":23,"short_name":"flag-is","short_names":["flag-is"],"text":null,"texts":null,"category":"Flags","sort_order":101,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS IT","unified":"1F1EE-1F1F9","variations":[],"docomo":null,"au":"EB0F","softbank":"E50F","google":"FE4E9","image":"1f1ee-1f1f9.png","sheet_x":35,"sheet_y":24,"short_name":"flag-it","short_names":["flag-it","it"],"text":null,"texts":null,"category":"Flags","sort_order":109,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS JE","unified":"1F1EF-1F1EA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ef-1f1ea.png","sheet_x":35,"sheet_y":25,"short_name":"flag-je","short_names":["flag-je"],"text":null,"texts":null,"category":"Flags","sort_order":113,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS JM","unified":"1F1EF-1F1F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ef-1f1f2.png","sheet_x":35,"sheet_y":26,"short_name":"flag-jm","short_names":["flag-jm"],"text":null,"texts":null,"category":"Flags","sort_order":111,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS JO","unified":"1F1EF-1F1F4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ef-1f1f4.png","sheet_x":35,"sheet_y":27,"short_name":"flag-jo","short_names":["flag-jo"],"text":null,"texts":null,"category":"Flags","sort_order":114,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS JP","unified":"1F1EF-1F1F5","variations":[],"docomo":null,"au":"E4CC","softbank":"E50B","google":"FE4E5","image":"1f1ef-1f1f5.png","sheet_x":35,"sheet_y":28,"short_name":"flag-jp","short_names":["flag-jp","jp"],"text":null,"texts":null,"category":"Flags","sort_order":112,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS KE","unified":"1F1F0-1F1EA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1ea.png","sheet_x":35,"sheet_y":29,"short_name":"flag-ke","short_names":["flag-ke"],"text":null,"texts":null,"category":"Flags","sort_order":116,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS KG","unified":"1F1F0-1F1EC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1ec.png","sheet_x":35,"sheet_y":30,"short_name":"flag-kg","short_names":["flag-kg"],"text":null,"texts":null,"category":"Flags","sort_order":120,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS KH","unified":"1F1F0-1F1ED","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1ed.png","sheet_x":35,"sheet_y":31,"short_name":"flag-kh","short_names":["flag-kh"],"text":null,"texts":null,"category":"Flags","sort_order":39,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS KI","unified":"1F1F0-1F1EE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1ee.png","sheet_x":35,"sheet_y":32,"short_name":"flag-ki","short_names":["flag-ki"],"text":null,"texts":null,"category":"Flags","sort_order":117,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS KM","unified":"1F1F0-1F1F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1f2.png","sheet_x":35,"sheet_y":33,"short_name":"flag-km","short_names":["flag-km"],"text":null,"texts":null,"category":"Flags","sort_order":51,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS KN","unified":"1F1F0-1F1F3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1f3.png","sheet_x":35,"sheet_y":34,"short_name":"flag-kn","short_names":["flag-kn"],"text":null,"texts":null,"category":"Flags","sort_order":187,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS KP","unified":"1F1F0-1F1F5","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1f5.png","sheet_x":35,"sheet_y":35,"short_name":"flag-kp","short_names":["flag-kp"],"text":null,"texts":null,"category":"Flags","sort_order":165,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS KR","unified":"1F1F0-1F1F7","variations":[],"docomo":null,"au":"EB12","softbank":"E514","google":"FE4EE","image":"1f1f0-1f1f7.png","sheet_x":35,"sheet_y":36,"short_name":"flag-kr","short_names":["flag-kr","kr"],"text":null,"texts":null,"category":"Flags","sort_order":207,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS KW","unified":"1F1F0-1F1FC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1fc.png","sheet_x":35,"sheet_y":37,"short_name":"flag-kw","short_names":["flag-kw"],"text":null,"texts":null,"category":"Flags","sort_order":119,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS KY","unified":"1F1F0-1F1FE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1fe.png","sheet_x":35,"sheet_y":38,"short_name":"flag-ky","short_names":["flag-ky"],"text":null,"texts":null,"category":"Flags","sort_order":43,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS KZ","unified":"1F1F0-1F1FF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f0-1f1ff.png","sheet_x":35,"sheet_y":39,"short_name":"flag-kz","short_names":["flag-kz"],"text":null,"texts":null,"category":"Flags","sort_order":115,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS LA","unified":"1F1F1-1F1E6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1e6.png","sheet_x":35,"sheet_y":40,"short_name":"flag-la","short_names":["flag-la"],"text":null,"texts":null,"category":"Flags","sort_order":121,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS LB","unified":"1F1F1-1F1E7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1e7.png","sheet_x":36,"sheet_y":0,"short_name":"flag-lb","short_names":["flag-lb"],"text":null,"texts":null,"category":"Flags","sort_order":123,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS LC","unified":"1F1F1-1F1E8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1e8.png","sheet_x":36,"sheet_y":1,"short_name":"flag-lc","short_names":["flag-lc"],"text":null,"texts":null,"category":"Flags","sort_order":188,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS LI","unified":"1F1F1-1F1EE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1ee.png","sheet_x":36,"sheet_y":2,"short_name":"flag-li","short_names":["flag-li"],"text":null,"texts":null,"category":"Flags","sort_order":127,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS LK","unified":"1F1F1-1F1F0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1f0.png","sheet_x":36,"sheet_y":3,"short_name":"flag-lk","short_names":["flag-lk"],"text":null,"texts":null,"category":"Flags","sort_order":210,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS LR","unified":"1F1F1-1F1F7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1f7.png","sheet_x":36,"sheet_y":4,"short_name":"flag-lr","short_names":["flag-lr"],"text":null,"texts":null,"category":"Flags","sort_order":125,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS LS","unified":"1F1F1-1F1F8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1f8.png","sheet_x":36,"sheet_y":5,"short_name":"flag-ls","short_names":["flag-ls"],"text":null,"texts":null,"category":"Flags","sort_order":124,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS LT","unified":"1F1F1-1F1F9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1f9.png","sheet_x":36,"sheet_y":6,"short_name":"flag-lt","short_names":["flag-lt"],"text":null,"texts":null,"category":"Flags","sort_order":128,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS LU","unified":"1F1F1-1F1FA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1fa.png","sheet_x":36,"sheet_y":7,"short_name":"flag-lu","short_names":["flag-lu"],"text":null,"texts":null,"category":"Flags","sort_order":129,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS LV","unified":"1F1F1-1F1FB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1fb.png","sheet_x":36,"sheet_y":8,"short_name":"flag-lv","short_names":["flag-lv"],"text":null,"texts":null,"category":"Flags","sort_order":122,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS LY","unified":"1F1F1-1F1FE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f1-1f1fe.png","sheet_x":36,"sheet_y":9,"short_name":"flag-ly","short_names":["flag-ly"],"text":null,"texts":null,"category":"Flags","sort_order":126,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MA","unified":"1F1F2-1F1E6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1e6.png","sheet_x":36,"sheet_y":10,"short_name":"flag-ma","short_names":["flag-ma"],"text":null,"texts":null,"category":"Flags","sort_order":150,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MC","unified":"1F1F2-1F1E8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1e8.png","sheet_x":36,"sheet_y":11,"short_name":"flag-mc","short_names":["flag-mc"],"text":null,"texts":null,"category":"Flags","sort_order":146,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MD","unified":"1F1F2-1F1E9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1e9.png","sheet_x":36,"sheet_y":12,"short_name":"flag-md","short_names":["flag-md"],"text":null,"texts":null,"category":"Flags","sort_order":145,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS ME","unified":"1F1F2-1F1EA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1ea.png","sheet_x":36,"sheet_y":13,"short_name":"flag-me","short_names":["flag-me"],"text":null,"texts":null,"category":"Flags","sort_order":148,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MF","unified":"1F1F2-1F1EB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1eb.png","sheet_x":36,"sheet_y":14,"short_name":"flag-mf","short_names":["flag-mf"],"text":null,"texts":null,"category":null,"sort_order":null,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MG","unified":"1F1F2-1F1EC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1ec.png","sheet_x":36,"sheet_y":15,"short_name":"flag-mg","short_names":["flag-mg"],"text":null,"texts":null,"category":"Flags","sort_order":132,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MH","unified":"1F1F2-1F1ED","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1ed.png","sheet_x":36,"sheet_y":16,"short_name":"flag-mh","short_names":["flag-mh"],"text":null,"texts":null,"category":"Flags","sort_order":138,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MK","unified":"1F1F2-1F1F0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f0.png","sheet_x":36,"sheet_y":17,"short_name":"flag-mk","short_names":["flag-mk"],"text":null,"texts":null,"category":"Flags","sort_order":131,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS ML","unified":"1F1F2-1F1F1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f1.png","sheet_x":36,"sheet_y":18,"short_name":"flag-ml","short_names":["flag-ml"],"text":null,"texts":null,"category":"Flags","sort_order":136,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MM","unified":"1F1F2-1F1F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f2.png","sheet_x":36,"sheet_y":19,"short_name":"flag-mm","short_names":["flag-mm"],"text":null,"texts":null,"category":"Flags","sort_order":152,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MN","unified":"1F1F2-1F1F3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f3.png","sheet_x":36,"sheet_y":20,"short_name":"flag-mn","short_names":["flag-mn"],"text":null,"texts":null,"category":"Flags","sort_order":147,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MO","unified":"1F1F2-1F1F4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f4.png","sheet_x":36,"sheet_y":21,"short_name":"flag-mo","short_names":["flag-mo"],"text":null,"texts":null,"category":"Flags","sort_order":130,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MP","unified":"1F1F2-1F1F5","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f5.png","sheet_x":36,"sheet_y":22,"short_name":"flag-mp","short_names":["flag-mp"],"text":null,"texts":null,"category":"Flags","sort_order":164,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MQ","unified":"1F1F2-1F1F6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f6.png","sheet_x":36,"sheet_y":23,"short_name":"flag-mq","short_names":["flag-mq"],"text":null,"texts":null,"category":"Flags","sort_order":139,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MR","unified":"1F1F2-1F1F7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f7.png","sheet_x":36,"sheet_y":24,"short_name":"flag-mr","short_names":["flag-mr"],"text":null,"texts":null,"category":"Flags","sort_order":140,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MS","unified":"1F1F2-1F1F8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f8.png","sheet_x":36,"sheet_y":25,"short_name":"flag-ms","short_names":["flag-ms"],"text":null,"texts":null,"category":"Flags","sort_order":149,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MT","unified":"1F1F2-1F1F9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1f9.png","sheet_x":36,"sheet_y":26,"short_name":"flag-mt","short_names":["flag-mt"],"text":null,"texts":null,"category":"Flags","sort_order":137,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MU","unified":"1F1F2-1F1FA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1fa.png","sheet_x":36,"sheet_y":27,"short_name":"flag-mu","short_names":["flag-mu"],"text":null,"texts":null,"category":"Flags","sort_order":141,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MV","unified":"1F1F2-1F1FB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1fb.png","sheet_x":36,"sheet_y":28,"short_name":"flag-mv","short_names":["flag-mv"],"text":null,"texts":null,"category":"Flags","sort_order":135,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MW","unified":"1F1F2-1F1FC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1fc.png","sheet_x":36,"sheet_y":29,"short_name":"flag-mw","short_names":["flag-mw"],"text":null,"texts":null,"category":"Flags","sort_order":133,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MX","unified":"1F1F2-1F1FD","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1fd.png","sheet_x":36,"sheet_y":30,"short_name":"flag-mx","short_names":["flag-mx"],"text":null,"texts":null,"category":"Flags","sort_order":143,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MY","unified":"1F1F2-1F1FE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1fe.png","sheet_x":36,"sheet_y":31,"short_name":"flag-my","short_names":["flag-my"],"text":null,"texts":null,"category":"Flags","sort_order":134,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS MZ","unified":"1F1F2-1F1FF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f2-1f1ff.png","sheet_x":36,"sheet_y":32,"short_name":"flag-mz","short_names":["flag-mz"],"text":null,"texts":null,"category":"Flags","sort_order":151,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS NA","unified":"1F1F3-1F1E6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1e6.png","sheet_x":36,"sheet_y":33,"short_name":"flag-na","short_names":["flag-na"],"text":null,"texts":null,"category":"Flags","sort_order":153,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS NC","unified":"1F1F3-1F1E8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1e8.png","sheet_x":36,"sheet_y":34,"short_name":"flag-nc","short_names":["flag-nc"],"text":null,"texts":null,"category":"Flags","sort_order":157,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS NE","unified":"1F1F3-1F1EA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1ea.png","sheet_x":36,"sheet_y":35,"short_name":"flag-ne","short_names":["flag-ne"],"text":null,"texts":null,"category":"Flags","sort_order":160,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS NF","unified":"1F1F3-1F1EB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1eb.png","sheet_x":36,"sheet_y":36,"short_name":"flag-nf","short_names":["flag-nf"],"text":null,"texts":null,"category":"Flags","sort_order":163,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS NG","unified":"1F1F3-1F1EC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1ec.png","sheet_x":36,"sheet_y":37,"short_name":"flag-ng","short_names":["flag-ng"],"text":null,"texts":null,"category":"Flags","sort_order":161,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS NI","unified":"1F1F3-1F1EE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1ee.png","sheet_x":36,"sheet_y":38,"short_name":"flag-ni","short_names":["flag-ni"],"text":null,"texts":null,"category":"Flags","sort_order":159,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS NL","unified":"1F1F3-1F1F1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1f1.png","sheet_x":36,"sheet_y":39,"short_name":"flag-nl","short_names":["flag-nl"],"text":null,"texts":null,"category":"Flags","sort_order":156,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS NO","unified":"1F1F3-1F1F4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1f4.png","sheet_x":36,"sheet_y":40,"short_name":"flag-no","short_names":["flag-no"],"text":null,"texts":null,"category":"Flags","sort_order":166,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS NP","unified":"1F1F3-1F1F5","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1f5.png","sheet_x":37,"sheet_y":0,"short_name":"flag-np","short_names":["flag-np"],"text":null,"texts":null,"category":"Flags","sort_order":155,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS NR","unified":"1F1F3-1F1F7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1f7.png","sheet_x":37,"sheet_y":1,"short_name":"flag-nr","short_names":["flag-nr"],"text":null,"texts":null,"category":"Flags","sort_order":154,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS NU","unified":"1F1F3-1F1FA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1fa.png","sheet_x":37,"sheet_y":2,"short_name":"flag-nu","short_names":["flag-nu"],"text":null,"texts":null,"category":"Flags","sort_order":162,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS NZ","unified":"1F1F3-1F1FF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f3-1f1ff.png","sheet_x":37,"sheet_y":3,"short_name":"flag-nz","short_names":["flag-nz"],"text":null,"texts":null,"category":"Flags","sort_order":158,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS OM","unified":"1F1F4-1F1F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f4-1f1f2.png","sheet_x":37,"sheet_y":4,"short_name":"flag-om","short_names":["flag-om"],"text":null,"texts":null,"category":"Flags","sort_order":167,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS PA","unified":"1F1F5-1F1E6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1e6.png","sheet_x":37,"sheet_y":5,"short_name":"flag-pa","short_names":["flag-pa"],"text":null,"texts":null,"category":"Flags","sort_order":171,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS PE","unified":"1F1F5-1F1EA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1ea.png","sheet_x":37,"sheet_y":6,"short_name":"flag-pe","short_names":["flag-pe"],"text":null,"texts":null,"category":"Flags","sort_order":174,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS PF","unified":"1F1F5-1F1EB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1eb.png","sheet_x":37,"sheet_y":7,"short_name":"flag-pf","short_names":["flag-pf"],"text":null,"texts":null,"category":"Flags","sort_order":79,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS PG","unified":"1F1F5-1F1EC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1ec.png","sheet_x":37,"sheet_y":8,"short_name":"flag-pg","short_names":["flag-pg"],"text":null,"texts":null,"category":"Flags","sort_order":172,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS PH","unified":"1F1F5-1F1ED","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1ed.png","sheet_x":37,"sheet_y":9,"short_name":"flag-ph","short_names":["flag-ph"],"text":null,"texts":null,"category":"Flags","sort_order":175,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS PK","unified":"1F1F5-1F1F0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1f0.png","sheet_x":37,"sheet_y":10,"short_name":"flag-pk","short_names":["flag-pk"],"text":null,"texts":null,"category":"Flags","sort_order":168,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS PL","unified":"1F1F5-1F1F1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1f1.png","sheet_x":37,"sheet_y":11,"short_name":"flag-pl","short_names":["flag-pl"],"text":null,"texts":null,"category":"Flags","sort_order":177,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS PM","unified":"1F1F5-1F1F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1f2.png","sheet_x":37,"sheet_y":12,"short_name":"flag-pm","short_names":["flag-pm"],"text":null,"texts":null,"category":"Flags","sort_order":189,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS PN","unified":"1F1F5-1F1F3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1f3.png","sheet_x":37,"sheet_y":13,"short_name":"flag-pn","short_names":["flag-pn"],"text":null,"texts":null,"category":"Flags","sort_order":176,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS PR","unified":"1F1F5-1F1F7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1f7.png","sheet_x":37,"sheet_y":14,"short_name":"flag-pr","short_names":["flag-pr"],"text":null,"texts":null,"category":"Flags","sort_order":179,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS PS","unified":"1F1F5-1F1F8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1f8.png","sheet_x":37,"sheet_y":15,"short_name":"flag-ps","short_names":["flag-ps"],"text":null,"texts":null,"category":"Flags","sort_order":170,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS PT","unified":"1F1F5-1F1F9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1f9.png","sheet_x":37,"sheet_y":16,"short_name":"flag-pt","short_names":["flag-pt"],"text":null,"texts":null,"category":"Flags","sort_order":178,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS PW","unified":"1F1F5-1F1FC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1fc.png","sheet_x":37,"sheet_y":17,"short_name":"flag-pw","short_names":["flag-pw"],"text":null,"texts":null,"category":"Flags","sort_order":169,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS PY","unified":"1F1F5-1F1FE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f5-1f1fe.png","sheet_x":37,"sheet_y":18,"short_name":"flag-py","short_names":["flag-py"],"text":null,"texts":null,"category":"Flags","sort_order":173,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS QA","unified":"1F1F6-1F1E6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f6-1f1e6.png","sheet_x":37,"sheet_y":19,"short_name":"flag-qa","short_names":["flag-qa"],"text":null,"texts":null,"category":"Flags","sort_order":180,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS RE","unified":"1F1F7-1F1EA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f7-1f1ea.png","sheet_x":37,"sheet_y":20,"short_name":"flag-re","short_names":["flag-re"],"text":null,"texts":null,"category":"Flags","sort_order":181,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS RO","unified":"1F1F7-1F1F4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f7-1f1f4.png","sheet_x":37,"sheet_y":21,"short_name":"flag-ro","short_names":["flag-ro"],"text":null,"texts":null,"category":"Flags","sort_order":182,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS RS","unified":"1F1F7-1F1F8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f7-1f1f8.png","sheet_x":37,"sheet_y":22,"short_name":"flag-rs","short_names":["flag-rs"],"text":null,"texts":null,"category":"Flags","sort_order":196,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS RU","unified":"1F1F7-1F1FA","variations":[],"docomo":null,"au":"E5D6","softbank":"E512","google":"FE4EC","image":"1f1f7-1f1fa.png","sheet_x":37,"sheet_y":23,"short_name":"flag-ru","short_names":["flag-ru","ru"],"text":null,"texts":null,"category":"Flags","sort_order":183,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS RW","unified":"1F1F7-1F1FC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f7-1f1fc.png","sheet_x":37,"sheet_y":24,"short_name":"flag-rw","short_names":["flag-rw"],"text":null,"texts":null,"category":"Flags","sort_order":184,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SA","unified":"1F1F8-1F1E6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1e6.png","sheet_x":37,"sheet_y":25,"short_name":"flag-sa","short_names":["flag-sa"],"text":null,"texts":null,"category":"Flags","sort_order":194,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SB","unified":"1F1F8-1F1E7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1e7.png","sheet_x":37,"sheet_y":26,"short_name":"flag-sb","short_names":["flag-sb"],"text":null,"texts":null,"category":"Flags","sort_order":203,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SC","unified":"1F1F8-1F1E8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1e8.png","sheet_x":37,"sheet_y":27,"short_name":"flag-sc","short_names":["flag-sc"],"text":null,"texts":null,"category":"Flags","sort_order":197,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SD","unified":"1F1F8-1F1E9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1e9.png","sheet_x":37,"sheet_y":28,"short_name":"flag-sd","short_names":["flag-sd"],"text":null,"texts":null,"category":"Flags","sort_order":211,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SE","unified":"1F1F8-1F1EA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1ea.png","sheet_x":37,"sheet_y":29,"short_name":"flag-se","short_names":["flag-se"],"text":null,"texts":null,"category":"Flags","sort_order":214,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SG","unified":"1F1F8-1F1EC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1ec.png","sheet_x":37,"sheet_y":30,"short_name":"flag-sg","short_names":["flag-sg"],"text":null,"texts":null,"category":"Flags","sort_order":199,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SH","unified":"1F1F8-1F1ED","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1ed.png","sheet_x":37,"sheet_y":31,"short_name":"flag-sh","short_names":["flag-sh"],"text":null,"texts":null,"category":"Flags","sort_order":186,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SI","unified":"1F1F8-1F1EE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1ee.png","sheet_x":37,"sheet_y":32,"short_name":"flag-si","short_names":["flag-si"],"text":null,"texts":null,"category":"Flags","sort_order":202,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SJ","unified":"1F1F8-1F1EF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1ef.png","sheet_x":37,"sheet_y":33,"short_name":"flag-sj","short_names":["flag-sj"],"text":null,"texts":null,"category":null,"sort_order":null,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SK","unified":"1F1F8-1F1F0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1f0.png","sheet_x":37,"sheet_y":34,"short_name":"flag-sk","short_names":["flag-sk"],"text":null,"texts":null,"category":"Flags","sort_order":201,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SL","unified":"1F1F8-1F1F1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1f1.png","sheet_x":37,"sheet_y":35,"short_name":"flag-sl","short_names":["flag-sl"],"text":null,"texts":null,"category":"Flags","sort_order":198,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SM","unified":"1F1F8-1F1F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1f2.png","sheet_x":37,"sheet_y":36,"short_name":"flag-sm","short_names":["flag-sm"],"text":null,"texts":null,"category":"Flags","sort_order":192,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SN","unified":"1F1F8-1F1F3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1f3.png","sheet_x":37,"sheet_y":37,"short_name":"flag-sn","short_names":["flag-sn"],"text":null,"texts":null,"category":"Flags","sort_order":195,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SO","unified":"1F1F8-1F1F4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1f4.png","sheet_x":37,"sheet_y":38,"short_name":"flag-so","short_names":["flag-so"],"text":null,"texts":null,"category":"Flags","sort_order":204,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SR","unified":"1F1F8-1F1F7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1f7.png","sheet_x":37,"sheet_y":39,"short_name":"flag-sr","short_names":["flag-sr"],"text":null,"texts":null,"category":"Flags","sort_order":212,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SS","unified":"1F1F8-1F1F8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1f8.png","sheet_x":37,"sheet_y":40,"short_name":"flag-ss","short_names":["flag-ss"],"text":null,"texts":null,"category":"Flags","sort_order":208,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS ST","unified":"1F1F8-1F1F9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1f9.png","sheet_x":38,"sheet_y":0,"short_name":"flag-st","short_names":["flag-st"],"text":null,"texts":null,"category":"Flags","sort_order":193,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SV","unified":"1F1F8-1F1FB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1fb.png","sheet_x":38,"sheet_y":1,"short_name":"flag-sv","short_names":["flag-sv"],"text":null,"texts":null,"category":"Flags","sort_order":67,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SX","unified":"1F1F8-1F1FD","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1fd.png","sheet_x":38,"sheet_y":2,"short_name":"flag-sx","short_names":["flag-sx"],"text":null,"texts":null,"category":"Flags","sort_order":200,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SY","unified":"1F1F8-1F1FE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1fe.png","sheet_x":38,"sheet_y":3,"short_name":"flag-sy","short_names":["flag-sy"],"text":null,"texts":null,"category":"Flags","sort_order":216,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS SZ","unified":"1F1F8-1F1FF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f8-1f1ff.png","sheet_x":38,"sheet_y":4,"short_name":"flag-sz","short_names":["flag-sz"],"text":null,"texts":null,"category":"Flags","sort_order":213,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS TA","unified":"1F1F9-1F1E6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1e6.png","sheet_x":38,"sheet_y":5,"short_name":"flag-ta","short_names":["flag-ta"],"text":null,"texts":null,"category":null,"sort_order":null,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS TC","unified":"1F1F9-1F1E8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1e8.png","sheet_x":38,"sheet_y":6,"short_name":"flag-tc","short_names":["flag-tc"],"text":null,"texts":null,"category":"Flags","sort_order":229,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS TD","unified":"1F1F9-1F1E9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1e9.png","sheet_x":38,"sheet_y":7,"short_name":"flag-td","short_names":["flag-td"],"text":null,"texts":null,"category":"Flags","sort_order":45,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS TF","unified":"1F1F9-1F1EB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1eb.png","sheet_x":38,"sheet_y":8,"short_name":"flag-tf","short_names":["flag-tf"],"text":null,"texts":null,"category":"Flags","sort_order":80,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS TG","unified":"1F1F9-1F1EC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1ec.png","sheet_x":38,"sheet_y":9,"short_name":"flag-tg","short_names":["flag-tg"],"text":null,"texts":null,"category":"Flags","sort_order":222,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS TH","unified":"1F1F9-1F1ED","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1ed.png","sheet_x":38,"sheet_y":10,"short_name":"flag-th","short_names":["flag-th"],"text":null,"texts":null,"category":"Flags","sort_order":220,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS TJ","unified":"1F1F9-1F1EF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1ef.png","sheet_x":38,"sheet_y":11,"short_name":"flag-tj","short_names":["flag-tj"],"text":null,"texts":null,"category":"Flags","sort_order":218,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS TK","unified":"1F1F9-1F1F0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1f0.png","sheet_x":38,"sheet_y":12,"short_name":"flag-tk","short_names":["flag-tk"],"text":null,"texts":null,"category":"Flags","sort_order":223,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS TL","unified":"1F1F9-1F1F1","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1f1.png","sheet_x":38,"sheet_y":13,"short_name":"flag-tl","short_names":["flag-tl"],"text":null,"texts":null,"category":"Flags","sort_order":221,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS TM","unified":"1F1F9-1F1F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1f2.png","sheet_x":38,"sheet_y":14,"short_name":"flag-tm","short_names":["flag-tm"],"text":null,"texts":null,"category":"Flags","sort_order":228,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS TN","unified":"1F1F9-1F1F3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1f3.png","sheet_x":38,"sheet_y":15,"short_name":"flag-tn","short_names":["flag-tn"],"text":null,"texts":null,"category":"Flags","sort_order":226,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS TO","unified":"1F1F9-1F1F4","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1f4.png","sheet_x":38,"sheet_y":16,"short_name":"flag-to","short_names":["flag-to"],"text":null,"texts":null,"category":"Flags","sort_order":224,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS TR","unified":"1F1F9-1F1F7","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1f7.png","sheet_x":38,"sheet_y":17,"short_name":"flag-tr","short_names":["flag-tr"],"text":null,"texts":null,"category":"Flags","sort_order":227,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS TT","unified":"1F1F9-1F1F9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1f9.png","sheet_x":38,"sheet_y":18,"short_name":"flag-tt","short_names":["flag-tt"],"text":null,"texts":null,"category":"Flags","sort_order":225,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS TV","unified":"1F1F9-1F1FB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1fb.png","sheet_x":38,"sheet_y":19,"short_name":"flag-tv","short_names":["flag-tv"],"text":null,"texts":null,"category":"Flags","sort_order":230,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS TW","unified":"1F1F9-1F1FC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1fc.png","sheet_x":38,"sheet_y":20,"short_name":"flag-tw","short_names":["flag-tw"],"text":null,"texts":null,"category":"Flags","sort_order":217,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS TZ","unified":"1F1F9-1F1FF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1f9-1f1ff.png","sheet_x":38,"sheet_y":21,"short_name":"flag-tz","short_names":["flag-tz"],"text":null,"texts":null,"category":"Flags","sort_order":219,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS UA","unified":"1F1FA-1F1E6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fa-1f1e6.png","sheet_x":38,"sheet_y":22,"short_name":"flag-ua","short_names":["flag-ua"],"text":null,"texts":null,"category":"Flags","sort_order":232,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS UG","unified":"1F1FA-1F1EC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fa-1f1ec.png","sheet_x":38,"sheet_y":23,"short_name":"flag-ug","short_names":["flag-ug"],"text":null,"texts":null,"category":"Flags","sort_order":231,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS UM","unified":"1F1FA-1F1F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fa-1f1f2.png","sheet_x":38,"sheet_y":24,"short_name":"flag-um","short_names":["flag-um"],"text":null,"texts":null,"category":null,"sort_order":null,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS US","unified":"1F1FA-1F1F8","variations":[],"docomo":null,"au":"E573","softbank":"E50C","google":"FE4E6","image":"1f1fa-1f1f8.png","sheet_x":38,"sheet_y":25,"short_name":"flag-us","short_names":["flag-us","us"],"text":null,"texts":null,"category":"Flags","sort_order":235,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS UY","unified":"1F1FA-1F1FE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fa-1f1fe.png","sheet_x":38,"sheet_y":26,"short_name":"flag-uy","short_names":["flag-uy"],"text":null,"texts":null,"category":"Flags","sort_order":237,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS UZ","unified":"1F1FA-1F1FF","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fa-1f1ff.png","sheet_x":38,"sheet_y":27,"short_name":"flag-uz","short_names":["flag-uz"],"text":null,"texts":null,"category":"Flags","sort_order":238,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS VA","unified":"1F1FB-1F1E6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fb-1f1e6.png","sheet_x":38,"sheet_y":28,"short_name":"flag-va","short_names":["flag-va"],"text":null,"texts":null,"category":"Flags","sort_order":240,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS VC","unified":"1F1FB-1F1E8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fb-1f1e8.png","sheet_x":38,"sheet_y":29,"short_name":"flag-vc","short_names":["flag-vc"],"text":null,"texts":null,"category":"Flags","sort_order":190,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS VE","unified":"1F1FB-1F1EA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fb-1f1ea.png","sheet_x":38,"sheet_y":30,"short_name":"flag-ve","short_names":["flag-ve"],"text":null,"texts":null,"category":"Flags","sort_order":241,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS VG","unified":"1F1FB-1F1EC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fb-1f1ec.png","sheet_x":38,"sheet_y":31,"short_name":"flag-vg","short_names":["flag-vg"],"text":null,"texts":null,"category":"Flags","sort_order":33,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS VI","unified":"1F1FB-1F1EE","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fb-1f1ee.png","sheet_x":38,"sheet_y":32,"short_name":"flag-vi","short_names":["flag-vi"],"text":null,"texts":null,"category":"Flags","sort_order":236,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS VN","unified":"1F1FB-1F1F3","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fb-1f1f3.png","sheet_x":38,"sheet_y":33,"short_name":"flag-vn","short_names":["flag-vn"],"text":null,"texts":null,"category":"Flags","sort_order":242,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS VU","unified":"1F1FB-1F1FA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fb-1f1fa.png","sheet_x":38,"sheet_y":34,"short_name":"flag-vu","short_names":["flag-vu"],"text":null,"texts":null,"category":"Flags","sort_order":239,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS WF","unified":"1F1FC-1F1EB","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fc-1f1eb.png","sheet_x":38,"sheet_y":35,"short_name":"flag-wf","short_names":["flag-wf"],"text":null,"texts":null,"category":"Flags","sort_order":243,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS WS","unified":"1F1FC-1F1F8","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fc-1f1f8.png","sheet_x":38,"sheet_y":36,"short_name":"flag-ws","short_names":["flag-ws"],"text":null,"texts":null,"category":"Flags","sort_order":191,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS XK","unified":"1F1FD-1F1F0","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fd-1f1f0.png","sheet_x":38,"sheet_y":37,"short_name":"flag-xk","short_names":["flag-xk"],"text":null,"texts":null,"category":"Flags","sort_order":118,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS YE","unified":"1F1FE-1F1EA","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fe-1f1ea.png","sheet_x":38,"sheet_y":38,"short_name":"flag-ye","short_names":["flag-ye"],"text":null,"texts":null,"category":"Flags","sort_order":245,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS YT","unified":"1F1FE-1F1F9","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1fe-1f1f9.png","sheet_x":38,"sheet_y":39,"short_name":"flag-yt","short_names":["flag-yt"],"text":null,"texts":null,"category":"Flags","sort_order":142,"has_img_apple":true,"has_img_google":false,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS ZA","unified":"1F1FF-1F1E6","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ff-1f1e6.png","sheet_x":38,"sheet_y":40,"short_name":"flag-za","short_names":["flag-za"],"text":null,"texts":null,"category":"Flags","sort_order":205,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS ZM","unified":"1F1FF-1F1F2","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ff-1f1f2.png","sheet_x":39,"sheet_y":0,"short_name":"flag-zm","short_names":["flag-zm"],"text":null,"texts":null,"category":"Flags","sort_order":246,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":"REGIONAL INDICATOR SYMBOL LETTERS ZW","unified":"1F1FF-1F1FC","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f1ff-1f1fc.png","sheet_x":39,"sheet_y":1,"short_name":"flag-zw","short_names":["flag-zw"],"text":null,"texts":null,"category":"Flags","sort_order":247,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":null,"unified":"1F468-200D-1F468-200D-1F466","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f468-200d-1f466.png","sheet_x":39,"sheet_y":2,"short_name":"man-man-boy","short_names":["man-man-boy"],"text":null,"texts":null,"category":"People","sort_order":171,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":null,"unified":"1F468-200D-1F468-200D-1F466-200D-1F466","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f468-200d-1f466-200d-1f466.png","sheet_x":39,"sheet_y":3,"short_name":"man-man-boy-boy","short_names":["man-man-boy-boy"],"text":null,"texts":null,"category":"People","sort_order":174,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":null,"unified":"1F468-200D-1F468-200D-1F467","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f468-200d-1f467.png","sheet_x":39,"sheet_y":4,"short_name":"man-man-girl","short_names":["man-man-girl"],"text":null,"texts":null,"category":"People","sort_order":172,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":null,"unified":"1F468-200D-1F468-200D-1F467-200D-1F466","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f468-200d-1f467-200d-1f466.png","sheet_x":39,"sheet_y":5,"short_name":"man-man-girl-boy","short_names":["man-man-girl-boy"],"text":null,"texts":null,"category":"People","sort_order":173,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":null,"unified":"1F468-200D-1F468-200D-1F467-200D-1F467","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f468-200d-1f467-200d-1f467.png","sheet_x":39,"sheet_y":6,"short_name":"man-man-girl-girl","short_names":["man-man-girl-girl"],"text":null,"texts":null,"category":"People","sort_order":175,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":null,"unified":"1F468-200D-1F469-200D-1F466-200D-1F466","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f469-200d-1f466-200d-1f466.png","sheet_x":39,"sheet_y":7,"short_name":"man-woman-boy-boy","short_names":["man-woman-boy-boy"],"text":null,"texts":null,"category":"People","sort_order":164,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":null,"unified":"1F468-200D-1F469-200D-1F467","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f469-200d-1f467.png","sheet_x":39,"sheet_y":8,"short_name":"man-woman-girl","short_names":["man-woman-girl"],"text":null,"texts":null,"category":"People","sort_order":162,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":null,"unified":"1F468-200D-1F469-200D-1F467-200D-1F466","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f469-200d-1f467-200d-1f466.png","sheet_x":39,"sheet_y":9,"short_name":"man-woman-girl-boy","short_names":["man-woman-girl-boy"],"text":null,"texts":null,"category":"People","sort_order":163,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":null,"unified":"1F468-200D-1F469-200D-1F467-200D-1F467","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-1f469-200d-1f467-200d-1f467.png","sheet_x":39,"sheet_y":10,"short_name":"man-woman-girl-girl","short_names":["man-woman-girl-girl"],"text":null,"texts":null,"category":"People","sort_order":165,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":null,"unified":"1F468-200D-2764-FE0F-200D-1F468","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-2764-fe0f-200d-1f468.png","sheet_x":39,"sheet_y":11,"short_name":"man-heart-man","short_names":["man-heart-man"],"text":null,"texts":null,"category":"People","sort_order":157,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":false},{"name":null,"unified":"1F468-200D-2764-FE0F-200D-1F48B-200D-1F468","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f468-200d-2764-fe0f-200d-1f48b-200d-1f468.png","sheet_x":39,"sheet_y":12,"short_name":"man-kiss-man","short_names":["man-kiss-man"],"text":null,"texts":null,"category":"People","sort_order":160,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":false},{"name":null,"unified":"1F469-200D-1F469-200D-1F466","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f469-200d-1f466.png","sheet_x":39,"sheet_y":13,"short_name":"woman-woman-boy","short_names":["woman-woman-boy"],"text":null,"texts":null,"category":"People","sort_order":166,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":null,"unified":"1F469-200D-1F469-200D-1F466-200D-1F466","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f469-200d-1f466-200d-1f466.png","sheet_x":39,"sheet_y":14,"short_name":"woman-woman-boy-boy","short_names":["woman-woman-boy-boy"],"text":null,"texts":null,"category":"People","sort_order":169,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":null,"unified":"1F469-200D-1F469-200D-1F467","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f469-200d-1f467.png","sheet_x":39,"sheet_y":15,"short_name":"woman-woman-girl","short_names":["woman-woman-girl"],"text":null,"texts":null,"category":"People","sort_order":167,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":null,"unified":"1F469-200D-1F469-200D-1F467-200D-1F466","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f469-200d-1f467-200d-1f466.png","sheet_x":39,"sheet_y":16,"short_name":"woman-woman-girl-boy","short_names":["woman-woman-girl-boy"],"text":null,"texts":null,"category":"People","sort_order":168,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":null,"unified":"1F469-200D-1F469-200D-1F467-200D-1F467","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-1f469-200d-1f467-200d-1f467.png","sheet_x":39,"sheet_y":17,"short_name":"woman-woman-girl-girl","short_names":["woman-woman-girl-girl"],"text":null,"texts":null,"category":"People","sort_order":170,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":true},{"name":null,"unified":"1F469-200D-2764-FE0F-200D-1F469","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-2764-fe0f-200d-1f469.png","sheet_x":39,"sheet_y":18,"short_name":"woman-heart-woman","short_names":["woman-heart-woman"],"text":null,"texts":null,"category":"People","sort_order":156,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":false},{"name":null,"unified":"1F469-200D-2764-FE0F-200D-1F48B-200D-1F469","variations":[],"docomo":null,"au":null,"softbank":null,"google":null,"image":"1f469-200d-2764-fe0f-200d-1f48b-200d-1f469.png","sheet_x":39,"sheet_y":19,"short_name":"woman-kiss-woman","short_names":["woman-kiss-woman"],"text":null,"texts":null,"category":"People","sort_order":159,"has_img_apple":true,"has_img_google":true,"has_img_twitter":true,"has_img_emojione":false}]} diff --git a/packages/client-app/internal_packages/composer-emoji/lib/emoji-message-extension.jsx b/packages/client-app/internal_packages/composer-emoji/lib/emoji-message-extension.jsx new file mode 100644 index 0000000000..4e260aeaf8 --- /dev/null +++ b/packages/client-app/internal_packages/composer-emoji/lib/emoji-message-extension.jsx @@ -0,0 +1,44 @@ +/* eslint no-cond-assign:0 */ +import {MessageViewExtension, RegExpUtils} from 'nylas-exports'; +import emoji from 'node-emoji'; + +import EmojiStore from './emoji-store'; + +function makeIntoEmojiTag(nodeArg, emojiName) { + const node = nodeArg; + node.src = EmojiStore.getImagePath(emojiName); + node.className = `emoji ${emojiName}`; + node.width = 14; + node.height = 14; + node.style = ''; + node.style.marginTop = '-5px'; +} + +class EmojiMessageExtension extends MessageViewExtension { + static renderedMessageBodyIntoDocument({document}) { + const emojiRegex = RegExpUtils.emojiRegex(); + + // Look for emoji in the content of text nodes + const treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT); + + while (treeWalker.nextNode()) { + emojiRegex.lastIndex = 0; + + const node = treeWalker.currentNode; + let match = null; + + while (match = emojiRegex.exec(node.textContent)) { + const matchEmojiName = emoji.which(match[0]); + if (matchEmojiName) { + const matchNode = (match.index === 0) ? node : node.splitText(match.index); + matchNode.splitText(match[0].length); + const imageNode = document.createElement('img'); + makeIntoEmojiTag(imageNode, matchEmojiName); + matchNode.parentNode.replaceChild(imageNode, matchNode); + } + } + } + } +} + +export default EmojiMessageExtension; diff --git a/packages/client-app/internal_packages/composer-emoji/lib/emoji-picker.jsx b/packages/client-app/internal_packages/composer-emoji/lib/emoji-picker.jsx new file mode 100644 index 0000000000..b085cd350f --- /dev/null +++ b/packages/client-app/internal_packages/composer-emoji/lib/emoji-picker.jsx @@ -0,0 +1,68 @@ +import {React, ReactDOM} from 'nylas-exports'; +import emoji from 'node-emoji'; + +import EmojiStore from './emoji-store'; +import EmojiActions from './emoji-actions'; + + +class EmojiPicker extends React.Component { + static displayName = "EmojiPicker"; + static propTypes = { + emojiOptions: React.PropTypes.array, + selectedEmoji: React.PropTypes.string, + }; + + constructor(props) { + super(props); + this.state = {}; + } + + componentDidUpdate() { + const selectedButton = ReactDOM.findDOMNode(this).querySelector(".emoji-option"); + if (selectedButton) { + selectedButton.scrollIntoViewIfNeeded(); + } + } + + onMouseDown(emojiName) { + EmojiActions.selectEmoji({emojiName, replaceSelection: true}); + } + + render() { + const emojiButtons = []; + let emojiIndex = this.props.emojiOptions.indexOf(this.props.selectedEmoji); + if (emojiIndex === -1) emojiIndex = 0; + if (this.props.emojiOptions) { + this.props.emojiOptions.forEach((emojiOption, i) => { + const emojiClass = emojiIndex === i ? "btn btn-icon emoji-option" : "btn btn-icon"; + let emojiChar = emoji.get(emojiOption); + emojiChar = ( + {emojiOption} + ); + emojiButtons.push( + + ); + emojiButtons.push(
); + }); + } + return ( +
+ {emojiButtons} +
+ ); + } +} + +export default EmojiPicker; diff --git a/packages/client-app/internal_packages/composer-emoji/lib/emoji-store.jsx b/packages/client-app/internal_packages/composer-emoji/lib/emoji-store.jsx new file mode 100644 index 0000000000..314e80f637 --- /dev/null +++ b/packages/client-app/internal_packages/composer-emoji/lib/emoji-store.jsx @@ -0,0 +1,82 @@ +/* eslint global-require: "off" */ + +import NylasStore from 'nylas-store'; +import _ from 'underscore'; + +import {Rx, DatabaseStore} from 'nylas-exports'; +import EmojiActions from './emoji-actions'; + +const EmojiJSONBlobKey = 'emoji'; + +let emojiData; + +class EmojiStore extends NylasStore { + constructor(props) { + super(props); + this._emoji = []; + } + + activate = () => { + const query = DatabaseStore.findJSONBlob(EmojiJSONBlobKey); + this._subscription = Rx.Observable.fromQuery(query).subscribe((emoji) => { + this._emoji = emoji || []; + this.trigger(); + }); + this.listenTo(EmojiActions.useEmoji, this._onUseEmoji); + } + + frequentlyUsedEmoji = () => { + const sortedEmoji = this._emoji; + sortedEmoji.sort((a, b) => { + if (a.frequency < b.frequency) return 1; + return (b.frequency < a.frequency) ? -1 : 0; + }); + const sortedEmojiNames = []; + for (const emoji of sortedEmoji) { + sortedEmojiNames.push(emoji.emojiName); + } + if (sortedEmojiNames.length > 32) { + return sortedEmojiNames.slice(0, 32); + } + return sortedEmojiNames; + } + + getImagePath(emojiName) { + emojiData = emojiData || require('./emoji-data').emojiData + for (const emoji of emojiData) { + if (emoji.short_names.indexOf(emojiName) !== -1) { + if (process.platform === "darwin") { + return `images/composer-emoji/apple/${emoji.image}`; + } + return `images/composer-emoji/twitter/${emoji.image}`; + } + } + return '' + } + + _onUseEmoji = (emoji) => { + const savedEmoji = _.find(this._emoji, (curEmoji) => { + return curEmoji.emojiChar === emoji.emojiChar; + }); + if (savedEmoji) { + for (const key of Object.keys(emoji)) { + savedEmoji[key] = emoji[key]; + } + savedEmoji.frequency++; + } else { + _.extend(emoji, {frequency: 1}); + this._emoji.push(emoji); + } + this._saveEmoji(); + this.trigger(); + } + + _saveEmoji = () => { + DatabaseStore.inTransaction((t) => { + return t.persistJSONBlob(EmojiJSONBlobKey, this._emoji); + }); + } + +} + +export default new EmojiStore(); diff --git a/packages/client-app/internal_packages/composer-emoji/lib/main.es6 b/packages/client-app/internal_packages/composer-emoji/lib/main.es6 new file mode 100644 index 0000000000..657ee3def1 --- /dev/null +++ b/packages/client-app/internal_packages/composer-emoji/lib/main.es6 @@ -0,0 +1,18 @@ +import {ExtensionRegistry, ComponentRegistry} from 'nylas-exports'; +import EmojiStore from './emoji-store'; +import EmojiComposerExtension from './emoji-composer-extension'; +import EmojiMessageExtension from './emoji-message-extension'; +import EmojiButton from './emoji-button'; + +export function activate() { + ExtensionRegistry.Composer.register(EmojiComposerExtension); + ExtensionRegistry.MessageView.register(EmojiMessageExtension); + ComponentRegistry.register(EmojiButton, {role: 'Composer:ActionButton'}); + EmojiStore.activate(); +} + +export function deactivate() { + ExtensionRegistry.Composer.unregister(EmojiComposerExtension); + ExtensionRegistry.MessageView.unregister(EmojiMessageExtension); + ComponentRegistry.unregister(EmojiButton); +} diff --git a/packages/client-app/internal_packages/composer-emoji/package.json b/packages/client-app/internal_packages/composer-emoji/package.json new file mode 100644 index 0000000000..a44ae0fba2 --- /dev/null +++ b/packages/client-app/internal_packages/composer-emoji/package.json @@ -0,0 +1,24 @@ +{ + "name": "composer-emoji", + "main": "./lib/main", + "version": "0.1.0", + "repository": { + "type": "git", + "url": "" + }, + "engines": { + "nylas": "*" + }, + + "isOptional": true, + + "title": "Emoji Picker", + "icon": "./assets/icon.png", + "description": "Insert emoji into messages by typing a colon (:) and the emoji name or choosing one from the composer toolbar!", + "windowTypes": { + "default": true, + "composer": true, + "thread-popout": true + }, + "license": "GPL-3.0" +} diff --git a/packages/client-app/internal_packages/composer-emoji/spec/emoji-button-popover-spec.jsx b/packages/client-app/internal_packages/composer-emoji/spec/emoji-button-popover-spec.jsx new file mode 100644 index 0000000000..382ffbda5b --- /dev/null +++ b/packages/client-app/internal_packages/composer-emoji/spec/emoji-button-popover-spec.jsx @@ -0,0 +1,51 @@ +import React from 'react'; +import ReactTestUtils from 'react-addons-test-utils'; + +import {findDOMNode} from 'react-dom'; +import {renderIntoDocument} from '../../../spec/nylas-test-utils'; +import Contenteditable from '../../../src/components/contenteditable/contenteditable'; +import EmojiButtonPopover from '../lib/emoji-button-popover'; +import EmojiComposerExtension from '../lib/emoji-composer-extension'; + +describe('EmojiButtonPopover', function emojiButtonPopover() { + beforeEach(() => { + this.position = { + x: 20, + y: 40, + } + spyOn(EmojiButtonPopover.prototype, 'calcPosition').andReturn(this.position); + spyOn(EmojiComposerExtension, '_onSelectEmoji').andCallThrough(); + + this.component = renderIntoDocument(); + this.canvas = findDOMNode(ReactTestUtils.findRenderedDOMComponentWithTag(this.component, 'canvas')); + + this.composer = renderIntoDocument( + + ); + }); + + describe('when inserting emoji', () => { + it('should insert emoji on click', () => { + ReactTestUtils.Simulate.mouseDown(this.canvas); + expect(EmojiComposerExtension._onSelectEmoji).toHaveBeenCalled(); + }); + }); + + describe('when searching for emoji', () => { + it('should filter for matches', () => { + this.searchNode = findDOMNode(ReactTestUtils.findRenderedDOMComponentWithClass(this.component, 'search')) + const event = { + target: { + value: "heart", + }, + } + ReactTestUtils.Simulate.change(this.searchNode, event); + ReactTestUtils.Simulate.mouseDown(this.canvas); + expect(EmojiComposerExtension._onSelectEmoji).toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/client-app/internal_packages/composer-emoji/spec/emoji-composer-extension-spec.jsx b/packages/client-app/internal_packages/composer-emoji/spec/emoji-composer-extension-spec.jsx new file mode 100644 index 0000000000..35d3825adb --- /dev/null +++ b/packages/client-app/internal_packages/composer-emoji/spec/emoji-composer-extension-spec.jsx @@ -0,0 +1,100 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import ReactTestUtils from 'react-addons-test-utils'; + +import {renderIntoDocument} from '../../../spec/nylas-test-utils'; +import Contenteditable from '../../../src/components/contenteditable/contenteditable'; +import EmojiComposerExtension from '../lib/emoji-composer-extension'; + +describe('EmojiComposerExtension', function emojiComposerExtension() { + beforeEach(() => { + spyOn(EmojiComposerExtension, 'onContentChanged').andCallThrough() + spyOn(EmojiComposerExtension, '_onSelectEmoji').andCallThrough() + this.component = renderIntoDocument( + + ) + this.editableNode = ReactDOM.findDOMNode(this.component).querySelector('[contenteditable]'); + }) + + describe('when emoji trigger is typed', () => { + beforeEach(() => { + this._performEdit = (newHTML) => { + this.editableNode.innerHTML = newHTML; + const sel = document.getSelection() + const textNode = this.editableNode.childNodes[0]; + sel.setBaseAndExtent(textNode, textNode.nodeValue.length, textNode, textNode.nodeValue.length); + } + }) + + it('should show the emoji picker', () => { + this._performEdit('Testing! :h'); + waitsFor(() => { + return ReactTestUtils.scryRenderedDOMComponentsWithClass(this.component, 'emoji-picker').length > 0 + }); + }) + + it('should be focused on the first emoji in the list', () => { + this._performEdit('Testing! :h'); + waitsFor(() => { + return ReactTestUtils.scryRenderedDOMComponentsWithClass(this.component, 'emoji-option').length > 0 + }); + runs(() => { + expect(ReactDOM.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithClass(this.component, 'emoji-option')).textContent.indexOf(":haircut:") !== -1).toBe(true); + }); + }) + + it('should insert an emoji on enter', () => { + this._performEdit('Testing! :h'); + waitsFor(() => { + return ReactTestUtils.scryRenderedDOMComponentsWithClass(this.component, 'emoji-picker').length > 0 + }); + runs(() => { + ReactTestUtils.Simulate.keyDown(this.editableNode, {key: "Enter", keyCode: 13, which: 13}); + }); + waitsFor(() => { + return EmojiComposerExtension._onSelectEmoji.calls.length > 0 + }) + runs(() => { + expect(this.editableNode.innerHTML).toContain("emoji haircut") + }); + }) + + it('should insert an emoji on click', () => { + this._performEdit('Testing! :h'); + waitsFor(() => { + return ReactTestUtils.scryRenderedDOMComponentsWithClass(this.component, 'emoji-picker').length > 0 + }); + runs(() => { + const button = ReactDOM.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithClass(this.component, 'emoji-option')) + ReactTestUtils.Simulate.mouseDown(button); + expect(EmojiComposerExtension._onSelectEmoji).toHaveBeenCalled() + }); + waitsFor(() => { + return EmojiComposerExtension._onSelectEmoji.calls.length > 0 + }) + runs(() => { + expect(this.editableNode.innerHTML).toContain("emoji haircut") + }); + }) + + it('should move to the next emoji on arrow down', () => { + this._performEdit('Testing! :h'); + waitsFor(() => { + return ReactTestUtils.scryRenderedDOMComponentsWithClass(this.component, 'emoji-option').length > 0 + }); + runs(() => { + ReactTestUtils.Simulate.keyDown(this.editableNode, {key: "ArrowDown", keyCode: 40, which: 40}); + }); + waitsFor(() => { + return EmojiComposerExtension.onContentChanged.calls.length > 1 + }); + runs(() => { + expect(ReactDOM.findDOMNode(ReactTestUtils.findRenderedDOMComponentWithClass(this.component, 'emoji-option')).textContent.indexOf(":hamburger:") !== -1).toBe(true); + }); + }) + }) +}) diff --git a/packages/client-app/internal_packages/composer-emoji/stylesheets/composer-emoji.less b/packages/client-app/internal_packages/composer-emoji/stylesheets/composer-emoji.less new file mode 100644 index 0000000000..693a61e9c3 --- /dev/null +++ b/packages/client-app/internal_packages/composer-emoji/stylesheets/composer-emoji.less @@ -0,0 +1,61 @@ +@import "ui-variables"; + +.emoji-picker { + max-height: 130px !important; + margin: 10px; + overflow: auto; + .btn.btn-icon { + font-size: 14px !important; + padding: 0 0.5em; + &:first-child { + padding-left: 0.5em !important; + } + &.emoji-option, &:hover { + background-color: @btn-emphasis-bg-color; + color: #FFFFFF; + border-radius: 5px; + } + } +} + +.emoji-button-popover { + width: 210px; + height: 290px; + overflow: hidden; + .emoji-tabs { + display: flex; + flex-direction: row; + padding: 5px 5px 5px 10px; + border-bottom: 1px solid @border-color-primary; + transition: box-shadow 0.5s; + &.shadow { + box-shadow: @standard-shadow; + } + .emoji-tab { + background-color: @gray-light; + &.active { + background-color: @component-active-color; + } + } + } + .emoji-finder-container { + height: 232px; + .scrollbar-track { + background: transparent; + border-left: none; + width: 10px; + } + .emoji-search-container { + padding: @padding-base-vertical * 1.5 @padding-base-horizontal 0; + } + } + .emoji-name { + height: 25px; + width: 192px; + margin-top: 2px; + margin-left: 10px; + overflow: hidden; + text-overflow: ellipsis; + color: @text-color-very-subtle; + } +} \ No newline at end of file diff --git a/packages/client-app/internal_packages/composer-markdown/README.md b/packages/client-app/internal_packages/composer-markdown/README.md new file mode 100644 index 0000000000..55468c6d5d --- /dev/null +++ b/packages/client-app/internal_packages/composer-markdown/README.md @@ -0,0 +1,26 @@ + +# N1 Markdown Composer + +A plugin for N1 that allows you to compose emails using markdown. + +![Markdown Screenshot Editor](/assets/markdown_screenshot_edit.png?raw=true "Markdown Composer Editor") +![Markdown Screenshot Preview](/assets/markdown_screenshot_preview.png?raw=true "Markdown Composer Preview") + +## Install this plugin: + +1. Download and run N1 + +2. Clone this repository (Make sure you have `git` installed and available in + your system path) + +3. From the menu, select `Developer > Install a Package Manually...` + From the dialog, choose the directory of this plugin to install it! + + > When you install packages, they're moved to `~/.nylas-mail/packages`, + > and N1 runs `apm install` on the command line to fetch dependencies + > listed in the package's `package.json` + + +## Usage + +Just write emails using markdown. diff --git a/packages/client-app/internal_packages/composer-markdown/assets/markdown_screenshot_edit.png b/packages/client-app/internal_packages/composer-markdown/assets/markdown_screenshot_edit.png new file mode 100644 index 0000000000..2120bdc2d6 Binary files /dev/null and b/packages/client-app/internal_packages/composer-markdown/assets/markdown_screenshot_edit.png differ diff --git a/packages/client-app/internal_packages/composer-markdown/assets/markdown_screenshot_preview.png b/packages/client-app/internal_packages/composer-markdown/assets/markdown_screenshot_preview.png new file mode 100644 index 0000000000..c8361d20f4 Binary files /dev/null and b/packages/client-app/internal_packages/composer-markdown/assets/markdown_screenshot_preview.png differ diff --git a/packages/client-app/internal_packages/composer-markdown/icon.png b/packages/client-app/internal_packages/composer-markdown/icon.png new file mode 100644 index 0000000000..2683b3b42a Binary files /dev/null and b/packages/client-app/internal_packages/composer-markdown/icon.png differ diff --git a/packages/client-app/internal_packages/composer-markdown/lib/main.cjsx b/packages/client-app/internal_packages/composer-markdown/lib/main.cjsx new file mode 100644 index 0000000000..fe1bbf7135 --- /dev/null +++ b/packages/client-app/internal_packages/composer-markdown/lib/main.cjsx @@ -0,0 +1,21 @@ +# Markdown Editor +# Last Revised: April 23, 2015 by Ben Gotow +# +# Markdown editor is a simple React component that allows you to type your +# emails in markdown and see the live preview of your email in html +# +{ExtensionRegistry, ComponentRegistry} = require 'nylas-exports' +MarkdownEditor = require './markdown-editor' +MarkdownComposerExtension = require './markdown-composer-extension' + +module.exports = + activate: -> + ComponentRegistry.register MarkdownEditor, + role: 'Composer:Editor' + ExtensionRegistry.Composer.register(MarkdownComposerExtension) + + serialize: -> + + deactivate: -> + ComponentRegistry.unregister(MarkdownEditor) + ExtensionRegistry.Composer.unregister(MarkdownComposerExtension) diff --git a/packages/client-app/internal_packages/composer-markdown/lib/markdown-composer-extension.coffee b/packages/client-app/internal_packages/composer-markdown/lib/markdown-composer-extension.coffee new file mode 100644 index 0000000000..27a02bb09a --- /dev/null +++ b/packages/client-app/internal_packages/composer-markdown/lib/markdown-composer-extension.coffee @@ -0,0 +1,17 @@ +marked = require 'marked' +Utils = require './utils' +{ComposerExtension} = require 'nylas-exports' + +rawBodies = {} + +class MarkdownComposerExtension extends ComposerExtension + + @applyTransformsForSending: ({draftBodyRootNode, draft}) -> + rawBodies[draft.clientId] = draftBodyRootNode.innerHTML + draftBodyRootNode.innerHTML = marked(draftBodyRootNode.innerText) + + @unapplyTransformsForSending: ({draftBodyRootNode, draft}) -> + if rawBodies[draft.clientId] + draftBodyRootNode.innerHTML = rawBodies[draft.clientId] + +module.exports = MarkdownComposerExtension diff --git a/packages/client-app/internal_packages/composer-markdown/lib/markdown-editor.cjsx b/packages/client-app/internal_packages/composer-markdown/lib/markdown-editor.cjsx new file mode 100644 index 0000000000..daae60aee9 --- /dev/null +++ b/packages/client-app/internal_packages/composer-markdown/lib/markdown-editor.cjsx @@ -0,0 +1,126 @@ +Utils = require './utils' +SimpleMDE = require 'simplemde' +{React, ReactDOM, QuotedHTMLTransformer} = require 'nylas-exports' + +# Keep a file-scope variable containing the contents of the markdown stylesheet. +# This will be embedded in the markdown preview iFrame, as well as the email body. +# The stylesheet is loaded when a preview component is first mounted. +markdownStylesheet = null + +splitContents = (contents) -> + quoteStart = contents.search(/(
+ @mde = new SimpleMDE( + inputStyle: 'contenteditable' + element: ReactDOM.findDOMNode(@refs.container), + hideIcons: ['fullscreen', 'side-by-side'] + showIcons: ['code', 'table'] + spellChecker: false, + ) + @mde.codemirror.on("change", @_onBodyChanged) + @mde.codemirror.on("keydown", @_onKeyDown) + @setCurrentBodyInDOM() + + componentDidUpdate: (prevProps) => + wasEmpty = prevProps.body.length is 0 + + if @props.body isnt prevProps.body and @props.body isnt @currentBodyInDOM() + @setCurrentBodyInDOM() + + if wasEmpty + @mde.codemirror.execCommand('goDocEnd') + + focus: => + @mde.codemirror.focus() + + focusAbsoluteEnd: => + @focus() + @mde.codemirror.execCommand('goDocEnd') + + setCurrentBodyInDOM: => + [editable, uneditable] = splitContents(@props.body) + + uneditableEl = ReactDOM.findDOMNode(@refs.uneditable) + uneditableEl.innerHTML = uneditable + uneditableNoticeEl = ReactDOM.findDOMNode(@refs.uneditableNotice) + if Utils.getTextFromHtml(uneditable).length > 0 + uneditableNoticeEl.style.display = 'block' + else + uneditableNoticeEl.style.display = 'none' + + @mde.value(Utils.getTextFromHtml(editable)) + + currentBodyInDOM: => + uneditableEl = ReactDOM.findDOMNode(@refs.uneditable) + return @mde.value() + uneditableEl.innerHTML + + getCurrentSelection: -> + + getPreviousSelection: -> + + setSelection: -> + container = ReactDOM.findDOMNode(@refs.container) + sel = document.getSelection() + sel.setBaseAndExtent(container, 0, container, 0) + + _onDOMMutated: -> + + _onBodyChanged: => + setImmediate => + value = @currentBodyInDOM() + @props.onBodyChanged({target: {value}}) + + _onKeyDown: (codemirror, e)=> + if e.key is 'Tab' and e.shiftKey is true + position = codemirror.cursorCoords(true, 'local') + isAtBeginning = position.top <= 5 and position.left <= 5 + if isAtBeginning + # TODO i'm /really/ sorry + # Subject is at position 2 within the tab group, the focused text area + # in this component is at position 17, so that's why we shift back 15 + # positions. + # This will break if the dom elements between here and the subject ever + # change + @context.parentTabGroup.shiftFocus(-15) + e.preventDefault() + e.codemirrorIgnore = true + + render: -> + # TODO sorry + # Add style tag to disable incompatible plugins +
+ +
+
+ The markdown editor does not support editing signatures or quoted text. Content below will be included in your message. +
+
+
+ +module.exports = MarkdownEditor diff --git a/packages/client-app/internal_packages/composer-markdown/lib/utils.coffee b/packages/client-app/internal_packages/composer-markdown/lib/utils.coffee new file mode 100644 index 0000000000..906d306fd2 --- /dev/null +++ b/packages/client-app/internal_packages/composer-markdown/lib/utils.coffee @@ -0,0 +1,9 @@ + +class Utils + + @getTextFromHtml: (html) -> + div = document.createElement('div') + div.innerHTML = html + div.textContent ? div.innerText + +module.exports = Utils diff --git a/packages/client-app/internal_packages/composer-markdown/package.json b/packages/client-app/internal_packages/composer-markdown/package.json new file mode 100644 index 0000000000..f324b9742e --- /dev/null +++ b/packages/client-app/internal_packages/composer-markdown/package.json @@ -0,0 +1,23 @@ +{ + "name": "composer-markdown", + "version": "0.1.0", + "main": "./lib/main", + "title": "Markdown (Experimental)", + "description": "Write emails using Markdown!", + "isHiddenOnPluginsPage": true, + "license": "GPL-3.0", + "icon": "./icon.png", + "engines": { + "nylas": "*" + }, + "isOptional": true, + "repository": { + "type": "git", + "url": "" + }, + "windowTypes": { + "default": true, + "composer": true, + "thread-popout": true + } +} diff --git a/packages/client-app/internal_packages/composer-markdown/stylesheets/index.less b/packages/client-app/internal_packages/composer-markdown/stylesheets/index.less new file mode 100644 index 0000000000..fce97425a0 --- /dev/null +++ b/packages/client-app/internal_packages/composer-markdown/stylesheets/index.less @@ -0,0 +1,35 @@ +@import "../internal_packages/composer-markdown/node_modules/simplemde/dist/simplemde.min.css"; +@import "ui-variables"; +@import "ui-mixins"; + +@blurred-primary-color: mix(@background-primary, #ffbb00, 96%); + +.compose-body .markdown-editor { + margin: auto; + padding: 10px 23px 10px; + width: 100%; + + .editing-region div[contenteditable] { + min-height: 0; + padding: 0; + } + + .uneditable-notice { + text-align: center; + font-size: 0.9em; + color: @text-color-very-subtle; + margin-bottom: 10px; + border-top: 1px dashed; + border-bottom: 1px dashed; + padding: 4px; + } +} + +.CodeMirror { + height: 225px; + min-height: 225px; + background: transparent; +} +.CodeMirror-sided { + display: inline-block; +} diff --git a/packages/client-app/internal_packages/composer-signature/lib/main.es6 b/packages/client-app/internal_packages/composer-signature/lib/main.es6 new file mode 100644 index 0000000000..05df986f2d --- /dev/null +++ b/packages/client-app/internal_packages/composer-signature/lib/main.es6 @@ -0,0 +1,31 @@ +import {PreferencesUIStore, ExtensionRegistry, ComponentRegistry} from 'nylas-exports'; + +import SignatureComposerExtension from './signature-composer-extension'; +import SignatureComposerDropdown from './signature-composer-dropdown'; +import PreferencesSignatures from "./preferences-signatures"; + +export function activate() { + this.preferencesTab = new PreferencesUIStore.TabItem({ + tabId: "Signatures", + displayName: "Signatures", + component: PreferencesSignatures, + }); + + ExtensionRegistry.Composer.register(SignatureComposerExtension); + PreferencesUIStore.registerPreferencesTab(this.preferencesTab); + + ComponentRegistry.register(SignatureComposerDropdown, { + role: 'Composer:FromFieldComponents', + }); +} + +export function deactivate() { + ExtensionRegistry.Composer.unregister(SignatureComposerExtension); + PreferencesUIStore.unregisterPreferencesTab(this.preferencesTab.sectionId); + + ComponentRegistry.unregister(SignatureComposerDropdown); +} + +export function serialize() { + return {}; +} diff --git a/packages/client-app/internal_packages/composer-signature/lib/preferences-signatures.jsx b/packages/client-app/internal_packages/composer-signature/lib/preferences-signatures.jsx new file mode 100644 index 0000000000..7f5735beed --- /dev/null +++ b/packages/client-app/internal_packages/composer-signature/lib/preferences-signatures.jsx @@ -0,0 +1,225 @@ +import React from 'react'; +import _ from 'underscore'; +import { + Flexbox, + RetinaImg, + EditableList, + Contenteditable, + ScrollRegion, + MultiselectDropdown, +} from 'nylas-component-kit'; +import {AccountStore, SignatureStore, Actions} from 'nylas-exports'; + + +export default class PreferencesSignatures extends React.Component { + static displayName = 'PreferencesSignatures'; + + constructor() { + super() + this.state = this._getStateFromStores() + } + + componentDidMount() { + this.unsubscribers = [ + SignatureStore.listen(this._onChange), + ] + } + + componentWillUnmount() { + this.unsubscribers.forEach(unsubscribe => unsubscribe()); + } + + + _onChange = () => { + this.setState(this._getStateFromStores()) + } + + _getStateFromStores() { + const signatures = SignatureStore.getSignatures() + const accountsAndAliases = AccountStore.aliases() + const selected = SignatureStore.selectedSignature() + const defaults = SignatureStore.getDefaults() + return { + signatures: signatures, + selectedSignature: selected, + defaults: defaults, + accountsAndAliases: accountsAndAliases, + editAsHTML: this.state ? this.state.editAsHTML : false, + } + } + + + _onCreateButtonClick = () => { + this._onAddSignature() + } + + _onAddSignature = () => { + Actions.addSignature() + } + + _onDeleteSignature = (signature) => { + Actions.removeSignature(signature) + } + + _onEditSignature = (edit) => { + let editedSig; + if (typeof edit === "object") { + editedSig = { + title: this.state.selectedSignature.title, + body: edit.target.value, + } + } else { + editedSig = { + title: edit, + body: this.state.selectedSignature.body, + } + } + Actions.updateSignature(editedSig, this.state.selectedSignature.id) + } + + _onSelectSignature = (sig) => { + Actions.selectSignature(sig.id) + } + + _onToggleAccount = (account) => { + Actions.toggleAccount(account.email) + } + + _onToggleEditAsHTML = () => { + const toggled = !this.state.editAsHTML + this.setState({editAsHTML: toggled}) + } + + _renderListItemContent = (sig) => { + return sig.title + } + + _renderSignatureToolbar() { + return ( +
+
+ Default for: {this._renderAccountPicker()} +
+
+ + +
+
+ ) + } + + _selectItemKey = (accountOrAlias) => { + return accountOrAlias.clientId + } + + _isChecked = (accountOrAlias) => { + if (!this.state.selectedSignature) { + return false; + } + return (this.state.defaults[accountOrAlias.email] === this.state.selectedSignature.id); + } + + _labelForAccountPicker() { + const sel = _.filter(this.state.accountsAndAliases, (accountOrAlias) => { + return this._isChecked(accountOrAlias) + }) + const numSelected = sel.length; + return numSelected.toString() + (numSelected === 1 ? " Account" : " Accounts") + } + + _renderAccountPicker() { + const buttonText = this._labelForAccountPicker() + + return ( + accountOrAlias.email} + /> + ) + } + + _renderEditableSignature() { + const selectedBody = this.state.selectedSignature ? this.state.selectedSignature.body : "" + return ( + + ) + } + + _renderHTMLSignature() { + return ( + - -
this.cancel.call(this)}> Cancel
-
- - ) - } - return ( -
-
Sync Policy
-
{this.props.stringifiedSyncPolicy}
-
this.edit.call(this)}> Edit
-
- ) - } -} - -SyncPolicy.propTypes = { - accountId: React.PropTypes.number, - stringifiedSyncPolicy: React.PropTypes.string, -} - -window.SyncPolicy = SyncPolicy; diff --git a/packages/nylas-dashboard/routes/account.js b/packages/nylas-dashboard/routes/account.js deleted file mode 100644 index a7c8c4fe5a..0000000000 --- a/packages/nylas-dashboard/routes/account.js +++ /dev/null @@ -1,30 +0,0 @@ -const Joi = require('joi'); -const {DatabaseConnector} = require(`nylas-core`); - -module.exports = (server) => { - server.route({ - method: 'PUT', - path: '/accounts/{accountId}/clear-sync-error', - config: { - description: 'Clears the sync error for the given account', - notes: 'Notes go here', - tags: ['accounts', 'sync-error'], - validate: { - params: { - accountId: Joi.number().integer(), - }, - }, - response: { - schema: Joi.string(), - }, - }, - handler: (request, reply) => { - DatabaseConnector.forShared().then(({Account}) => { - Account.find({where: {id: request.params.accountId}}).then((account) => { - account.syncError = null; - account.save().then(() => reply("Success")); - }) - }) - }, - }); -}; diff --git a/packages/nylas-dashboard/routes/sync-policy.js b/packages/nylas-dashboard/routes/sync-policy.js deleted file mode 100644 index ac327af4bb..0000000000 --- a/packages/nylas-dashboard/routes/sync-policy.js +++ /dev/null @@ -1,54 +0,0 @@ -const Joi = require('joi'); -const {SchedulerUtils} = require(`nylas-core`); - -module.exports = (server) => { - server.route({ - method: 'POST', - path: '/sync-policy/{account_id}', - config: { - description: 'Set the sync policy', - notes: 'Notes go here', - tags: ['sync-policy'], - validate: { - params: { - account_id: Joi.number().integer(), - }, - payload: { - sync_policy: Joi.string(), - }, - }, - response: { - schema: Joi.string(), - }, - }, - handler: (request, reply) => { - const newPolicy = JSON.parse(request.payload.sync_policy); - SchedulerUtils.assignPolicy(request.params.account_id, newPolicy) - .then(() => reply("Success")); - }, - }); - - server.route({ - method: 'POST', - path: '/sync-policy', - config: { - description: 'Set the sync policy for several accounts', - notes: 'Notes go here', - tags: ['sync-policy'], - validate: { - payload: { - sync_policy: Joi.string(), - account_ids: Joi.array().items(Joi.number().integer().min(0)), - }, - }, - response: { - schema: Joi.string(), - }, - }, - handler: (request, reply) => { - const newPolicy = JSON.parse(request.payload.sync_policy); - SchedulerUtils.assignPolicyToAcounts(request.payload.account_ids, newPolicy) - .then(() => reply("Success")); - }, - }) -}; diff --git a/packages/nylas-dashboard/routes/syncback-requests.js b/packages/nylas-dashboard/routes/syncback-requests.js deleted file mode 100644 index 267f1086a2..0000000000 --- a/packages/nylas-dashboard/routes/syncback-requests.js +++ /dev/null @@ -1,81 +0,0 @@ -const Joi = require('joi'); -const {DatabaseConnector} = require(`nylas-core`); - -module.exports = (server) => { - server.route({ - method: 'GET', - path: '/syncback-requests/{account_id}', - config: { - description: 'Get the SyncbackRequests for an account', - notes: 'Notes go here', - tags: ['syncback-requests'], - validate: { - params: { - account_id: Joi.number().integer(), - }, - }, - response: { - schema: Joi.string(), - }, - }, - handler: (request, reply) => { - DatabaseConnector.forAccount(request.params.account_id).then((db) => { - const {SyncbackRequest} = db; - SyncbackRequest.findAll().then((syncbackRequests) => { - reply(JSON.stringify(syncbackRequests)) - }); - }); - }, - }); - - server.route({ - method: 'GET', - path: '/syncback-requests/{account_id}/counts', - config: { - description: 'Get stats on the statuses of SyncbackRequests', - notes: 'Notes go here', - tags: ['syncback-requests'], - validate: { - params: { - account_id: Joi.number().integer(), - }, - query: { - since: Joi.date().timestamp(), - }, - }, - response: { - schema: Joi.string(), - }, - }, - handler: (request, reply) => { - DatabaseConnector.forAccount(request.params.account_id).then((db) => { - const {SyncbackRequest} = db; - - const counts = { - 'new': null, - 'succeeded': null, - 'failed': null, - } - - const where = {}; - if (request.query.since) { - where.createdAt = {gt: request.query.since}; - } - - const countPromises = []; - for (const status of Object.keys(counts)) { - where.status = status.toUpperCase(); - countPromises.push( - SyncbackRequest.count({where: where}).then((count) => { - counts[status] = count; - }) - ); - } - - Promise.all(countPromises).then(() => { - reply(JSON.stringify(counts)); - }) - }); - }, - }); -}; diff --git a/packages/nylas-dashboard/routes/websocket.js b/packages/nylas-dashboard/routes/websocket.js deleted file mode 100644 index 5bfb8e419f..0000000000 --- a/packages/nylas-dashboard/routes/websocket.js +++ /dev/null @@ -1,132 +0,0 @@ -const { - DatabaseConnector, - PubsubConnector, - SchedulerUtils, -} = require(`nylas-core`); - -function onWebsocketConnected(wss, ws) { - let toSend; - function resetToSend() { - toSend = { - updatedAccounts: [], - activeAccountIds: [], - assignments: {}, - processLoads: {}, - }; - } - resetToSend(); - - function sendUpdate() { - ws.send(JSON.stringify({cmd: "UPDATE", payload: toSend})); - resetToSend(); - } - - DatabaseConnector.forShared().then(({Account}) => { - Account.findAll().then((accounts) => { - accounts.forEach((acct) => { - toSend.updatedAccounts.push(acct); - if (toSend.updatedAccounts.length >= 50) { - sendUpdate(); - } - }); - sendUpdate(); - }); - - this.observable = PubsubConnector.observeAllAccounts().subscribe((accountId) => { - Account.find({where: {id: accountId}}).then((acct) => { - toSend.updatedAccounts.push(acct); - }); - }); - - this.pollInterval = setInterval(() => { - const getActiveIds = SchedulerUtils.listActiveAccounts().then((accountIds) => { - toSend.activeAccountIds = accountIds; - }); - const getAssignments = SchedulerUtils.forEachAccountList((identity, accountIds) => { - toSend.processLoads[identity] = accountIds; - for (const accountId of accountIds) { - toSend.assignments[accountId] = identity; - } - }) - - Promise.all([getActiveIds, getAssignments]).then(() => { - sendUpdate(); - }) - }, 1000); - }); -} - -function onWebsocketDisconnected() { - clearInterval(this.pollInterval); - this.observable.dispose(); -} - -function onWebsocketConnectedFake(wss, ws) { - const accts = []; - for (let ii = 0; ii < 100; ii++) { - const acct = { - id: ii, - email_address: `halla+${ii}@nylas.com`, - object: "account", - organization_unit: "folder", - provider: "imap", - connection_settings: { - imap_host: "imap.mail.me.com", - imap_port: 993, - smtp_host: "smtp.mail.me.com", - smtp_port: 0, - ssl_required: true, - }, - sync_policy: { - afterSync: "idle", - intervals: { - active: 30000, - inactive: 300000, - }, - folderSyncOptions: { - deepFolderScan: 600000, - }, - }, - sync_error: null, - first_sync_completion: 0, - last_sync_completions: [], - created_at: "2016-07-13T00:49:25.000Z", - }; - ws.send(JSON.stringify({ cmd: "UPDATE", payload: { - updatedAccounts: [acct], - activeAccountIds: [], - assignments: {}, - }})); - accts.push(acct); - } - setInterval(() => { - const acct = accts[Math.floor(Math.random() * accts.length)]; - ws.send(JSON.stringify({ cmd: "UPDATE", payload: { - updatedAccounts: [acct], - activeAccountIds: [], - assignments: {}, - }})); - }, 250); -} - -module.exports = (server) => { - server.route({ - method: "POST", - path: "/websocket", - config: { - plugins: { - websocket: { - only: true, - connect: onWebsocketConnected, - disconnect: onWebsocketDisconnected, - }, - }, - }, - handler: (request, reply) => { - if (request.payload.cmd === "PING") { - reply(JSON.stringify({ result: "PONG" })); - return; - } - }, - }); -} diff --git a/packages/nylas-message-processor/app.js b/packages/nylas-message-processor/app.js deleted file mode 100644 index b2ff487d00..0000000000 --- a/packages/nylas-message-processor/app.js +++ /dev/null @@ -1,87 +0,0 @@ -const Metrics = require(`nylas-metrics`) -Metrics.startCapturing('nylas-k2-message-processor') - -const {PubsubConnector, DatabaseConnector, Logger} = require(`nylas-core`) -const {processors} = require('./processors') - -global.Metrics = Metrics -global.Logger = Logger.createLogger('nylas-k2-message-processor') - -const onUnhandledError = (err) => { - global.Logger.fatal(err, 'Unhandled error') - global.Metrics.reportError(err) -} -process.on('uncaughtException', onUnhandledError) -process.on('unhandledRejection', onUnhandledError) - -// List of the attributes of Message that the processor should be allowed to change. -// The message may move between folders, get starred, etc. while it's being -// processed, and it shouldn't overwrite changes to those fields. -const MessageAttributes = ['body', 'processed', 'to', 'from', 'cc', 'replyTo', 'bcc', 'snippet', 'threadId'] -const MessageProcessorVersion = 1; - -const redis = PubsubConnector.buildClient(); - -function runPipeline({db, accountId, message, logger}) { - logger.info(`MessageProcessor: Processing message`) - return processors.reduce((prevPromise, processor) => ( - prevPromise.then((prevMessage) => { - const processed = processor({message: prevMessage, accountId, db, logger}); - return Promise.resolve(processed) - .then((nextMessage) => { - if (!nextMessage.body) { - throw new Error("processor did not resolve with a valid message object.") - } - return Promise.resolve(nextMessage); - }) - }) - ), Promise.resolve(message)) -} - -function saveMessage(message) { - message.processed = MessageProcessorVersion; - return message.save({ - fields: MessageAttributes, - }); -} - -function dequeueJob() { - redis.brpopAsync('message-processor-queue', 10).then((item) => { - if (!item) { - return dequeueJob(); - } - - let json = null; - try { - json = JSON.parse(item[1]); - } catch (error) { - global.Logger.error({item}, `MessageProcessor: Found invalid JSON item in queue`) - return dequeueJob(); - } - const {messageId, accountId} = json; - const logger = global.Logger.forAccount({id: accountId}).child({message_id: messageId}) - - DatabaseConnector.forAccount(accountId).then((db) => { - return db.Message.find({ - where: {id: messageId}, - include: [{model: db.Folder}, {model: db.Label}], - }).then((message) => { - if (!message) { - return Promise.reject(new Error(`Message not found (${messageId}). Maybe account was deleted?`)) - } - return runPipeline({db, accountId, message, logger}).then((processedMessage) => - saveMessage(processedMessage) - ).catch((err) => - logger.error(err, `MessageProcessor: Failed`) - ) - }) - }) - .finally(() => { - dequeueJob() - }); - - return null; - }) -} - -dequeueJob(); diff --git a/packages/nylas-message-processor/newrelic.js b/packages/nylas-message-processor/newrelic.js deleted file mode 100644 index e74c412de5..0000000000 --- a/packages/nylas-message-processor/newrelic.js +++ /dev/null @@ -1,22 +0,0 @@ -const {NODE_ENV} = process.env -/** - * New Relic agent configuration. - * - * See lib/config.defaults.js in the agent distribution for a more complete - * description of configuration variables and their potential values. - */ -exports.config = { - /** - * Array of application names. - */ - app_name: [`k2-message-processor-${NODE_ENV}`], - logging: { - /** - * Level at which to log. 'trace' is most useful to New Relic when diagnosing - * issues with the agent, 'info' and higher will impose the least overhead on - * production applications. - */ - level: 'info', - }, -} - diff --git a/packages/nylas-message-processor/package.json b/packages/nylas-message-processor/package.json deleted file mode 100644 index a50b1ccf37..0000000000 --- a/packages/nylas-message-processor/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "nylas-message-processor", - "version": "0.0.1", - "description": "Message processing pipeline", - "main": "index.js", - "author": "Nylas", - "license": "ISC", - "scripts": { - "test": "babel-node spec/run.js" - }, - "dependencies": { - "babel-cli": "6.10.1", - "babel-preset-es2015": "6.9.0", - "jasmine": "2.4.1", - "mailparser": "0.6.0", - "mimelib": "0.2.19", - "nylas-core": "0.x.x", - "nylas-metrics": "0.x.x" - }, - "devDependencies": { - "babel-cli": "^6.10.1", - "babel-preset-es2015": "^6.9.0", - "jasmine": "^2.4.1" - }, - "babel": { - "presets": [ - "es2015" - ] - } -} diff --git a/packages/nylas-message-processor/processors/contact.js b/packages/nylas-message-processor/processors/contact.js deleted file mode 100644 index c5d93e6e06..0000000000 --- a/packages/nylas-message-processor/processors/contact.js +++ /dev/null @@ -1,46 +0,0 @@ - -class ContactProcessor { - - verified(contact) { - // some suggestions: http://stackoverflow.com/questions/6317714/apache-camel-mail-to-identify-auto-generated-messages - const regex = new RegExp(/^(noreply|no-reply|donotreply|mailer|support|webmaster|news(letter)?@)/ig) - - if (!contact.email) { - return false; - } - if (regex.test(contact.email) || contact.email.length > 60) { - return false - } - return true - } - - processMessage({db, message}) { - const {Contact} = db; - - let allContacts = []; - ['to', 'from', 'bcc', 'cc'].forEach((field) => { - allContacts = allContacts.concat(message[field]) - }) - - const verifiedContacts = allContacts.filter(this.verified); - - return db.sequelize.transaction((transaction) => { - return Promise.all(verifiedContacts.map((contact) => - Contact.upsert({ - name: contact.name, - email: contact.email, - accountId: message.accountId, - }, { - transaction, - }) - )) - }).thenReturn(message) - } -} - -const processor = new ContactProcessor() - -module.exports = { - order: 3, - processMessage: processor.processMessage.bind(processor), -} diff --git a/packages/nylas-message-processor/processors/index.js b/packages/nylas-message-processor/processors/index.js deleted file mode 100644 index a11c906a48..0000000000 --- a/packages/nylas-message-processor/processors/index.js +++ /dev/null @@ -1,15 +0,0 @@ -const fs = require('fs') - -const processors = fs.readdirSync(__dirname) -.filter((file) => file !== 'index.js') -.map((file) => { - const {processMessage, order} = require(`./${file}`) - if (!processMessage) { - throw new Error(`${file} does not export a method named processMessage.`) - } - return {processMessage, order} -}) -.sort((p1, p2) => p1.order - p2.order) -.map((p) => p.processMessage) - -module.exports = {processors} diff --git a/packages/nylas-message-processor/processors/parsing.js b/packages/nylas-message-processor/processors/parsing.js deleted file mode 100644 index a144199ab0..0000000000 --- a/packages/nylas-message-processor/processors/parsing.js +++ /dev/null @@ -1,42 +0,0 @@ -const mimelib = require('mimelib'); -const SNIPPET_SIZE = 100 - -function Contact({name, address} = {}) { - return { - name, - email: address, - } -} - -const extractContacts = (values) => - (values || []).map(v => Contact(mimelib.parseAddresses(v).pop())) - -function processMessage({message, logger}) { - if (message.snippet) { - // trim and clean snippet which is alreay present (from message plaintext) - message.snippet = message.snippet.replace(/[\n\r]/g, ' ').replace(/\s\s+/g, ' ') - const loc = message.snippet.indexOf(' ', SNIPPET_SIZE); - if (loc !== -1) { - message.snippet = message.snippet.substr(0, loc); - } - } else if (message.body) { - // create snippet from body, which is most likely html - // TODO: Fanciness - message.snippet = message.body.substr(0, Math.min(message.body.length, SNIPPET_SIZE)); - } else { - logger.info("MessageProcessor: Parsing - Received message has no body or snippet.") - } - - message.to = extractContacts(message.headers.to); - message.cc = extractContacts(message.headers.cc); - message.bcc = extractContacts(message.headers.bcc); - message.from = extractContacts(message.headers.from); - message.replyTo = extractContacts(message.headers['reply-to']); - - return Promise.resolve(message); -} - -module.exports = { - order: 0, - processMessage, -} diff --git a/packages/nylas-message-processor/processors/quoted-text.js b/packages/nylas-message-processor/processors/quoted-text.js deleted file mode 100644 index ca36ff96a6..0000000000 --- a/packages/nylas-message-processor/processors/quoted-text.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - order: 2, - processMessage: ({message}) => Promise.resolve(message), -} diff --git a/packages/nylas-message-processor/processors/threading.js b/packages/nylas-message-processor/processors/threading.js deleted file mode 100644 index 039f36409f..0000000000 --- a/packages/nylas-message-processor/processors/threading.js +++ /dev/null @@ -1,174 +0,0 @@ -const {PromiseUtils} = require('nylas-core') -// const _ = require('underscore'); - -class ThreadingProcessor { - pickMatchingThread(message, threads) { - return threads.pop(); - - // This logic is tricky... Used to say that threads with >2 participants in common - // should be treated as the same, plus special cases for when it's a 1<>1 - // conversation. Put it back soonish. - - // const messageEmails = _.uniq([].concat(message.to, message.cc, message.from).map(p => p.email)); - // this.logger.info({ - // num_candidate_threads: threads.length, - // message_subject: message.subject, - // }, `Found candidate threads for message`) - // - // for (const thread of threads) { - // const threadEmails = _.uniq([].concat(thread.participants).map(p => p.email)); - // this.logger.info(`Intersection: ${_.intersection(threadEmails, messageEmails).join(',')}`) - // - // if (_.intersection(threadEmails, messageEmails) >= threadEmails.length * 0.9) { - // return thread; - // } - // } - // - // return null; - } - - cleanSubject(subject = "") { - const regex = new RegExp(/^((re|fw|fwd|aw|wg|undeliverable|undelivered):\s*)+/ig); - return subject.replace(regex, () => ""); - } - - emptyThread({Thread, accountId}, options = {}) { - return Thread.create(Object.assign({accountId}, options)).then((t) => { - t.folders = []; - t.labels = []; - return t; - }); - } - - findOrCreateByMatching(db, message) { - const {Thread, Label, Folder} = db - - // in the future, we should look at In-reply-to. Problem is it's a single- - // directional linked list, and we don't scan the mailbox from oldest=>newest, - // but from newest->oldest, so when we ingest a message it's very unlikely - // we have the "In-reply-to" message yet. - - return Thread.findAll({ - where: { - subject: this.cleanSubject(message.subject), - }, - order: [ - ['id', 'DESC'], - ], - limit: 10, - include: [{model: Label}, {model: Folder}], - }).then((threads) => - this.pickMatchingThread(message, threads) || this.emptyThread(db, {}) - ) - } - - findOrCreateByRemoteThreadId(db, remoteThreadId) { - const {Thread, Label, Folder} = db; - return Thread.find({ - where: {remoteThreadId}, - include: [{model: Label}, {model: Folder}], - }).then((thread) => { - return thread || this.emptyThread(db, {remoteThreadId}) - }) - } - - processMessage({db, message, logger}) { - if (!(message.labels instanceof Array)) { - throw new Error("Threading processMessage expects labels to be an inflated array."); - } - if (message.folder === undefined) { - throw new Error("Threading processMessage expects folder value to be present."); - } - - - const {Folder, Label} = db; - let findOrCreateThread = null; - if (message.headers['x-gm-thrid']) { - findOrCreateThread = this.findOrCreateByRemoteThreadId(db, message.headers['x-gm-thrid']) - } else { - findOrCreateThread = this.findOrCreateByMatching(db, message) - } - - return PromiseUtils.props({ - thread: findOrCreateThread, - sentFolder: Folder.find({where: {role: 'sent'}}), - sentLabel: Label.find({where: {role: 'sent'}}), - }) - .then(({thread, sentFolder, sentLabel}) => { - thread.addMessage(message); - - if (!(thread.labels instanceof Array)) { - throw new Error("Threading processMessage expects thread.labels to be an inflated array."); - } - if (!(thread.folders instanceof Array)) { - throw new Error("Threading processMessage expects thread.folders to be an inflated array."); - } - - // update the basic properties of the thread - thread.accountId = message.accountId; - - // update the participants on the thread - const threadParticipants = [].concat(thread.participants); - const threadEmails = thread.participants.map(p => p.email); - - for (const p of [].concat(message.to, message.cc, message.from)) { - if (!threadEmails.includes(p.email)) { - threadParticipants.push(p); - threadEmails.push(p.email); - } - } - thread.participants = threadParticipants; - - // update starred and unread - if (thread.starredCount == null) { thread.starredCount = 0; } - thread.starredCount += message.starred ? 1 : 0; - if (thread.unreadCount == null) { thread.unreadCount = 0; } - thread.unreadCount += message.unread ? 1 : 0; - - // update dates - if (!thread.lastMessageDate || (message.date > thread.lastMessageDate)) { - thread.lastMessageDate = message.date; - thread.snippet = message.snippet; - thread.subject = this.cleanSubject(message.subject); - } - if (!thread.firstMessageDate || (message.date < thread.firstMessageDate)) { - thread.firstMessageDate = message.date; - } - - let isSent = false; - if (sentFolder) { - isSent = message.folderId === sentFolder.id - } else if (sentLabel) { - isSent = !!message.labels.find(l => l.id === sentLabel.id) - } - - if (isSent && ((message.date > thread.lastMessageSentDate) || !thread.lastMessageSentDate)) { - thread.lastMessageSentDate = message.date; - } - if (!isSent && ((message.date > thread.lastMessageReceivedDate) || !thread.lastMessageReceivedDate)) { - thread.lastMessageReceivedDate = message.date; - } - // update folders and labels - if (!thread.folders.find(f => f.id === message.folderId)) { - thread.addFolder(message.folder) - } - for (const label of message.labels) { - if (!thread.labels.find(l => l.id === label)) { - thread.addLabel(label) - } - } - - return thread.save().then((saved) => { - message.threadId = saved.id; - return message; - }); - }); - } -} - -const processor = new ThreadingProcessor(); - -module.exports = { - processMessage: processor.processMessage.bind(processor), - order: 1, -}; diff --git a/packages/nylas-message-processor/spec/fixtures/1-99174-body.txt b/packages/nylas-message-processor/spec/fixtures/1-99174-body.txt deleted file mode 100644 index c711338c08..0000000000 --- a/packages/nylas-message-processor/spec/fixtures/1-99174-body.txt +++ /dev/null @@ -1,51 +0,0 @@ -----==_mimepart_576812149912_70c83ff638d112bc812dd -Content-Type: text/plain; - charset=UTF-8 -Content-Transfer-Encoding: 7bit - -> @@ -1298,3 +1298,15 @@ -> - "Unity" -> - "Scratch" -> icon: "gausssense.png" -> +- -> + name: "Jasper" -> + description: "The GitHub Issue Reader" -> + website: "https://jasperapp.io" -> + repository: "https://github.com/jasperapp/jasper" -> + license: "" - -Oh oops, one more thing, you can also just remove this line rather than leave it empty. - ---- -You are receiving this because you are subscribed to this thread. -Reply to this email directly or view it on GitHub: -https://github.com/electron/electron.atom.io/pull/352/files/c3af72fe5aea0add912cd5afe153a626619de222#r67715160 -----==_mimepart_576812149912_70c83ff638d112bc812dd -Content-Type: text/html; - charset=UTF-8 -Content-Transfer-Encoding: 7bit - -

In _data/apps.yml:

-
> @@ -1298,3 +1298,15 @@
->      - "Unity"
->      - "Scratch"
->    icon: "gausssense.png"
-> +-
-> +  name: "Jasper"
-> +  description: "The GitHub Issue Reader"
-> +  website: "https://jasperapp.io"
-> +  repository: "https://github.com/jasperapp/jasper"
-> +  license: ""
-
-

Oh oops, one more thing, you can also just remove this line rather than leave it empty.

- -


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.

-
-
- - -
- -
- -----==_mimepart_576812149912_70c83ff638d112bc812dd-- diff --git a/packages/nylas-message-processor/spec/fixtures/1-99174-headers.txt b/packages/nylas-message-processor/spec/fixtures/1-99174-headers.txt deleted file mode 100644 index 3658cf07b5..0000000000 --- a/packages/nylas-message-processor/spec/fixtures/1-99174-headers.txt +++ /dev/null @@ -1,67 +0,0 @@ -Delivered-To: bengotow@gmail.com -Received: by 10.107.39.131 with SMTP id n125csp1566492ion; Mon, 20 Jun 2016 - 08:56:06 -0700 (PDT) -X-Received: by 10.107.175.83 with SMTP id y80mr24643127ioe.70.1466438166869; - Mon, 20 Jun 2016 08:56:06 -0700 (PDT) -Return-Path: -Received: from o5.sgmail.github.com (o5.sgmail.github.com. [192.254.113.10]) - by mx.google.com with ESMTPS id g123si16992050ioe.194.2016.06.20.08.56.06 for - (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 - bits=128/128); Mon, 20 Jun 2016 08:56:06 -0700 (PDT) -Received-SPF: pass (google.com: best guess record for domain of - bounces+848413-92ca-bengotow=gmail.com@sgmail.github.com designates - 192.254.113.10 as permitted sender) client-ip=192.254.113.10; -Authentication-Results: mx.google.com; dkim=pass header.i=@github.com; - dkim=pass header.i=@sendgrid.info; spf=pass (google.com: best guess record - for domain of bounces+848413-92ca-bengotow=gmail.com@sgmail.github.com - designates 192.254.113.10 as permitted sender) - smtp.mailfrom=bounces+848413-92ca-bengotow=gmail.com@sgmail.github.com; - dmarc=pass (p=NONE dis=NONE) header.from=github.com -DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=github.com; - h=from:reply-to:to:cc:in-reply-to:references:subject:mime-version:content-type:content-transfer-encoding:list-id:list-archive:list-post:list-unsubscribe; - s=s20150108; bh=5XfiJ/IF3qb2JfQruE1ZXDdHpzo=; b=pVyxGvcKaxEE3zix - VALF4ugiEssCLrxxiuKOPttOz/WL7Xzf6wUl74cHRUbZ4ShEXT3pe8yUbiMuWJIy - R/Kegxrf+ThIoFNNj4SLuZbtv+N7Ic2GrXZdxZLyHnR5sxNKdXk7r0Z6GkXdmavZ - Y3GYWjJp9DO7FMFyQ88BqIPQXto= -DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sendgrid.info; - h=from:reply-to:to:cc:in-reply-to:references:subject:mime-version:content-type:content-transfer-encoding:list-id:list-archive:list-post:list-unsubscribe:x-feedback-id; - s=smtpapi; bh=5XfiJ/IF3qb2JfQruE1ZXDdHpzo=; b=WHrBPVafQkvIJmwSPM - SPpv2z0GcAQcM8IKTj/L4KDI92vwfoy8zclmTj+NB4XP5LH8Fcjd3Zj9yFJvb5O/ - 2+ZSEhQNSg9ALStUDQuGYR0kzspSQsvncx4jI7tHZPfP2oAZkQIu4IYLjVN80IEF - SuFzrPpitO7BmwcRSiAy7SZLI= -Received: by filter0441p1mdw1.sendgrid.net with SMTP id - filter0441p1mdw1.13584.5768121421 2016-06-20 15:56:04.264672456 +0000 UTC -Received: from github-smtp2b-ext-cp1-prd.iad.github.net - (github-smtp2b-ext-cp1-prd.iad.github.net [192.30.253.17]) by - ismtpd0004p1iad1.sendgrid.net (SG) with ESMTP id LipJElPMRZOlYFTbOZCHmg for - ; Mon, 20 Jun 2016 15:56:04.173 +0000 (UTC) -Date: Mon, 20 Jun 2016 08:56:04 -0700 -From: Jessica Lord -Reply-To: "electron/electron.atom.io" - -To: "electron/electron.atom.io" -Cc: -Message-ID: -In-Reply-To: -References: -Subject: Re: [electron/electron.atom.io] Add Jasper app (#352) -Mime-Version: 1.0 -Content-Type: multipart/alternative; - boundary="--==_mimepart_576812149912_70c83ff638d112bc812dd"; charset=UTF-8 -Content-Transfer-Encoding: 7bit -Precedence: list -X-GitHub-Sender: jlord -X-GitHub-Recipient: bengotow -List-ID: electron/electron.atom.io -List-Archive: https://github.com/electron/electron.atom.io -List-Post: -List-Unsubscribe: , - -X-Auto-Response-Suppress: All -X-GitHub-Recipient-Address: bengotow@gmail.com -X-SG-EID: zpvha59ROIAeS8NpfQSmmILEvCHiUBlaQ8TFhOrQHPl0QSHOgRoWHzGqgNGQ1PnCE+4nt5yi6b+iVd - zi18AeK3NmcJMPjYoKBL9oylAFXcX7JSvmAI2o8GQMXjRfn6C9jjTKhzFhZ3brgLB13N+v++poucpZ - K0ksN28YtdSO0PvYYktx2Lu+g8yKBRFzyXmcuJM5D9O3YDhd0/VRVk1WlanT5sFV/t1fjjJtGwaOvg - 4= -X-Feedback-ID: 848413:6xvVEJqleZlAW7/vhv7PzD/cv5tamo2SWZDKyvugGvg=:6xvVEJqleZlAW7/vhv7PzD/cv5tamo2SWZDKyvugGvg=:SG - diff --git a/packages/nylas-message-processor/spec/fixtures/thread.js b/packages/nylas-message-processor/spec/fixtures/thread.js deleted file mode 100644 index ce407afb7d..0000000000 --- a/packages/nylas-message-processor/spec/fixtures/thread.js +++ /dev/null @@ -1,94 +0,0 @@ -export const message = { - id: 1, - subject: "Loved your work and interests", - body: "Hi Jackie,
While browsing Nylas themes, I stumbled upon your website and looked at your work. 
Great work on projects, nice to see your multidisciplinary interests :)

Thanks, 
Sagar Sutar
thesagarsutar.me
", - headers: { - "Delivered-To": "jackiehluo@gmail.com", - "Received": `by 10.107.182.215 with SMTP id g206csp311103iof; - Fri, 17 Jun 2016 09:38:45 -0700 (PDT)`, - "X-Received": `by 10.66.16.133 with SMTP id g5mr1799805pad.145.1466181525915; - Fri, 17 Jun 2016 09:38:45 -0700 (PDT)`, - "Return-Path": "", - "Received": `from mail-pf0-f174.google.com (mail-pf0-f174.google.com. - [209.85.192.174]) - by mx.google.com with ESMTPS id n6si15649421pav.242.2016.06.17.09.38.45 - for - (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); - Fri, 17 Jun 2016 09:38:45 -0700 (PDT)`, - "Received-SPF": `pass (google.com: domain of sagy26.1991@gmail.com - designates 209.85.192.174 as permitted sender) client-ip=209.85.192.174;`, - "Authentication-Results": `mx.google.com; - spf=pass (google.com: domain of sagy26.1991@gmail.com designates - 209.85.192.174 as permitted sender) smtp.mailfrom=sagy26.1991@gmail.com`, - "Received": `by mail-pf0-f174.google.com with SMTP id i123so25772868pfg.0 - for ; Fri, 17 Jun 2016 09:38:45 -0700 (PDT)`, - "X-Google-DKIM-Signature": `v=1; a=rsa-sha256; c=relaxed/relaxed; - d=1e100.net; s=20130820; - h=x-gm-message-state:date:user-agent:message-id:to:from:subject - :mime-version; - bh=to3fCB9g4R6V18kpAAKSAlUeTC+N0rg4JckFbiaILA4=; - b=WfI5viTYPjviUur9Bd2rJQfpHxIm2xYRdxrN64bJGuX0TQlb7p8bDvCBNNhY3mTXJx - lsQzRX9RA4FMuDk0oz0mpviWtkpkZsDeyjpSmA+ONcPgdyPAezzPDvSWRzMZY21fiHxS - hr4I5AeFKesGcbvwtJu+S0fMGhdveC8E35oTA010Xfave6Xd55qGXy7hW+4xCfvIesy4 - 01oOaXWDmLHqixKO3SXwmGCcDzqn/IKXhB7UXkF0efSTwh8yid6v9iXdW+ovJ2qg9peI - HSnPIilYk8SaKoPdGDgYZykfUIgNrSugtK/vvGG2aN+9lhURxPfzhniWdNqdsgR7G4E7 - 7XqA==`, - "X-Gm-Message-State": "ALyK8tIf7XyYaylyVf0qjzh8rhYz3rj/VQYaNLDjVq5ESH19ioJIgW7o9FbghP+wFYrBuw==", - "X-Received": `by 10.98.111.138 with SMTP id k132mr3246291pfc.105.1466181525186; - Fri, 17 Jun 2016 09:38:45 -0700 (PDT)`, - "Return-Path": "", - "Received": `from [127.0.0.1] (ec2-52-36-99-221.us-west-2.compute.amazonaws.com. [52.36.99.221]) - by smtp.gmail.com with ESMTPSA id d69sm64179062pfj.31.2016.06.17.09.38.44 - for - (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); - Fri, 17 Jun 2016 09:38:44 -0700 (PDT)`, - "Date": "Fri, 17 Jun 2016 09:38:44 -0700 (PDT)", - "User-Agent": "NylasMailer/0.4", - "Message-Id": "<82y7eq1ipmadaxwcy6kr072bw-2147483647@mailer.nylas.com>", - "X-Inbox-Id": "82y7eq1ipmadaxwcy6kr072bw-2147483647", - }, - from: [{ - name: "Sagar Sutar", - email: "", - }], - to: [{ - name: "jackiehluo@gmail.com", - email: "", - }], - cc: [], - bcc: [], - headerMessageId: "<82y7eq1ipmadaxwcy6kr072bw-2147483647@mailer.nylas.com>", - snippet: "Hi Jackie, While browsing Nylas themes, I stumbled upon your website and looked at your work. Great ", - setThread: (thread) => { - message.thread = thread.id - }, -} - -export const reply = { - id: 2, - subject: "Re: Loved your work and interests", - body: "Sagar,

Aw, glad to hear it! Thanks for getting in touch!

Jackie Luo
Software Engineer, Nylas

On Jun 17 2016, at 9:38 am, Sagar Sutar <sagar_s@nid.edu> wrote:
Hi Jackie,
While browsing Nylas themes, I stumbled upon your website and looked at your work. 
Great work on projects, nice to see your multidisciplinary interests :)

Thanks, 
Sagar Sutar
thesagarsutar.me
", - headers: { - "Date": "Fri, 17 Jun 2016 18:20:47 +0000", - "References": "<82y7eq1ipmadaxwcy6kr072bw-2147483647@mailer.nylas.com>", - "In-Reply-To": "<82y7eq1ipmadaxwcy6kr072bw-2147483647@mailer.nylas.com>", - "User-Agent": "NylasMailer/0.4", - "Message-Id": "", - "X-Inbox-Id": "cq08iqwatp00kai4qnff7zbaj-2147483647", - }, - from: [{ - name: "Jackie Luo", - email: "", - }], - to: [{ - name: "Sagar Sutar", - email: "", - }], - cc: [], - bcc: [], - headerMessageId: "", - snippet: "Sagar, Aw, glad to hear it! Thanks for getting in touch! Jackie Luo Software Engineer, Nylas", - setThread: (thread) => { - reply.thread = thread.id - }, -} diff --git a/packages/nylas-message-processor/spec/parsing-spec.js b/packages/nylas-message-processor/spec/parsing-spec.js deleted file mode 100644 index e8025c0b71..0000000000 --- a/packages/nylas-message-processor/spec/parsing-spec.js +++ /dev/null @@ -1,23 +0,0 @@ -const path = require('path') -const fs = require('fs') -const {processMessage} = require('../processors/parsing') - -const BASE_PATH = path.join(__dirname, 'fixtures') - - -it('parses the message correctly', (done) => { - const bodyPath = path.join(BASE_PATH, '1-99174-body.txt') - const headersPath = path.join(BASE_PATH, '1-99174-headers.txt') - const rawBody = fs.readFileSync(bodyPath, 'utf8') - const rawHeaders = fs.readFileSync(headersPath, 'utf8') - const message = { rawHeaders, rawBody } - const bodyPart = `

In _data/apps.yml:

` - - processMessage({message}).then((processed) => { - expect(processed.headers['in-reply-to']).toEqual('') - expect(processed.headerMessageId).toEqual('') - expect(processed.subject).toEqual('Re: [electron/electron.atom.io] Add Jasper app (#352)') - expect(processed.body.includes(bodyPart)).toBe(true) - done() - }) -}) diff --git a/packages/nylas-message-processor/spec/run.js b/packages/nylas-message-processor/spec/run.js deleted file mode 100644 index 4eb56830fd..0000000000 --- a/packages/nylas-message-processor/spec/run.js +++ /dev/null @@ -1,5 +0,0 @@ -import Jasmine from 'jasmine' - -const jasmine = new Jasmine() -jasmine.loadConfigFile('spec/support/jasmine.json') -jasmine.execute() diff --git a/packages/nylas-message-processor/spec/support/jasmine.json b/packages/nylas-message-processor/spec/support/jasmine.json deleted file mode 100644 index 6a2e779f43..0000000000 --- a/packages/nylas-message-processor/spec/support/jasmine.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "spec_dir": "spec", - "spec_files": [ - "**/*-spec.js" - ], - "helpers": [ ] -} diff --git a/packages/nylas-message-processor/spec/threading-spec.js b/packages/nylas-message-processor/spec/threading-spec.js deleted file mode 100644 index 5e0aaa3baf..0000000000 --- a/packages/nylas-message-processor/spec/threading-spec.js +++ /dev/null @@ -1,57 +0,0 @@ -const path = require('path') -const fs = require('fs') -const {DatabaseConnector} = require('nylas-core') -const {processMessage} = require('../processors/threading') - -const BASE_PATH = path.join(__dirname, 'fixtures') - - -it('adds the message to the thread', (done) => { - const {message, reply} = require(`${BASE_PATH}/thread`) - const accountId = 'a-1' - const mockDb = { - Thread: { - findAll: () => { - return Promise.resolve([ - { - id: 1, - subject: "Loved your work and interests", - messages: [message], - }]) - }, - find: () => { - return Promise.resolve(null) - }, - create: (thread) => { - thread.id = 1 - thread.addMessage = (message) => { - if (thread.messages) { - thread.messages.push(message.id) - } else { - thread.messages = [message.id] - } - } - return Promise.resolve(thread) - } - }, - Message: { - findAll: () => { - return Promise.resolve([message, reply]) - }, - find: () => { - return Promise.resolve(reply) - }, - create: (message) => { - message.setThread = (thread) => { - message.thread = thread.id - } - return Promise.resolve(message) - } - } - } - - processMessage({db: mockDb, message: reply, accountId}).then((processed) => { - expect(processed.thread).toBe(1) - done() - }) -}) diff --git a/packages/nylas-metrics/index.js b/packages/nylas-metrics/index.js deleted file mode 100644 index 1dbc70fa78..0000000000 --- a/packages/nylas-metrics/index.js +++ /dev/null @@ -1,54 +0,0 @@ -const {env: {NODE_ENV, SIGNALFX_TOKEN}, pid} = process -const os = require('os') -const signalfx = require('signalfx') - -let newrelicClient = null -let signalfxClient = null - -const MetricTypes = { - Gauge: 'gauges', - Counter: 'counters', - CumulativeCounter: 'cumulative_counters', -} -const shouldReport = NODE_ENV && NODE_ENV !== 'development' - - -const Metrics = { - - MetricTypes, - - startCapturing(name) { - if (!shouldReport) { return } - newrelicClient = require('newrelic') - signalfxClient = new signalfx.Ingest(SIGNALFX_TOKEN, { - dimensions: { - name, - host: os.hostname(), - pid: pid.toString(), - env: NODE_ENV, - }, - }) - }, - - reportError(error) { - if (!newrelicClient || !shouldReport) { return } - newrelicClient.noticeError(error) - }, - - reportMetric({name, value, type, dimensions = {}} = {}) { - if (!signalfxClient || !shouldReport) { return } - if (!name) { - throw new Error('Metrics.reportMetric requires a metric.name') - } - if (value == null) { - throw new Error('Metrics.reportMetric requires a metric.value') - } - if (!type) { - throw new Error('Metrics.reportMetric requires a metric.type from Metrics.MetricTypes') - } - const metric = {metric: name, value, timestamp: Date.now(), dimensions} - signalfxClient.send({[type]: [metric]}) - }, -} - -module.exports = Metrics diff --git a/packages/nylas-metrics/package.json b/packages/nylas-metrics/package.json deleted file mode 100644 index c355ee8e52..0000000000 --- a/packages/nylas-metrics/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "nylas-metrics", - "version": "0.0.1", - "description": "Metrics package", - "main": "index.js", - "dependencies": { - "newrelic": "1.34.0", - "signalfx": "3.0.1" - }, - "author": "Nylas", - "license": "ISC" -} diff --git a/packages/nylas-sync/app.js b/packages/nylas-sync/app.js deleted file mode 100644 index 7f32ad3554..0000000000 --- a/packages/nylas-sync/app.js +++ /dev/null @@ -1,49 +0,0 @@ -const Metrics = require('nylas-metrics') -Metrics.startCapturing('nylas-k2-sync') - -const {DatabaseConnector, Logger} = require('nylas-core') - -global.Metrics = Metrics -global.Logger = Logger.createLogger('nylas-k2-sync') - -const onUnhandledError = (err) => { - global.Logger.fatal(err, 'Unhandled error') - global.Metrics.reportError(err) -} -process.on('uncaughtException', onUnhandledError) -process.on('unhandledRejection', onUnhandledError) - - -const prepareEnvironmentInfo = (callback) => { - if (process.env.NODE_ENV === 'development') { - const os = require('os') - global.instanceId = os.hostname(); - callback(); - } else { - const request = require('request') - request('http://169.254.169.254/latest/meta-data/instance-id', (error, response, body) => { - global.instanceId = body; - callback(); - }); - } -} - -prepareEnvironmentInfo(() => { - const SyncProcessManager = require('./sync-process-manager') - const manager = new SyncProcessManager(); - - DatabaseConnector.forShared().then((db) => { - const {Account} = db; - Account.findAll().then((accounts) => { - if (accounts.length === 0) { - global.Logger.info(`Couldn't find any accounts to sync. Run this CURL command to auth one!`) - global.Logger.info(`curl -X POST -H "Content-Type: application/json" -d '{"email":"inboxapptest1@fastmail.fm", "name":"Ben Gotow", "provider":"imap", "settings":{"imap_username":"inboxapptest1@fastmail.fm","imap_host":"mail.messagingengine.com","imap_port":993,"smtp_host":"mail.messagingengine.com","smtp_port":0,"smtp_username":"inboxapptest1@fastmail.fm", "smtp_password":"trar2e","imap_password":"trar2e","ssl_required":true}}' "http://localhost:5100/auth?client_id=123"`) - } - manager.ensureAccountIDsInRedis(accounts.map(a => a.id)).then(() => { - manager.start(); - }) - }); - }); - - global.manager = manager; -}); diff --git a/packages/nylas-sync/imap/fetch-folder-list.js b/packages/nylas-sync/imap/fetch-folder-list.js deleted file mode 100644 index 3ca310c6ac..0000000000 --- a/packages/nylas-sync/imap/fetch-folder-list.js +++ /dev/null @@ -1,119 +0,0 @@ -const {Provider, PromiseUtils} = require('nylas-core'); - -const GMAIL_ROLES_WITH_FOLDERS = ['all', 'trash', 'junk']; - -class FetchFolderList { - constructor(provider, logger) { - this._provider = provider; - this._logger = logger; - if (!this._logger) { - throw new Error("FetchFolderList requires a logger") - } - } - - description() { - return `FetchFolderList`; - } - - _classForMailboxWithRole(role, {Folder, Label}) { - if (this._provider === Provider.Gmail) { - return GMAIL_ROLES_WITH_FOLDERS.includes(role) ? Folder : Label; - } - return Folder; - } - - _roleForMailbox(boxName, box) { - for (const attrib of (box.attribs || [])) { - const role = { - '\\Sent': 'sent', - '\\Drafts': 'drafts', - '\\Junk': 'junk', - '\\Trash': 'trash', - '\\All': 'all', - '\\Important': 'important', - '\\Flagged': 'flagged', - }[attrib]; - if (role) { - return role; - } - } - if (boxName.toLowerCase().trim() === 'inbox') { - return 'inbox'; - } - return null; - } - - _updateCategoriesWithBoxes(categories, boxes) { - const stack = []; - const created = []; - const next = []; - - Object.keys(boxes).forEach((boxName) => { - stack.push([boxName, boxes[boxName]]); - }); - - while (stack.length > 0) { - const [boxName, box] = stack.pop(); - if (!box.attribs) { - // Some boxes seem to come back as partial objects. Not sure why, but - // I also can't access them via openMailbox. Possible node-imap i8n issue? - continue; - } - - this._logger.info({ - box_name: boxName, - attributes: JSON.stringify(box.attribs), - }, `FetchFolderList: Box Information`) - - - if (box.children && box.attribs.includes('\\HasChildren')) { - Object.keys(box.children).forEach((subname) => { - stack.push([`${boxName}${box.delimiter}${subname}`, box.children[subname]]); - }); - } - - let category = categories.find((cat) => cat.name === boxName); - if (!category) { - const role = this._roleForMailbox(boxName, box); - const Klass = this._classForMailboxWithRole(role, this._db); - category = Klass.build({ - name: boxName, - accountId: this._db.accountId, - role: role, - }); - created.push(category); - } - next.push(category); - } - - // Todo: decide whether these are renames or deletes - const deleted = categories.filter(cat => !next.includes(cat)); - - return {next, created, deleted}; - } - - run(db, imap) { - this._db = db; - - return imap.getBoxes().then((boxes) => { - const {Folder, Label, sequelize} = this._db; - - return sequelize.transaction((transaction) => { - return PromiseUtils.props({ - folders: Folder.findAll({transaction}), - labels: Label.findAll({transaction}), - }).then(({folders, labels}) => { - const all = [].concat(folders, labels); - const {created, deleted} = this._updateCategoriesWithBoxes(all, boxes); - - let promises = [Promise.resolve()] - promises = promises.concat(created.map(cat => cat.save({transaction}))) - promises = promises.concat(deleted.map(cat => cat.destroy({transaction}))) - return Promise.all(promises) - }); - }); - }); - } -} - -module.exports = FetchFolderList; diff --git a/packages/nylas-sync/imap/fetch-messages-in-folder.js b/packages/nylas-sync/imap/fetch-messages-in-folder.js deleted file mode 100644 index 5715664bbd..0000000000 --- a/packages/nylas-sync/imap/fetch-messages-in-folder.js +++ /dev/null @@ -1,467 +0,0 @@ -const _ = require('underscore'); -const Imap = require('imap'); - -const {PromiseUtils, IMAPConnection, PubsubConnector} = require('nylas-core'); -const {Capabilities} = IMAPConnection; - -const MessageFlagAttributes = ['id', 'folderImapUID', 'unread', 'starred', 'folderImapXGMLabels'] - -const SHALLOW_SCAN_UID_COUNT = 1000; -const FETCH_MESSAGES_FIRST_COUNT = 100; -const FETCH_MESSAGES_COUNT = 200; - -class FetchMessagesInFolder { - constructor(category, options, logger) { - this._imap = null - this._box = null - this._db = null - this._category = category; - this._options = options; - this._logger = logger; - if (!this._logger) { - throw new Error("FetchMessagesInFolder requires a logger") - } - if (!this._category) { - throw new Error("FetchMessagesInFolder requires a category") - } - } - - description() { - return `FetchMessagesInFolder (${this._category.name} - ${this._category.id})\n Options: ${JSON.stringify(this._options)}`; - } - - _getLowerBoundUID(count) { - return count ? Math.max(1, this._box.uidnext - count) : 1; - } - - _recoverFromUIDInvalidity() { - // UID invalidity means the server has asked us to delete all the UIDs for - // this folder and start from scratch. Instead of deleting all the messages, - // we just remove the category ID and UID. We may re-assign the same message - // the same UID. Otherwise they're eventually garbage collected. - const {Message} = this._db; - return this._db.sequelize.transaction((transaction) => - Message.update({ - folderImapUID: null, - folderId: null, - }, { - transaction: transaction, - where: { - folderId: this._category.id, - }, - }) - ) - } - - _updateMessageAttributes(remoteUIDAttributes, localMessageAttributes) { - const {sequelize, Label} = this._db; - - const messageAttributesMap = {}; - for (const msg of localMessageAttributes) { - messageAttributesMap[msg.folderImapUID] = msg; - } - - const createdUIDs = []; - const flagChangeMessages = []; - - return Label.findAll().then((preloadedLabels) => { - Object.keys(remoteUIDAttributes).forEach((uid) => { - const msg = messageAttributesMap[uid]; - const attrs = remoteUIDAttributes[uid]; - - if (!msg) { - createdUIDs.push(uid); - return; - } - - const unread = !attrs.flags.includes('\\Seen'); - const starred = attrs.flags.includes('\\Flagged'); - const xGmLabels = attrs['x-gm-labels']; - const xGmLabelsJSON = xGmLabels ? JSON.stringify(xGmLabels) : null; - - if (msg.folderImapXGMLabels !== xGmLabelsJSON) { - msg.setLabelsFromXGM(xGmLabels, {Label, preloadedLabels}); - } - - if (msg.unread !== unread || msg.starred !== starred) { - msg.unread = unread; - msg.starred = starred; - flagChangeMessages.push(msg); - } - }) - - this._logger.info({ - flag_changes: flagChangeMessages.length, - }, `FetchMessagesInFolder: found flag changes`) - if (createdUIDs.length > 0) { - this._logger.info({ - new_messages: createdUIDs.length, - }, `FetchMessagesInFolder: found new messages. These will not be processed because we assume that they will be assigned uid = uidnext, and will be picked up in the next sync when we discover unseen messages.`) - } - - if (flagChangeMessages.length === 0) { - return Promise.resolve(); - } - - return sequelize.transaction((transaction) => - Promise.all(flagChangeMessages.map(m => m.save({ - fields: MessageFlagAttributes, - transaction, - }))) - ); - }); - } - - _removeDeletedMessages(remoteUIDAttributes, localMessageAttributes) { - const {Message} = this._db; - - const removedUIDs = localMessageAttributes - .filter(msg => !remoteUIDAttributes[msg.folderImapUID]) - .map(msg => msg.folderImapUID) - - this._logger.info({ - removed_messages: removedUIDs.length, - }, `FetchMessagesInFolder: found messages no longer in the folder`) - - if (removedUIDs.length === 0) { - return Promise.resolve(); - } - return this._db.sequelize.transaction((transaction) => - Message.update({ - folderImapUID: null, - folderId: null, - }, { - transaction, - where: { - folderId: this._category.id, - folderImapUID: removedUIDs, - }, - }) - ); - } - - _getDesiredMIMEParts(struct) { - const desired = []; - const available = []; - const unseen = [struct]; - while (unseen.length > 0) { - const part = unseen.shift(); - if (part instanceof Array) { - unseen.push(...part); - } else { - const mimetype = `${part.type}/${part.subtype}`; - available.push(mimetype); - if (['text/plain', 'text/html', 'application/pgp-encrypted'].includes(mimetype)) { - desired.push({id: part.partID, mimetype}); - } - } - } - - if (desired.length === 0) { - this._logger.warn({ - available_options: available.join(', '), - }, `FetchMessagesInFolder: Could not find good part`) - } - - return desired; - } - - _fetchMessagesAndQueueForProcessing(range) { - const uidsByPart = {}; - - return this._box.fetchEach(range, {struct: true}, ({attributes}) => { - const desiredParts = this._getDesiredMIMEParts(attributes.struct); - if (desiredParts.length === 0) { - return; - } - const key = JSON.stringify(desiredParts); - uidsByPart[key] = uidsByPart[key] || []; - uidsByPart[key].push(attributes.uid); - }) - .then(() => { - return PromiseUtils.each(Object.keys(uidsByPart), (key) => { - const uids = uidsByPart[key]; - const desiredParts = JSON.parse(key); - const bodies = ['HEADER'].concat(desiredParts.map(p => p.id)); - - this._logger.info({ - key, - num_messages: uids.length, - }, `FetchMessagesInFolder: Fetching parts for messages`) - - // note: the order of UIDs in the array doesn't matter, Gmail always - // returns them in ascending (oldest => newest) order. - - return this._box.fetchEach(uids, {bodies, struct: true}, (msg) => { - msg.body = {}; - for (const {id, mimetype} of desiredParts) { - msg.body[mimetype] = msg.parts[id]; - } - this._processMessage(msg); - }); - }); - }); - } - - _collectFilesFromStruct(message, struct) { - const {File} = this._db; - let collected = []; - - for (const part of struct) { - if (part.constructor === Array) { - collected = collected.concat(this._collectFilesFromStruct(message, part)); - } else if (part.type !== 'text' && part.disposition) { - // Only exposes partId for inline attachments - const partId = part.disposition.type === 'inline' ? part.partID : null; - const filename = part.disposition.params ? part.disposition.params.filename : null; - - collected.push(File.build({ - filename: filename, - partId: partId, - messageId: message.id, - contentType: `${part.type}/${part.subtype}`, - accountId: this._db.accountId, - size: part.size, - })); - } - } - - return collected; - } - - _processMessage({attributes, headers, body}) { - const {Message, Label, accountId} = this._db; - const hash = Message.hashForHeaders(headers); - - const parsedHeaders = Imap.parseHeader(headers); - for (const key of ['x-gm-thrid', 'x-gm-msgid', 'x-gm-labels']) { - parsedHeaders[key] = attributes[key]; - } - - const values = { - hash: hash, - accountId: this._db.accountId, - body: body['text/html'] || body['text/plain'] || body['application/pgp-encrypted'] || '', - snippet: body['text/plain'] ? body['text/plain'].substr(0, 255) : null, - unread: !attributes.flags.includes('\\Seen'), - starred: attributes.flags.includes('\\Flagged'), - date: attributes.date, - folderImapUID: attributes.uid, - folderId: this._category.id, - headers: parsedHeaders, - headerMessageId: parsedHeaders['message-id'] ? parsedHeaders['message-id'][0] : '', - subject: parsedHeaders.subject[0], - } - - let created = false; - - Message.find({where: {hash}}) - .then((existing) => { - created = !existing; - return existing ? existing.update(values) : Message.create(values); - }) - .then((message) => - message.setLabelsFromXGM(attributes['x-gm-labels'], {Label}).thenReturn(message) - ) - .then((message) => { - if (created) { - this._logger.info({ - message_id: message.id, - uid: attributes.uid, - }, `FetchMessagesInFolder: Created message`) - - const files = this._collectFilesFromStruct(message, attributes.struct); - if (files.length > 0) { - this._db.sequelize.transaction((transaction) => - Promise.all(files.map(f => f.save({transaction}))) - ) - } - - PubsubConnector.queueProcessMessage({accountId, messageId: message.id}); - } else { - this._logger.info({ - message_id: message.id, - uid: attributes.uid, - }, `FetchMessagesInFolder: Updated message`) - } - }) - - return null; - } - - _openMailboxAndEnsureValidity() { - return this._imap.openBox(this._category.name).then((box) => { - if (box.persistentUIDs === false) { - return Promise.reject(new Error("Mailbox does not support persistentUIDs.")) - } - - const lastUIDValidity = this._category.syncState.uidvalidity; - - if (lastUIDValidity && (box.uidvalidity !== lastUIDValidity)) { - this._logger.info({ - boxname: box.name, - categoryname: this._category.name, - remoteuidvalidity: box.uidvalidity, - localuidvalidity: lastUIDValidity, - }, `FetchMessagesInFolder: Recovering from UIDInvalidity`); - return this._recoverFromUIDInvalidity().thenReturn(box) - } - return Promise.resolve(box); - }); - } - - _fetchUnsyncedMessages() { - const savedSyncState = this._category.syncState; - const isFirstSync = savedSyncState.fetchedmax === undefined; - const boxUidnext = this._box.uidnext; - const boxUidvalidity = this._box.uidvalidity; - - const desiredRanges = []; - - this._logger.info({ - range: `${savedSyncState.fetchedmin}:${savedSyncState.fetchedmax}`, - }, `FetchMessagesInFolder: Fetching messages.`) - - // Todo: In the future, this is where logic should go that limits - // sync based on number of messages / age of messages. - - if (isFirstSync) { - const lowerbound = Math.max(1, boxUidnext - FETCH_MESSAGES_FIRST_COUNT); - desiredRanges.push({min: lowerbound, max: boxUidnext}) - } else { - if (savedSyncState.fetchedmax < boxUidnext) { - desiredRanges.push({min: savedSyncState.fetchedmax, max: boxUidnext}) - } else { - this._logger.info('FetchMessagesInFolder: fetchedmax == uidnext, nothing more recent to fetch.') - } - if (savedSyncState.fetchedmin > 1) { - const lowerbound = Math.max(1, savedSyncState.fetchedmin - FETCH_MESSAGES_COUNT); - desiredRanges.push({min: lowerbound, max: savedSyncState.fetchedmin}) - } else { - this._logger.info("FetchMessagesInFolder: fetchedmin == 1, nothing older to fetch.") - } - } - - return PromiseUtils.each(desiredRanges, ({min, max}) => { - this._logger.info({ - range: `${min}:${max}`, - }, `FetchMessagesInFolder: Fetching range`); - - return this._fetchMessagesAndQueueForProcessing(`${min}:${max}`).then(() => { - const {fetchedmin, fetchedmax} = this._category.syncState; - return this.updateFolderSyncState({ - fetchedmin: fetchedmin ? Math.min(fetchedmin, min) : min, - fetchedmax: fetchedmax ? Math.max(fetchedmax, max) : max, - uidvalidity: boxUidvalidity, - timeFetchedUnseen: Date.now(), - }); - }) - }) - .then(() => { - this._logger.info(`FetchMessagesInFolder: Fetching messages finished`); - }); - } - - _runScan() { - const {fetchedmin, fetchedmax} = this._category.syncState; - if ((fetchedmin === undefined) || (fetchedmax === undefined)) { - throw new Error("Unseen messages must be fetched at least once before the first update/delete scan.") - } - return this._shouldRunDeepScan() ? this._runDeepScan() : this._runShallowScan() - } - - _shouldRunDeepScan() { - const {timeDeepScan} = this._category.syncState; - return Date.now() - (timeDeepScan || 0) > this._options.deepFolderScan; - } - - _runShallowScan() { - const {highestmodseq} = this._category.syncState; - const nextHighestmodseq = this._box.highestmodseq; - - let shallowFetch = null; - - if (this._imap.serverSupports(Capabilities.Condstore)) { - this._logger.info(`FetchMessagesInFolder: Shallow attribute scan (using CONDSTORE)`) - if (nextHighestmodseq === highestmodseq) { - this._logger.info('FetchMessagesInFolder: highestmodseq matches, nothing more to fetch') - return Promise.resolve(); - } - shallowFetch = this._box.fetchUIDAttributes(`1:*`, {changedsince: highestmodseq}); - } else { - const range = `${this._getLowerBoundUID(SHALLOW_SCAN_UID_COUNT)}:*`; - this._logger.info({range}, `FetchMessagesInFolder: Shallow attribute scan`) - shallowFetch = this._box.fetchUIDAttributes(range); - } - - return shallowFetch - .then((remoteUIDAttributes) => ( - this._db.Message.findAll({ - where: {folderId: this._category.id}, - attributes: MessageFlagAttributes, - }) - .then((localMessageAttributes) => ( - this._updateMessageAttributes(remoteUIDAttributes, localMessageAttributes) - )) - .then(() => { - this._logger.info(`FetchMessagesInFolder: finished fetching changes to messages`); - return this.updateFolderSyncState({ - highestmodseq: nextHighestmodseq, - timeShallowScan: Date.now(), - }) - }) - )) - } - - _runDeepScan() { - const {Message} = this._db; - const {fetchedmin, fetchedmax} = this._category.syncState; - const range = `${fetchedmin}:${fetchedmax}`; - - this._logger.info({range}, `FetchMessagesInFolder: Deep attribute scan: fetching attributes in range`) - - return this._box.fetchUIDAttributes(range) - .then((remoteUIDAttributes) => { - return Message.findAll({ - where: {folderId: this._category.id}, - attributes: MessageFlagAttributes, - }) - .then((localMessageAttributes) => ( - PromiseUtils.props({ - updates: this._updateMessageAttributes(remoteUIDAttributes, localMessageAttributes), - deletes: this._removeDeletedMessages(remoteUIDAttributes, localMessageAttributes), - }) - )) - .then(() => { - this._logger.info(`FetchMessagesInFolder: Deep scan finished.`); - return this.updateFolderSyncState({ - highestmodseq: this._box.highestmodseq, - timeDeepScan: Date.now(), - timeShallowScan: Date.now(), - }) - }) - }); - } - - updateFolderSyncState(newState) { - if (_.isMatch(this._category.syncState, newState)) { - return Promise.resolve(); - } - this._category.syncState = Object.assign(this._category.syncState, newState); - return this._category.save(); - } - - run(db, imap) { - this._db = db; - this._imap = imap; - - return this._openMailboxAndEnsureValidity().then((box) => { - this._box = box - return this._fetchUnsyncedMessages().then(() => - this._runScan() - ) - }) - } -} - -module.exports = FetchMessagesInFolder; diff --git a/packages/nylas-sync/package.json b/packages/nylas-sync/package.json deleted file mode 100644 index 1ac52b60b9..0000000000 --- a/packages/nylas-sync/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "nylas-sync", - "version": "0.0.1", - "description": "Nylas Sync Engine", - "dependencies": { - "nylas-core": "0.x.x", - "nylas-message-processor": "0.x.x", - "nylas-metrics": "0.x.x", - "xoauth2": "1.1.0" - }, - "scripts": { - "start": "node app.js" - }, - "author": "Nylas", - "license": "ISC" -} diff --git a/packages/nylas-sync/sync-process-manager.js b/packages/nylas-sync/sync-process-manager.js deleted file mode 100644 index bf23f1a99b..0000000000 --- a/packages/nylas-sync/sync-process-manager.js +++ /dev/null @@ -1,223 +0,0 @@ -const SyncWorker = require('./sync-worker'); -const {PromiseUtils, DatabaseConnector, PubsubConnector, SchedulerUtils} = require(`nylas-core`) - -const IDENTITY = `${global.instanceId}-${process.pid}`; - -const { - ACCOUNTS_FOR, - ACCOUNTS_UNCLAIMED, - ACCOUNTS_CLAIMED_PREFIX, - HEARTBEAT_FOR, - HEARTBEAT_EXPIRES, - forEachAccountList, -} = SchedulerUtils; - -/* -Accounts ALWAYS exist in either `accounts:unclaimed` or an `accounts:{id}` list. -They are atomically moved between these sets as they are claimed and returned. - -Periodically, each worker in the pool looks at all the `accounts:{id}` lists. -For each list it finds, it checks for the existence of `heartbeat:{id}`, a key -that expires quickly if the sync process doesn't refresh it. - -If it does not find the key, it moves all of the accounts in the list back to -the unclaimed key. - -Sync processes only claim an account for a fixed period of time. This means that -an engineer can add new sync machines to the pool and the load across instances -will balance on it's own. It also means one bad instance will not permanently -disrupt sync for any accounts. (Eg: instance has faulty network connection.) - -Sync processes periodically claim accounts when they can find them, regardless -of how busy they are. A separate API (`/routes/monitoring`) allows CloudWatch -to decide whether to spin up instances or take them offline based on CPU/RAM -utilization across the pool. -*/ - -class SyncProcessManager { - constructor() { - this._workers = {}; - this._listenForSyncsClient = null; - this._exiting = false; - this._logger = global.Logger.child({identity: IDENTITY}) - } - - start() { - this._logger.info(`ProcessManager: Starting with ID`) - - this.unassignAccountsAssignedTo(IDENTITY).then(() => { - this.unassignAccountsMissingHeartbeats(); - this.update(); - }); - - setInterval(() => this.updateHeartbeat(), HEARTBEAT_EXPIRES / 5.0 * 1000); - this.updateHeartbeat(); - - process.on('SIGINT', () => this.onSigInt()); - process.on('SIGTERM', () => this.onSigInt()); - } - - updateHeartbeat() { - const key = HEARTBEAT_FOR(IDENTITY); - const client = PubsubConnector.broadcastClient(); - client.setAsync(key, Date.now()) - .then(() => client.expireAsync(key, HEARTBEAT_EXPIRES)) - .then(() => { - const accountsSyncing = Object.keys(this._workers).length - this._logger.info({ - accounts_syncing_count: accountsSyncing, - }, "ProcessManager: 💘") - global.Metrics.reportMetric({ - name: 'accounts_syncing_count', - value: accountsSyncing, - type: global.Metrics.MetricTypes.Gauge, - }) - }) - } - - onSigInt() { - this._logger.info(`ProcessManager: Exiting...`) - this._exiting = true; - - this.unassignAccountsAssignedTo(IDENTITY).then(() => - PubsubConnector.broadcastClient().delAsync(ACCOUNTS_FOR(IDENTITY)).then(() => - PubsubConnector.broadcastClient().delAsync(HEARTBEAT_FOR(IDENTITY)) - ) - ).finally(() => { - process.exit(1); - }); - } - - ensureAccountIDsInRedis(accountIds) { - const client = PubsubConnector.broadcastClient(); - - let unseenIds = [].concat(accountIds); - - this._logger.info("ProcessManager: Starting scan for accountIds in database that are not present in Redis.") - - return forEachAccountList((foundProcessIdentity, foundIds) => { - unseenIds = unseenIds.filter((a) => !foundIds.includes(`${a}`)) - }) - .finally(() => { - if (unseenIds.length === 0) { - return; - } - this._logger.info({ - unseen_ids: unseenIds.join(', '), - channel: ACCOUNTS_UNCLAIMED, - }, `ProcessManager: Adding unseen account IDs to ACCOUNTS_UNCLAIMED channel.`) - unseenIds.map((id) => client.lpushAsync(ACCOUNTS_UNCLAIMED, id)); - }); - } - - unassignAccountsMissingHeartbeats() { - const client = PubsubConnector.broadcastClient(); - - this._logger.info("ProcessManager: Starting unassignment for processes missing heartbeats.") - - PromiseUtils.each(client.keysAsync(`${ACCOUNTS_CLAIMED_PREFIX}*`), (key) => { - const id = key.replace(ACCOUNTS_CLAIMED_PREFIX, ''); - return client.existsAsync(HEARTBEAT_FOR(id)).then((exists) => - (exists ? Promise.resolve() : this.unassignAccountsAssignedTo(id)) - ) - }) - .finally(() => { - const delay = HEARTBEAT_EXPIRES * 1000; - setTimeout(() => this.unassignAccountsMissingHeartbeats(), delay); - }); - } - - unassignAccountsAssignedTo(identity) { - const src = ACCOUNTS_FOR(identity); - const dst = ACCOUNTS_UNCLAIMED; - - const unassignOne = (count) => - PubsubConnector.broadcastClient().rpoplpushAsync(src, dst).then((val) => - (val ? unassignOne(count + 1) : Promise.resolve(count)) - ) - - return unassignOne(0).then((returned) => { - this._logger.info({ - returned, - assigned_to: identity, - }, `ProcessManager: Returned accounts`) - }); - } - - update() { - this._logger.info(`ProcessManager: Searching for an unclaimed account to sync.`) - - this.acceptUnclaimedAccount().finally(() => { - if (this._exiting) { - return; - } - this.update(); - }); - } - - acceptUnclaimedAccount() { - if (!this._waitForAccountClient) { - this._waitForAccountClient = PubsubConnector.buildClient(); - } - - const src = ACCOUNTS_UNCLAIMED; - const dst = ACCOUNTS_FOR(IDENTITY); - - return this._waitForAccountClient.brpoplpushAsync(src, dst, 10000).then((accountId) => { - if (!accountId) { - return Promise.resolve(); - } - this.addWorkerForAccountId(accountId); - - // If we've added an account, wait a second before asking for another one. - // Spacing them out is probably healthy. - return PromiseUtils.sleep(2000); - }); - } - - addWorkerForAccountId(accountId) { - DatabaseConnector.forShared().then(({Account}) => - Account.find({where: {id: accountId}}).then((account) => { - if (!account) { - return Promise.reject(new Error("Could not find account")); - } - return DatabaseConnector.forAccount(accountId).then((db) => { - if (this._exiting || this._workers[accountId]) { - return Promise.reject(new Error("Exiting or local worker already exists")); - } - this._workers[account.id] = new SyncWorker(account, db, () => { - this.removeWorkerForAccountId(accountId) - }); - return Promise.resolve(); - }); - }) - ) - .then(() => { - this._logger.info({account_id: accountId}, `ProcessManager: Claiming Account Succeeded`) - }) - .catch((err) => { - this._logger.error({account_id: accountId, reason: err.message}, `ProcessManager: Claiming Account Failed`) - }); - } - - removeWorkerForAccountId(accountId) { - const src = ACCOUNTS_FOR(IDENTITY); - const dst = ACCOUNTS_UNCLAIMED; - - return PubsubConnector.broadcastClient().lremAsync(src, 1, accountId).then((didRemove) => { - this._workers[accountId] = null; - if (didRemove) { - return PubsubConnector.broadcastClient().rpushAsync(dst, accountId) - } - return Promise.reject(new Error("Did not own account.")); - }) - .then(() => { - this._logger.info({account_id: accountId}, `ProcessManager: Relinquishing Account Succeeded`) - }) - .catch((err) => { - this._logger.error({account_id: accountId, reason: err.message}, `ProcessManager: Relinquishing Account Failed`) - }); - } -} - -module.exports = SyncProcessManager; diff --git a/packages/nylas-sync/sync-utils.js b/packages/nylas-sync/sync-utils.js deleted file mode 100644 index 82d5b6cdfe..0000000000 --- a/packages/nylas-sync/sync-utils.js +++ /dev/null @@ -1,11 +0,0 @@ - -function jsonError(error) { - return { - message: error.message, - stack: error.stack ? error.stack.split('\n') : [], - } -} - -module.exports = { - jsonError, -} diff --git a/packages/nylas-sync/sync-worker.js b/packages/nylas-sync/sync-worker.js deleted file mode 100644 index e5fbdc0ff6..0000000000 --- a/packages/nylas-sync/sync-worker.js +++ /dev/null @@ -1,288 +0,0 @@ -const { - SchedulerUtils, - IMAPConnection, - PubsubConnector, - DatabaseConnector, - MessageTypes, - Errors, - PromiseUtils, -} = require('nylas-core'); -const { - jsonError, -} = require('./sync-utils') - -const {CLAIM_DURATION} = SchedulerUtils; - -const FetchFolderList = require('./imap/fetch-folder-list') -const FetchMessagesInFolder = require('./imap/fetch-messages-in-folder') -const SyncbackTaskFactory = require('./syncback-task-factory') - - -class SyncWorker { - constructor(account, db, onExpired) { - this._db = db; - this._conn = null; - this._account = account; - this._startTime = Date.now(); - this._lastSyncTime = null; - this._onExpired = onExpired; - this._logger = global.Logger.forAccount(account) - - this._syncTimer = null; - this._destroyed = false; - - this.syncNow({reason: 'Initial'}); - - this._onMessage = this._onMessage.bind(this); - this._listener = PubsubConnector.observeAccount(account.id).subscribe(this._onMessage) - } - - cleanup() { - clearTimeout(this._syncTimer); - this._syncTimer = null; - this._destroyed = true; - this._listener.dispose(); - this.closeConnection() - } - - closeConnection() { - if (this._conn) { - this._conn.end(); - } - } - - _onMessage(msg) { - const {type} = JSON.parse(msg); - switch (type) { - case MessageTypes.ACCOUNT_CREATED: - // No other processing currently required for account creation - break; - case MessageTypes.ACCOUNT_UPDATED: - this._onAccountUpdated(); - break; - case MessageTypes.ACCOUNT_DELETED: - this.cleanup(); - this._onExpired(); - break; - case MessageTypes.SYNCBACK_REQUESTED: - this.syncNow({reason: 'Syncback Action Queued'}); - break; - default: - this._logger.error({message: msg}, 'SyncWorker: Invalid message') - } - } - - _onAccountUpdated() { - const syncingNow = !this.isWaitingForNextSync() - const syncingJustFinished = (Date.now() - this._lastSyncTime < 5000); - - if (syncingNow || syncingJustFinished) { - return; - } - - this._getAccount().then((account) => { - this._account = account; - this.syncNow({reason: 'Account Modification'}); - }) - .catch((err) => { - this._logger.error(err, 'SyncWorker: Error getting account for update') - }) - } - - _onConnectionIdleUpdate() { - if (!this.isWaitingForNextSync()) { - return; - } - this.syncNow({reason: 'IMAP IDLE Fired'}); - } - - _getAccount() { - return DatabaseConnector.forShared().then(({Account}) => - Account.find({where: {id: this._account.id}}) - ); - } - - _getIdleFolder() { - return this._db.Folder.find({where: {role: ['all', 'inbox']}}) - } - - ensureConnection() { - if (this._conn) { - return this._conn.connect(); - } - const settings = this._account.connectionSettings; - const credentials = this._account.decryptedCredentials(); - - if (!settings || !settings.imap_host) { - return Promise.reject(new Error("ensureConnection: There are no IMAP connection settings for this account.")) - } - if (!credentials) { - return Promise.reject(new Error("ensureConnection: There are no IMAP connection credentials for this account.")) - } - - const conn = new IMAPConnection({ - db: this._db, - settings: Object.assign({}, settings, credentials), - logger: this._logger, - }); - - conn.on('mail', () => { - this._onConnectionIdleUpdate(); - }) - conn.on('update', () => { - this._onConnectionIdleUpdate(); - }) - conn.on('queue-empty', () => { - }); - - this._conn = conn; - return this._conn.connect(); - } - - syncbackMessageActions() { - const where = {where: {status: "NEW"}, limit: 100}; - return PromiseUtils.each((this._db.SyncbackRequest.findAll(where) - .map((req) => SyncbackTaskFactory.create(this._account, req))), - this.runSyncbackTask.bind(this)) - } - - runSyncbackTask(task) { - const syncbackRequest = task.syncbackRequestObject() - return this._conn.runOperation(task) - .then(() => { - syncbackRequest.status = "SUCCEEDED" - }) - .catch((error) => { - syncbackRequest.error = error - syncbackRequest.status = "FAILED" - }) - .finally(() => syncbackRequest.save()) - } - - syncAllCategories() { - const {Folder} = this._db; - const {folderSyncOptions} = this._account.syncPolicy; - - return Folder.findAll().then((categories) => { - const priority = ['inbox', 'all', 'drafts', 'sent', 'spam', 'trash'].reverse(); - const categoriesToSync = categories.sort((a, b) => - (priority.indexOf(a.role) - priority.indexOf(b.role)) * -1 - ) - - return Promise.all(categoriesToSync.map((cat) => - this._conn.runOperation(new FetchMessagesInFolder(cat, folderSyncOptions, this._logger)) - )) - }); - } - - syncNow({reason} = {}) { - clearTimeout(this._syncTimer); - this._syncTimer = null; - - this._account.reload().then(() => { - if (!process.env.SYNC_AFTER_ERRORS && this._account.errored()) { - this._logger.info(`SyncWorker: Account is in error state - Skipping sync`) - return Promise.resolve(); - } - this._logger.info({reason}, `SyncWorker: Account sync started`) - - return this._account.update({syncError: null}) - .then(() => this.ensureConnection()) - .then(() => this.syncbackMessageActions()) - .then(() => this._conn.runOperation(new FetchFolderList(this._account.provider, this._logger))) - .then(() => this.syncAllCategories()) - .then(() => this.onSyncDidComplete()) - .catch((error) => this.onSyncError(error)) - }) - .finally(() => { - this._lastSyncTime = Date.now() - this.scheduleNextSync() - }) - } - - onSyncError(error) { - this.closeConnection() - - this._logger.error(error, `SyncWorker: Error while syncing account`) - global.Metrics.reportMetric({ - name: 'sync_error', - value: 1, - type: global.Metrics.Counter, - }) - - // Continue to retry if it was a network error - if (error instanceof Errors.RetryableError) { - return Promise.resolve() - } - - this._account.syncError = jsonError(error) - return this._account.save() - } - - onSyncDidComplete() { - const {afterSync} = this._account.syncPolicy; - const now = Date.now(); - - if (!this._account.firstSyncCompletion) { - this._account.firstSyncCompletion = now; - } - - const syncGraphTimeLength = 60 * 30; // 30 minutes, should be the same as SyncGraph.config.timeLength - let lastSyncCompletions = [].concat(this._account.lastSyncCompletions) - lastSyncCompletions = [now, ...lastSyncCompletions] - while (now - lastSyncCompletions[lastSyncCompletions.length - 1] > 1000 * syncGraphTimeLength) { - lastSyncCompletions.pop(); - } - - this._account.lastSyncCompletions = lastSyncCompletions - this._account.save() - - this._logger.info('Syncworker: Completed sync cycle') - - if (afterSync === 'idle') { - return this._getIdleFolder() - .then((idleFolder) => this._conn.openBox(idleFolder.name)) - .then(() => this._logger.info('SyncWorker: Idling on inbox category')) - } - - if (afterSync === 'close') { - this._logger.info('SyncWorker: Closing connection'); - this.closeConnection() - return Promise.resolve() - } - - this._logger.error({after_sync: afterSync}, `SyncWorker.onSyncDidComplete: Unknown afterSync behavior`) - throw new Error('SyncWorker.onSyncDidComplete: Unknown afterSync behavior') - } - - isWaitingForNextSync() { - return this._syncTimer != null; - } - - scheduleNextSync() { - if (Date.now() - this._startTime > CLAIM_DURATION) { - this._logger.info("SyncWorker: - Has held account for more than CLAIM_DURATION, returning to pool."); - this.cleanup(); - this._onExpired(); - return; - } - - SchedulerUtils.checkIfAccountIsActive(this._account.id).then((active) => { - const {intervals} = this._account.syncPolicy; - const interval = active ? intervals.active : intervals.inactive; - - if (interval) { - const target = this._lastSyncTime + interval; - this._logger.info({ - is_active: active, - next_sync: new Date(target).toLocaleString(), - }, `SyncWorker: Next sync scheduled`); - this._syncTimer = setTimeout(() => { - this.syncNow({reason: 'Scheduled'}); - }, target - Date.now()); - } - }); - } -} - -module.exports = SyncWorker; diff --git a/packages/nylas-sync/syncback-task-factory.js b/packages/nylas-sync/syncback-task-factory.js deleted file mode 100644 index e11176accb..0000000000 --- a/packages/nylas-sync/syncback-task-factory.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Given a `SyncbackRequestObject` it creates the appropriate syncback task. - * - */ -class SyncbackTaskFactory { - static create(account, syncbackRequest) { - let Task = null; - switch (syncbackRequest.type) { - case "MoveToFolder": - Task = require('./syncback_tasks/move-to-folder.imap'); break; - case "MarkThreadAsRead": - Task = require('./syncback_tasks/mark-thread-as-read.imap'); break; - case "MarkThreadAsUnread": - Task = require('./syncback_tasks/mark-thread-as-unread.imap'); break; - case "StarThread": - Task = require('./syncback_tasks/star-thread.imap'); break; - case "UnstarThread": - Task = require('./syncback_tasks/unstar-thread.imap'); break; - case "MoveMessageToFolder": - Task = require('./syncback_tasks/move-message-to-folder.imap'); break; - case "MarkMessageAsRead": - Task = require('./syncback_tasks/mark-message-as-read.imap'); break; - case "MarkMessageAsUnread": - Task = require('./syncback_tasks/mark-message-as-unread.imap'); break; - case "StarMessage": - Task = require('./syncback_tasks/star-message.imap'); break; - case "UnstarMessage": - Task = require('./syncback_tasks/unstar-message.imap'); break; - case "CreateFolder": - Task = require('./syncback_tasks/create-folder.imap'); break; - case "RenameFolder": - Task = require('./syncback_tasks/rename-folder.imap'); break; - case "DeleteFolder": - Task = require('./syncback_tasks/delete-folder.imap'); break; - default: - throw new Error(`Invalid Task Type: ${syncbackRequest.type}`) - } - return new Task(account, syncbackRequest) - } -} - -module.exports = SyncbackTaskFactory diff --git a/packages/nylas-sync/syncback_tasks/create-folder.imap.js b/packages/nylas-sync/syncback_tasks/create-folder.imap.js deleted file mode 100644 index c5ecec491b..0000000000 --- a/packages/nylas-sync/syncback_tasks/create-folder.imap.js +++ /dev/null @@ -1,13 +0,0 @@ -const SyncbackTask = require('./syncback-task') - -class CreateFolderIMAP extends SyncbackTask { - description() { - return `CreateFolder`; - } - - run(db, imap) { - const folderName = this.syncbackRequestObject().props.displayName - return imap.addBox(folderName) - } -} -module.exports = CreateFolderIMAP diff --git a/packages/nylas-sync/syncback_tasks/delete-folder.imap.js b/packages/nylas-sync/syncback_tasks/delete-folder.imap.js deleted file mode 100644 index 005d6c9afd..0000000000 --- a/packages/nylas-sync/syncback_tasks/delete-folder.imap.js +++ /dev/null @@ -1,15 +0,0 @@ -const SyncbackTask = require('./syncback-task') - -class DeleteFolderIMAP extends SyncbackTask { - description() { - return `DeleteFolder`; - } - - run(db, imap) { - const folderId = this.syncbackRequestObject().props.id - return db.Folder.findById(folderId).then((folder) => { - return imap.delBox(folder.name); - }) - } -} -module.exports = DeleteFolderIMAP diff --git a/packages/nylas-sync/syncback_tasks/mark-message-as-read.imap.js b/packages/nylas-sync/syncback_tasks/mark-message-as-read.imap.js deleted file mode 100644 index 26dfb6faff..0000000000 --- a/packages/nylas-sync/syncback_tasks/mark-message-as-read.imap.js +++ /dev/null @@ -1,18 +0,0 @@ -const SyncbackTask = require('./syncback-task') -const TaskHelpers = require('./task-helpers') - -class MarkMessageAsReadIMAP extends SyncbackTask { - description() { - return `MarkMessageAsRead`; - } - - run(db, imap) { - const messageId = this.syncbackRequestObject().props.messageId - - return TaskHelpers.openMessageBox({messageId, db, imap}) - .then(({box, message}) => { - return box.addFlags(message.folderImapUID, 'SEEN') - }) - } -} -module.exports = MarkMessageAsReadIMAP; diff --git a/packages/nylas-sync/syncback_tasks/mark-message-as-unread.imap.js b/packages/nylas-sync/syncback_tasks/mark-message-as-unread.imap.js deleted file mode 100644 index f7f7a34848..0000000000 --- a/packages/nylas-sync/syncback_tasks/mark-message-as-unread.imap.js +++ /dev/null @@ -1,18 +0,0 @@ -const SyncbackTask = require('./syncback-task') -const TaskHelpers = require('./task-helpers') - -class MarkMessageAsUnreadIMAP extends SyncbackTask { - description() { - return `MarkMessageAsUnread`; - } - - run(db, imap) { - const messageId = this.syncbackRequestObject().props.messageId - - return TaskHelpers.openMessageBox({messageId, db, imap}) - .then(({box, message}) => { - return box.delFlags(message.folderImapUID, 'SEEN') - }) - } -} -module.exports = MarkMessageAsUnreadIMAP; diff --git a/packages/nylas-sync/syncback_tasks/mark-thread-as-read.imap.js b/packages/nylas-sync/syncback_tasks/mark-thread-as-read.imap.js deleted file mode 100644 index c74ac249c1..0000000000 --- a/packages/nylas-sync/syncback_tasks/mark-thread-as-read.imap.js +++ /dev/null @@ -1,19 +0,0 @@ -const SyncbackTask = require('./syncback-task') -const TaskHelpers = require('./task-helpers') - -class MarkThreadAsRead extends SyncbackTask { - description() { - return `MarkThreadAsRead`; - } - - run(db, imap) { - const threadId = this.syncbackRequestObject().props.threadId - - const eachMsg = ({message, box}) => { - return box.addFlags(message.folderImapUID, 'SEEN') - } - - return TaskHelpers.forEachMessageInThread({threadId, db, imap, callback: eachMsg}) - } -} -module.exports = MarkThreadAsRead; diff --git a/packages/nylas-sync/syncback_tasks/mark-thread-as-unread.imap.js b/packages/nylas-sync/syncback_tasks/mark-thread-as-unread.imap.js deleted file mode 100644 index 72ae4068a4..0000000000 --- a/packages/nylas-sync/syncback_tasks/mark-thread-as-unread.imap.js +++ /dev/null @@ -1,19 +0,0 @@ -const SyncbackTask = require('./syncback-task') -const TaskHelpers = require('./task-helpers') - -class MarkThreadAsUnread extends SyncbackTask { - description() { - return `MarkThreadAsUnread`; - } - - run(db, imap) { - const threadId = this.syncbackRequestObject().props.threadId - - const eachMsg = ({message, box}) => { - return box.delFlags(message.folderImapUID, 'SEEN') - } - - return TaskHelpers.forEachMessageInThread({threadId, db, imap, callback: eachMsg}) - } -} -module.exports = MarkThreadAsUnread; diff --git a/packages/nylas-sync/syncback_tasks/move-message-to-folder.imap.js b/packages/nylas-sync/syncback_tasks/move-message-to-folder.imap.js deleted file mode 100644 index 4f342d0476..0000000000 --- a/packages/nylas-sync/syncback_tasks/move-message-to-folder.imap.js +++ /dev/null @@ -1,21 +0,0 @@ -const SyncbackTask = require('./syncback-task') -const TaskHelpers = require('./task-helpers') - -class MoveMessageToFolderIMAP extends SyncbackTask { - description() { - return `MoveMessageToFolder`; - } - - run(db, imap) { - const messageId = this.syncbackRequestObject().props.messageId - const toFolderId = this.syncbackRequestObject().props.folderId - - return TaskHelpers.openMessageBox({messageId, db, imap}) - .then(({box, message}) => { - return db.Folder.findById(toFolderId).then((newFolder) => { - return box.moveFromBox(message.folderImapUID, newFolder.name) - }) - }) - } -} -module.exports = MoveMessageToFolderIMAP diff --git a/packages/nylas-sync/syncback_tasks/move-to-folder.imap.js b/packages/nylas-sync/syncback_tasks/move-to-folder.imap.js deleted file mode 100644 index 989d534c74..0000000000 --- a/packages/nylas-sync/syncback_tasks/move-to-folder.imap.js +++ /dev/null @@ -1,22 +0,0 @@ -const SyncbackTask = require('./syncback-task') -const TaskHelpers = require('./task-helpers') - -class MoveToFolderIMAP extends SyncbackTask { - description() { - return `MoveToFolder`; - } - - run(db, imap) { - const threadId = this.syncbackRequestObject().props.threadId - const toFolderId = this.syncbackRequestObject().props.folderId - - const eachMsg = ({message, box}) => { - return db.Folder.findById(toFolderId).then((category) => { - return box.moveFromBox(message.folderImapUID, category.name) - }) - } - - return TaskHelpers.forEachMessageInThread({threadId, db, imap, callback: eachMsg}) - } -} -module.exports = MoveToFolderIMAP diff --git a/packages/nylas-sync/syncback_tasks/rename-folder.imap.js b/packages/nylas-sync/syncback_tasks/rename-folder.imap.js deleted file mode 100644 index 003e12bb38..0000000000 --- a/packages/nylas-sync/syncback_tasks/rename-folder.imap.js +++ /dev/null @@ -1,16 +0,0 @@ -const SyncbackTask = require('./syncback-task') - -class RenameFolderIMAP extends SyncbackTask { - description() { - return `RenameFolder`; - } - - run(db, imap) { - const folderId = this.syncbackRequestObject().props.id - const newFolderName = this.syncbackRequestObject().props.displayName - return db.Folder.findById(folderId).then((folder) => { - return imap.renameBox(folder.name, newFolderName); - }) - } -} -module.exports = RenameFolderIMAP diff --git a/packages/nylas-sync/syncback_tasks/star-message.imap.js b/packages/nylas-sync/syncback_tasks/star-message.imap.js deleted file mode 100644 index 8ae5a4f596..0000000000 --- a/packages/nylas-sync/syncback_tasks/star-message.imap.js +++ /dev/null @@ -1,18 +0,0 @@ -const SyncbackTask = require('./syncback-task') -const TaskHelpers = require('./task-helpers') - -class StarMessageIMAP extends SyncbackTask { - description() { - return `StarMessage`; - } - - run(db, imap) { - const messageId = this.syncbackRequestObject().props.messageId - - return TaskHelpers.openMessageBox({messageId, db, imap}) - .then(({box, message}) => { - return box.addFlags(message.folderImapUID, 'FLAGGED') - }) - } -} -module.exports = StarMessageIMAP; diff --git a/packages/nylas-sync/syncback_tasks/star-thread.imap.js b/packages/nylas-sync/syncback_tasks/star-thread.imap.js deleted file mode 100644 index 71083f96ff..0000000000 --- a/packages/nylas-sync/syncback_tasks/star-thread.imap.js +++ /dev/null @@ -1,19 +0,0 @@ -const SyncbackTask = require('./syncback-task') -const TaskHelpers = require('./task-helpers') - -class StarThread extends SyncbackTask { - description() { - return `StarThread`; - } - - run(db, imap) { - const threadId = this.syncbackRequestObject().props.threadId - - const eachMsg = ({message, box}) => { - return box.addFlags(message.folderImapUID, 'FLAGGED') - } - - return TaskHelpers.forEachMessageInThread({threadId, db, imap, callback: eachMsg}) - } -} -module.exports = StarThread; diff --git a/packages/nylas-sync/syncback_tasks/syncback-task.js b/packages/nylas-sync/syncback_tasks/syncback-task.js deleted file mode 100644 index 6379bc11b6..0000000000 --- a/packages/nylas-sync/syncback_tasks/syncback-task.js +++ /dev/null @@ -1,19 +0,0 @@ -class SyncbackTask { - constructor(account, syncbackRequest) { - this._account = account; - this._syncbackRequest = syncbackRequest; - } - - syncbackRequestObject() { - return this._syncbackRequest; - } - - description() { - throw new Error("Must return a description") - } - - run() { - throw new Error("Must implement a run method") - } -} -module.exports = SyncbackTask diff --git a/packages/nylas-sync/syncback_tasks/task-helpers.js b/packages/nylas-sync/syncback_tasks/task-helpers.js deleted file mode 100644 index d16ccf1876..0000000000 --- a/packages/nylas-sync/syncback_tasks/task-helpers.js +++ /dev/null @@ -1,37 +0,0 @@ -const _ = require('underscore') -const {PromiseUtils} = require('nylas-core') - -const TaskHelpers = { - messagesForThreadByFolder: function messagesForThreadByFolder(db, threadId) { - return Promise.resolve(db.Thread.findById(threadId).then((thread) => { - return thread.getMessages() - })).then((messages) => { - return _.groupBy(messages, "folderId") - }) - }, - - forEachMessageInThread: function forEachMessageInThread({threadId, db, imap, callback}) { - return TaskHelpers.messagesForThreadByFolder(db, threadId) - .then((msgsInCategories) => { - const cids = Object.keys(msgsInCategories); - return PromiseUtils.each(db.Folder.findAll({where: {id: cids}}), (category) => - imap.openBox(category.name, {readOnly: false}).then((box) => - Promise.all(msgsInCategories[category.id].map((message) => - callback({message, category, box}) - )).then(() => box.closeBox()) - ) - ) - }) - }, - - openMessageBox: function openMessageBox({messageId, db, imap}) { - return Promise.resolve(db.Message.findById(messageId).then((message) => { - return db.Folder.findById(message.folderId).then((category) => { - return imap.openBox(category.name).then((box) => { - return Promise.resolve({box, message}) - }) - }) - })) - }, -} -module.exports = TaskHelpers diff --git a/packages/nylas-sync/syncback_tasks/unstar-message.imap.js b/packages/nylas-sync/syncback_tasks/unstar-message.imap.js deleted file mode 100644 index c4f9cbd850..0000000000 --- a/packages/nylas-sync/syncback_tasks/unstar-message.imap.js +++ /dev/null @@ -1,18 +0,0 @@ -const SyncbackTask = require('./syncback-task') -const TaskHelpers = require('./task-helpers') - -class UnstarMessageIMAP extends SyncbackTask { - description() { - return `UnstarMessage`; - } - - run(db, imap) { - const messageId = this.syncbackRequestObject().props.messageId - - return TaskHelpers.openMessageBox({messageId, db, imap}) - .then(({box, message}) => { - return box.delFlags(message.folderImapUID, 'FLAGGED') - }) - } -} -module.exports = UnstarMessageIMAP; diff --git a/packages/nylas-sync/syncback_tasks/unstar-thread.imap.js b/packages/nylas-sync/syncback_tasks/unstar-thread.imap.js deleted file mode 100644 index c1652318c7..0000000000 --- a/packages/nylas-sync/syncback_tasks/unstar-thread.imap.js +++ /dev/null @@ -1,19 +0,0 @@ -const SyncbackTask = require('./syncback-task') -const TaskHelpers = require('./task-helpers') - -class UnstarThread extends SyncbackTask { - description() { - return `UnstarThread`; - } - - run(db, imap) { - const threadId = this.syncbackRequestObject().props.threadId - - const eachMsg = ({message, box}) => { - return box.delFlags(message.folderImapUID, 'FLAGGED') - } - - return TaskHelpers.forEachMessageInThread({threadId, db, imap, callback: eachMsg}) - } -} -module.exports = UnstarThread; diff --git a/pm2-dev.yml b/pm2-dev.yml deleted file mode 100644 index 5401f25647..0000000000 --- a/pm2-dev.yml +++ /dev/null @@ -1,38 +0,0 @@ -apps: - - script : packages/nylas-api/app.js - watch : ["packages"] - name : api - env : - PORT: 5100 - ALLOW_LIST_ACCOUNTS : true - DB_ENCRYPTION_ALGORITHM : "aes-256-ctr" - DB_ENCRYPTION_PASSWORD : "d6F3Efeq" - GMAIL_CLIENT_ID : "271342407743-nibas08fua1itr1utq9qjladbkv3esdm.apps.googleusercontent.com" - GMAIL_CLIENT_SECRET : "WhmxErj-ei6vJXLocNhBbfBF" - GMAIL_REDIRECT_URL : "http://localhost:5100/auth/gmail/oauthcallback" - NODE_ENV: 'development' - - script : packages/nylas-sync/app.js - watch : ["packages"] - name : sync - env : - SYNC_AFTER_ERRORS : true - DB_ENCRYPTION_ALGORITHM : "aes-256-ctr" - DB_ENCRYPTION_PASSWORD : "d6F3Efeq" - NODE_ENV: 'development' - - script : packages/nylas-dashboard/app.js - watch : ["packages"] - name : dashboard - env : - PORT: 5101 - DB_ENCRYPTION_ALGORITHM : "aes-256-ctr" - DB_ENCRYPTION_PASSWORD : "d6F3Efeq" - NODE_ENV: 'development' - - script : packages/nylas-message-processor/app.js - watch : ["packages"] - name : processor - env : - DB_ENCRYPTION_ALGORITHM : "aes-256-ctr" - DB_ENCRYPTION_PASSWORD : "d6F3Efeq" - NODE_ENV: 'development' - - script : redis-server - name : redis diff --git a/pm2-prod-api.yml b/pm2-prod-api.yml deleted file mode 100644 index e6f679b569..0000000000 --- a/pm2-prod-api.yml +++ /dev/null @@ -1,7 +0,0 @@ -apps: - - script : packages/nylas-api/app.js - name : api - instances: 0 - exec_mode: cluster - env : - PORT: 5100 diff --git a/pm2-prod-dashboard.yml b/pm2-prod-dashboard.yml deleted file mode 100644 index 5f5e4234ea..0000000000 --- a/pm2-prod-dashboard.yml +++ /dev/null @@ -1,7 +0,0 @@ -apps: - - script : packages/nylas-dashboard/app.js - name : dashboard - instances: 0 - exec_mode: cluster - env : - PORT: 5100 diff --git a/pm2-prod-sync.yml b/pm2-prod-sync.yml deleted file mode 100644 index 1bcb4cfb44..0000000000 --- a/pm2-prod-sync.yml +++ /dev/null @@ -1,9 +0,0 @@ -apps: - - script : packages/nylas-sync/app.js - name : sync - instances: 0 - exec_mode: fork - - script : packages/nylas-message-processor/app.js - name : processor - instances: 0 - exec_mode: fork diff --git a/scripts/benchmark-initial-sync.sh b/scripts/benchmark-initial-sync.sh new file mode 100644 index 0000000000..d7f414ea81 --- /dev/null +++ b/scripts/benchmark-initial-sync.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +set -e + +function print_help { + OUTPUT=$(cat <<- EOM + + A script to benchmark the initial sync performance of Nylas Mail. To use, + simply run the script after authing whatever accounts you wish to measure + in your development version of Nylas Mail. The benchmarking script will + clear all of the data except for your accounts and open and close Nylas + Mail several times, printing out the number of messages synced after each + iteration. + ) + echo "$OUTPUT" +} + +if [[ $1 == '-h' || $1 == '--help' ]] +then + print_help + exit 0 +fi + +CWD="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +NYLAS_DIR="$HOME/.nylas-bench" +EDGEHILL_DB="$NYLAS_DIR/edgehill.db" +TIME_LIMIT=120 +ITERS=5 + +for i in `seq 1 $ITERS` +do + bash $CWD/drop-data-except-accounts.sh > /dev/null + + (npm run benchmark &> /dev/null &) + + sleep $TIME_LIMIT + + ELECTRON_PID=`ps aux | grep "Electron packages/client-app" | grep -v grep | awk '{print $2}'` + kill -9 $ELECTRON_PID + + MESSAGE_COUNT=`sqlite3 $EDGEHILL_DB 'SELECT COUNT(*) FROM Message'` + echo "Synced Messages: $MESSAGE_COUNT" + + # Sometimes it takes a while to shutdown + while [[ $ELECTRON_PID != '' ]] + do + sleep 1 + ELECTRON_PID=`ps aux | grep "Electron packages/client-app" | grep -v grep | awk '{print $2}'` + done +done diff --git a/scripts/benchmark-new-commits.sh b/scripts/benchmark-new-commits.sh new file mode 100644 index 0000000000..21cb98d121 --- /dev/null +++ b/scripts/benchmark-new-commits.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +set -e + +function get_next_commit() { + local LAST_COMMIT=$1 + local NEXT_COMMIT=$(git log master ^$LAST_COMMIT --ancestry-path --pretty=oneline | cut -d" " -f1 | tail -n 1) + echo "$NEXT_COMMIT" +} + +BENCHMARK_RESULTS_DIR="$HOME/.benchmark_results" +mkdir -p $BENCHMARK_RESULTS_DIR + +CWD="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd $CWD/.. + +git checkout -q master +git pull -q --rebase + +LAST_COMMIT=$(cat $BENCHMARK_RESULTS_DIR/last_commit) +NEXT_COMMIT=$(get_next_commit $LAST_COMMIT) + +while [[ $NEXT_COMMIT != '' ]] +do + echo $NEXT_COMMIT + git checkout -q $NEXT_COMMIT + bash $CWD/benchmark-initial-sync.sh > "$BENCHMARK_RESULTS_DIR/$NEXT_COMMIT-results.txt" + echo "$NEXT_COMMIT" > "$BENCHMARK_RESULTS_DIR/last_commit" + NEXT_COMMIT=$(get_next_commit $NEXT_COMMIT) +done diff --git a/scripts/daily.js b/scripts/daily.js new file mode 100644 index 0000000000..8aa92d0680 --- /dev/null +++ b/scripts/daily.js @@ -0,0 +1,238 @@ +#!/usr/bin/env babel-node +const childProcess = require('child_process') +const path = require('path') +const mkdirp = require('mkdirp') +const semver = require('semver') +const program = require('commander') + +const TMP_DIR = path.join(__dirname, '..', 'tmp') + +async function spawn(cmd, args, opts = {}) { + return new Promise((resolve, reject) => { + const env = Object.assign({}, process.env, opts.env || {}) + delete opts.env + const options = Object.assign({stdio: 'inherit', env}, opts); + const proc = childProcess.spawn(cmd, args, options) + proc.on("error", reject) + proc.on("exit", resolve) + }) +} + +function exec(cmd, opts = {}) { + return new Promise((resolve, reject) => { + childProcess.exec(cmd, opts, (err, stdout) => { + if (err) { + return reject(err) + } + return resolve(stdout) + }) + }) +} + +function git(subCmd, opts = {}) { + const optsString = Object.keys(opts).reduce((prev, key) => { + const optVal = opts[key] + if (optVal == null) { + return key.length > 1 ? `${prev} --${key}` : `${prev} -${key}` + } + return key.length > 1 ? `${prev} --${key}=${optVal}` : `${prev} -${key} ${optVal}` + }, '') + return exec(`git ${subCmd} ${optsString}`, {cwd: './'}) +} + +async function prependToFile(filepath, string) { + await exec(`echo "${string}" > ${TMP_DIR}/tmpfile`) + await exec(`cat ${filepath} >> ${TMP_DIR}/tmpfile`) + await exec(`mv ${TMP_DIR}/tmpfile ${filepath}`) +} + +async function sliceFileLines(filepath, idx) { + await exec(`tail -n +${1 + idx} ${filepath} > ${TMP_DIR}/tmpfile`) + await exec(`mv ${TMP_DIR}/tmpfile ${filepath}`) +} + +async function updateChangelogFile(changelogString) { + mkdirp.sync(TMP_DIR) + await sliceFileLines('./packages/client-app/CHANGELOG.md', 2) + await prependToFile('./packages/client-app/CHANGELOG.md', changelogString) +} + +function getFormattedLogs(mainLog) { + const formattedMainLog = ( + mainLog + .filter(line => line.length > 0) + .filter(line => !/^bump/i.test(line) && !/changelog/i.test(line)) + .map(line => ` + ${line.replace('*', '\\*')}`) + .join('\n') + ) + return `${formattedMainLog}\n` +} + +function getChangelogHeader(nextVersion) { + const date = new Date().toLocaleDateString() + return ( + `# Nylas Mail Changelog + +### ${nextVersion} (${date}) + +` + ) +} + +function validateArgs(args) { + if (args.editChangelog && !process.env.EDITOR) { + throw new Error(`You can't edit the changelog without a default EDITOR in your env`) + } +} + +// TODO add progress indicators with ora +// TODO add options +// --update-daily-channel +// --notify +// --quiet +async function main(args) { + validateArgs(args) + + // Pull latest changes + try { + await git(`checkout master`) + await git(`pull --rebase`) + } catch (err) { + console.error(err) + process.exit(1) + } + + const pkg = require('../packages/client-app/package.json') //eslint-disable-line + const currentVersion = pkg.version + const nextVersion = semver.inc(currentVersion, 'patch') + + // Make sure working directory is clean + try { + await exec('git diff --exit-code && git diff --cached --exit-code') + } catch (err) { + console.error('Git working directory is not clean!') + process.exit(1) + } + + // Make sure there is a diff to build + let mainLog = ''; + try { + mainLog = (await git(`log ${currentVersion}..master --format='%s'`)).split('\n') + if (mainLog.length <= 1) { + console.error(`There are no changes to build since ${currentVersion}`) + process.exit(1) + } + } catch (err) { + console.error(err) + process.exit(1) + } + + // Update CHANGELOG + try { + const commitLogSinceLatestVersion = await getFormattedLogs(mainLog) + const changelogHeader = getChangelogHeader(nextVersion) + const changelogString = `${changelogHeader}${commitLogSinceLatestVersion}` + await updateChangelogFile(changelogString) + console.log(changelogString) + } catch (err) { + console.error('Could not update changelog file') + console.error(err) + process.exit(1) + } + + // Allow editing + if (args.editChangelog) { + try { + await spawn(process.env.EDITOR, ['./packages/client-app/CHANGELOG.md'], {stdio: 'inherit'}) + } catch (err) { + console.error('Error editing CHANGELOG.md') + console.error(err) + process.exit(1) + } + } + + // Bump patch version in package.json + try { + await exec('npm --no-git-tag-version version patch', {cwd: 'packages/client-app'}) + } catch (err) { + console.error('Could not bump version in package.json') + console.error(err) + process.exit(1) + } + + if (args.noCommit) { + return + } + + // Commit changes + try { + await git('add .') + await git(`commit -m 'bump(version): ${nextVersion}'`) + } catch (err) { + console.error('Could not commit changes') + console.error(err) + process.exit(1) + } + + if (args.noTag) { + return + } + + // Tag commit + try { + await git(`tag ${nextVersion}`) + } catch (err) { + console.error('Could not tag commit') + console.error(err) + process.exit(1) + } + + if (args.noPush) { + return + } + + // Push changes + try { + await git(`push origin master --tags`) + } catch (err) { + console.error('Could not tag commit') + console.error(err) + process.exit(1) + } + + // Build locally. This should only be used when building from our in-office + // coffee machine mac mini + if (args.build) { + try { + await spawn('git', ['clean', '-xdf']) + await spawn('cp', ['-r', '../n1-keys-and-certificates', 'packages/client-app/build/resources/certs']) + await spawn('npm', ['install'], {env: {INSTALL_TARGET: 'client'}}) + await spawn('npm', ['run', 'build-client'], {env: {SIGN_BUILD: true}}) + await spawn('codesign', ['--verify', '--deep', '--verbose=2', '"packages/client-app/dist/Nylas Mail-darwin-x64/Nylas Mail.app"']) + await spawn('npm', ['run', 'upload-client']) + } catch (err) { + console.error('Errored while running build') + console.error(err) + process.exit(1) + } + + // TODO Update `daily` channel + + // TODO send out notification email + } + + console.log('Done!') +} + +program +.version('0.0.1') +.usage('[options]') +.description('This script will bump the version in package.json, edit the changelog with the latest\n git log (for easier editing), commit and tag the changes, and push to Github to trigger\n a build') +.option('--edit-changelog', 'Open your $EDITOR to edit CHANGELOG before commiting version bump.') +.option('--no-commit', 'Wether to commit changes to CHANGELOG.md and package.json') +.option('--no-tag', 'Wether to tag the version bump commit (no-op if --no-commit is used)') +.option('--no-push', 'Wether to push changes to the Github remote') +.option('--build', 'Wether to build the app locally. This should only be used when building from our in-office Mac Mini by the coffee machine') +.parse(process.argv) + +main(program) diff --git a/scripts/drop-data-except-accounts.sh b/scripts/drop-data-except-accounts.sh new file mode 100644 index 0000000000..990cbf95ce --- /dev/null +++ b/scripts/drop-data-except-accounts.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +set -e + +CWD="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +NYLAS_DIR="$HOME/.nylas-bench" +TRUNCATE_TABLES=" +Account +AccountPluginMetadata +Calendar +Category +Contact +ContactSearch +Event +EventSearch +File +Message +MessageBody +MessagePluginMetadata +ProviderSyncbackRequest +Thread +ThreadCategory +ThreadContact +ThreadCounts +ThreadPluginMetadata +ThreadSearch" +SQLITE_DIR=$CWD/sqlite +SQLITE_BIN=$SQLITE_DIR/sqlite3 +SQLITE_SRC_DIR="$SQLITE_DIR/sqlite-amalgamation-3170000" + +# Build the sqlite3 amalgamation if necessary because we need FTS5 support. +if [[ ! -e "$SQLITE_BIN" ]] +then + mkdir -p $SQLITE_DIR + curl -s "https://www.sqlite.org/2017/sqlite-amalgamation-3170000.zip" > "$SQLITE_DIR/sqlite-amalgamation.zip" + unzip -o -d $SQLITE_DIR "$SQLITE_DIR/sqlite-amalgamation.zip" + clang -DSQLITE_DISABLE_INTRINSIC -DSQLITE_ENABLE_FTS5 "$SQLITE_SRC_DIR/sqlite3.c" "$SQLITE_SRC_DIR/shell.c" -o $SQLITE_BIN +fi + +rm -f $NYLAS_DIR/a-*.sqlite + +EDGEHILL_DB=$NYLAS_DIR/edgehill.db + +for TABLE in $TRUNCATE_TABLES +do + COMMAND="DELETE FROM $TABLE" + $SQLITE_BIN $EDGEHILL_DB "DELETE FROM $TABLE" +done + +$SQLITE_BIN $EDGEHILL_DB 'DELETE FROM JSONBlob WHERE client_id != "NylasID"' diff --git a/scripts/postinstall.es6 b/scripts/postinstall.es6 new file mode 100644 index 0000000000..e63d4d2c9f --- /dev/null +++ b/scripts/postinstall.es6 @@ -0,0 +1,196 @@ +import fs from 'fs-plus' +import path from 'path' +import childProcess from 'child_process' + +const TARGET_ALL = 'all' +const TARGET_CLOUD = 'cloud' +const TARGET_CLIENT = 'client' + +async function spawn(cmd, args, opts = {}) { + return new Promise((resolve, reject) => { + const options = Object.assign({stdio: 'inherit'}, opts); + const proc = childProcess.spawn(cmd, args, options) + proc.on("error", reject) + proc.on("exit", resolve) + }) +} + +function unlinkIfExistsSync(p) { + try { + if (fs.lstatSync(p)) { + fs.removeSync(p); + } + } catch (err) { + return + } +} + +function copyErrorLoggerExtensions(privateDir) { + const from = path.join(privateDir, 'src') + const to = path.resolve(path.join('packages', 'client-app', 'src')) + unlinkIfExistsSync(path.join(to, 'error-logger-extensions')); + fs.copySync(from, to); +} + +function installClientSyncPackage() { + console.log("\n---> Linking client-sync") + // link client-sync + const clientSyncDir = path.resolve(path.join('packages', 'client-sync')); + const destination = path.resolve(path.join('packages', 'client-app', 'internal_packages', 'client-sync')); + unlinkIfExistsSync(destination); + fs.symlinkSync(clientSyncDir, destination, 'dir'); +} + +function installPrivateResources() { + console.log("\n---> Linking private plugins") + const privateDir = path.resolve(path.join('packages', 'client-private-plugins')) + if (!fs.existsSync(privateDir)) { + console.log("\n---> No client app to link. Moving on") + return; + } + + copyErrorLoggerExtensions(privateDir) + + // link private plugins + for (const plugin of fs.readdirSync(path.join(privateDir, 'packages'))) { + const from = path.resolve(path.join(privateDir, 'packages', plugin)); + const to = path.resolve(path.join('packages', 'client-app', 'internal_packages', plugin)); + unlinkIfExistsSync(to); + fs.symlinkSync(from, to, 'dir'); + } +} + +async function lernaBootstrap(installTarget) { + console.log("\n---> Installing packages"); + const lernaCmd = process.platform === 'win32' ? 'lerna.cmd' : 'lerna'; + const args = ["bootstrap"] + switch (installTarget) { + case TARGET_CLIENT: + args.push(`--ignore='cloud-*'`) + break + case TARGET_CLOUD: + args.push(`--ignore='client-*'`) + break + default: + break + } + await spawn(path.join('node_modules', '.bin', lernaCmd), args) +} + +const npmEnvs = { + system: process.env, + apm: Object.assign({}, process.env, { + NPM_CONFIG_TARGET: '0.10.40', + }), + electron: Object.assign({}, process.env, { + NPM_CONFIG_TARGET: '1.4.15', + NPM_CONFIG_ARCH: process.arch, + NPM_CONFIG_TARGET_ARCH: process.arch, + NPM_CONFIG_DISTURL: 'https://atom.io/download/electron', + NPM_CONFIG_RUNTIME: 'electron', + NPM_CONFIG_BUILD_FROM_SOURCE: true, + }), +}; + +async function npm(cmd, options) { + const {cwd, env} = Object.assign({cwd: '.', env: 'system'}, options); + const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm' + await spawn(npmCmd, [cmd], { + cwd: path.resolve(__dirname, '..', cwd), + env: npmEnvs[env], + }) +} + +async function electronRebuild() { + if (!fs.existsSync(path.join("packages", "client-app", "apm"))) { + console.log("\n---> No client app to rebuild. Moving on") + return; + } + await npm('install', { + cwd: path.join('packages', 'client-app', 'apm'), + env: 'apm', + }) + await npm('rebuild', { + cwd: path.join('packages', 'client-app'), + env: 'electron', + }) +} + +const getJasmineDir = (packageName) => path.resolve( + path.join('packages', packageName, 'spec', 'jasmine') +) +const getJasmineConfigPath = (packageName) => path.resolve( + path.join(getJasmineDir(packageName), 'config.json') +) + +function linkJasmineConfigs() { + console.log("\n---> Linking Jasmine configs"); + const linkToPackages = ['cloud-api', 'cloud-core', 'cloud-workers'] + const from = getJasmineConfigPath('isomorphic-core') + for (const packageName of linkToPackages) { + const packageDir = path.join('packages', packageName) + if (!fs.existsSync(packageDir)) { + console.log("\n---> No cloud packages to link. Moving on") + return + } + + const jasmineDir = getJasmineDir(packageName) + if (!fs.existsSync(jasmineDir)) { + fs.mkdirSync(jasmineDir) + } + const to = getJasmineConfigPath(packageName) + unlinkIfExistsSync(to) + fs.symlinkSync(from, to, 'file') + } +} + +function linkIsomorphicCoreSpecs() { + console.log("\n---> Linking isomorphic-core specs to client-app specs") + const from = path.resolve(path.join('packages', 'isomorphic-core', 'spec')) + const to = path.resolve(path.join('packages', 'client-app', 'spec', 'isomorphic-core')) + unlinkIfExistsSync(to) + fs.symlinkSync(from, to, 'dir') +} + +function getInstallTarget() { + const {INSTALL_TARGET} = process.env + if (!INSTALL_TARGET) { + return TARGET_ALL + } + if (![TARGET_ALL, TARGET_CLIENT, TARGET_CLOUD].includes(INSTALL_TARGET)) { + throw new Error(`postinstall: INSTALL_TARGET must be one of client, cloud, or all. It was set to ${INSTALL_TARGET}`) + } + return INSTALL_TARGET +} + +async function main() { + try { + const installTarget = getInstallTarget() + console.log(`\n---> Installing for target ${installTarget}`); + + if ([TARGET_ALL, TARGET_CLIENT].includes(installTarget)) { + installPrivateResources() + installClientSyncPackage() + } + + await lernaBootstrap(installTarget); + + if ([TARGET_ALL, TARGET_CLIENT].includes(installTarget)) { + if (process.platform === "darwin") { + // Given that `lerna bootstrap` does not install optional dependencies, we + // need to manually run `npm install` inside `client-app` so + // `node-mac-notifier` get's correctly installed and included in the build + // See https://github.com/lerna/lerna/issues/121 + console.log("\n---> Reinstalling client-app dependencies to include optional dependencies"); + await npm('install', {cwd: 'packages/client-app'}) + } + await electronRebuild(); + linkJasmineConfigs(); + linkIsomorphicCoreSpecs(); + } + } catch (err) { + console.error(err); + process.exit(1); + } +} +main() diff --git a/scripts/requirements.txt b/scripts/requirements.txt new file mode 100644 index 0000000000..0964f01c71 --- /dev/null +++ b/scripts/requirements.txt @@ -0,0 +1,12 @@ +appdirs==1.4.3 +gspread==0.6.2 +httplib2==0.10.3 +oauth2client==4.0.0 +packaging==16.8 +pyasn1==0.2.3 +pyasn1-modules==0.0.8 +pyparsing==2.2.0 +python-dateutil==2.6.0 +requests==2.13.0 +rsa==3.4.2 +six==1.10.0 diff --git a/scripts/run-once-per-day.sh b/scripts/run-once-per-day.sh new file mode 100644 index 0000000000..0fdfaccd24 --- /dev/null +++ b/scripts/run-once-per-day.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# This just checks the time every 60 seconds to see if it should run the benchmarks. +# This is a hack to work around cron's/automator's inability to start GUI apps +# using `npm start`. + +CWD="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +BENCHMARK_SCRIPT=$CWD/benchmark-new-commits.sh +UPLOAD_SCRIPT=$CWD/upload-benchmark-data.py +BENCHMARK_RESULTS_DIR=$HOME/.benchmark_results +TARGET_TIME="$1" + +if [[ $# != 1 ]] +then + echo "Usage: run-once-per-day.sh '23:00'" + exit 1 +fi + +while [[ true ]] +do + CURRENT_TIME=$(date +"%H:%M") + + if [[ $CURRENT_TIME = $TARGET_TIME ]] + then + /bin/bash $BENCHMARK_SCRIPT + python $UPLOAD_SCRIPT $BENCHMARK_RESULTS_DIR + fi + sleep 60 +done + diff --git a/scripts/upload-benchmark-data.py b/scripts/upload-benchmark-data.py new file mode 100644 index 0000000000..3606acf152 --- /dev/null +++ b/scripts/upload-benchmark-data.py @@ -0,0 +1,70 @@ +import os +import re +import sys +import subprocess + +from dateutil.parser import parse as parse_datestr +from glob import glob + +import gspread + +from oauth2client.service_account import ServiceAccountCredentials + +scope = ['https://spreadsheets.google.com/feeds'] + + +def usage(): + print "./scripts/upload-benchmark-data.py " + + +def anymean(filename): + output = subprocess.check_output(['./scripts/toolbox/any_mean.py', filename]) + if output == '': + return 0.0, 0.0 + + # e.g. 'Synced Messages: 77.00 +-0.00' + synced_messages, confidence_interval = re.match('^Synced Messages: ([0-9.]+) (\+-[0-9.]+)$', output).groups() + return synced_messages, confidence_interval + + +def update_spreadsheet(datadir): + credentials = ServiceAccountCredentials.from_json_keyfile_name('client_secret.json', scope) + gc = gspread.authorize(credentials) + worksheet = gc.open("Nylas Mail Benchmarks").sheet1 + + filenames = [] + for filename in glob('{datadir}/*-results.txt'.format(datadir=datadir)): + gitsha = re.match('^(.*)-results.txt$', os.path.basename(filename)).groups(0)[0] + formatted_datetime = subprocess.check_output(['git', 'show', '-s', '--format=%ci', gitsha]) + parsed_datetime = parse_datestr(formatted_datetime) + filenames.append((filename, gitsha, parsed_datetime)) + + new_data = [] + for filename, gitsha, parsed_datetime in sorted(filenames, key=lambda t: t[2]): + synced_messages, confidence_interval = anymean(filename) + row = (parsed_datetime.strftime("%Y-%m-%d %H:%M:%S"), gitsha, synced_messages, confidence_interval) + new_data.append(row) + print row + + # TODO: might want to use the batch upload api in order to not run into rate-limits + for i, new_row in enumerate(new_data): + row_num = i+2 + existing_row = worksheet.range('A{row_num}:D{row_num}'.format(row_num=row_num)) + for j, cell in enumerate(existing_row): + col_num = j+1 + cell.value = new_row[j] + print "updating cell {row_num}:{col_num} with {val}".format(row_num=row_num, col_num=col_num, val=cell.value) + worksheet.update_cells(existing_row) + + +def main(): + if len(sys.argv) != 2: + usage() + return 1 + + datadir = sys.argv[1] + update_spreadsheet(datadir) + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/test_accounts.txt b/test_accounts.txt deleted file mode 100644 index 0049f9ad65..0000000000 --- a/test_accounts.txt +++ /dev/null @@ -1,16 +0,0 @@ -Yahoo: -curl -k -X POST -H "Content-Type: application/json" -d '{"email":"benbitdiddle1861@yahoo.com", "name":"Ben Gotow", "provider":"imap", "settings":{"imap_username":"benbitdiddle1861@yahoo.com",imap_host":"imap.mail.yahoo.com","imap_port":993,"smtp_host":"smtp.mail.yahoo.com","smtp_port":0,"smtp_username":"benbitdiddle1861@yahoo.com", "smtp_password":"EverybodyLovesIMAPv4","imap_password":"EverybodyLovesIMAPv4","ssl_required":true}}' "http://localhost:5100/auth?client_id=123" -curl -k -X POST -H "Content-Type: application/json" -d '{"email":"cypresstest@yahoo.com", "name":"Ben Gotow", "provider":"imap", "settings":{"imap_username":"cypresstest@yahoo.com","imap_host":"imap.mail.yahoo.com","imap_port":993,"smtp_host":"smtp.mail.yahoo.com","smtp_port":0,"smtp_username":"cypresstest@yahoo.com", "smtp_password":"IHate2Gmail","imap_password":"IHate2Gmail","ssl_required":true}}' "http://localhost:5100.com/auth?client_id=123" - -Aol: -curl -k -X POST -H "Content-Type: application/json" -d '{"email":"benbitdit@aol.com", "name":"Ben Gotow", "provider":"imap", "settings":{"imap_username":"benbitdit@aol.com","imap_host":"imap.aol.com","imap_port":993,"smtp_host":"smtp.aol.com","smtp_port":0,"smtp_username":"benbitdit@aol.com", "smtp_password":"IHate2Gmail","imap_password":"IHate2Gmail","ssl_required":true}}' "http://localhost:5100/auth?client_id=123" - -iCloud: -curl -k -X POST -H "Content-Type: application/json" -d '{"email":"inbox.systemtest@icloud.com", "name":"Ben Gotow", "provider":"imap", "settings":{"imap_username":"inbox.systemtest@icloud.com","imap_host":"imap.mail.me.com","imap_port":993,"smtp_host":"smtp.mail.me.com","smtp_port":0,"smtp_username":"inbox.systemtest@icloud.com", "smtp_password":"iHate2Gmai","imap_password":"iHate2Gmai","ssl_required":true}}' "http://localhost:5100/auth?client_id=123" -curl -k -X POST -H "Content-Type: application/json" -d '{"email":"inbox.watchdog@icloud.com", "name":"Ben Gotow", "provider":"imap", "settings":{"imap_username":"inbox.watchdog@icloud.com","imap_host":"imap.mail.me.com","imap_port":993,"smtp_host":"smtp.mail.me.com","smtp_port":0,"smtp_username":"inbox.watchdog@icloud.com", "smtp_password":"iHate2Gmai","imap_password":"iHate2Gmai","ssl_required":true}}' "http://localhost:5100/auth?client_id=123" -curl -k -X POST -H "Content-Type: application/json" -d '{"email":"benbitdiddle@icloud.com", "name":"Ben Gotow", "provider":"imap", "settings":{"imap_username":"benbitdiddle@icloud.com","imap_host":"imap.mail.me.com","imap_port":993,"smtp_host":"smtp.mail.me.com","smtp_port":0,"smtp_username":"benbitdiddle@icloud.com", "smtp_password":"ih4teIMAP","imap_password":"ih4teIMAP","ssl_required":true}}' "http://localhost:5100/auth?client_id=123" - -Others: -curl -k -X POST -H "Content-Type: application/json" -d '{"email":"nylastest@runbox.com", "name":"Ben Gotow", "provider":"imap", "settings":{"imap_username":"nylastest","imap_host":"mail.runbox.com","imap_port":993,"smtp_host":"mail.runbox.com","smtp_port":0,"smtp_username":"nylastest", "smtp_password":"IHate2Gmail!","imap_password":"IHate2Gmail!","ssl_required":true}}' "http://localhost:5100/auth?client_id=123" -curl -k -X POST -H "Content-Type: application/json" -d '{"email":"securemail@defendcyber.space", "name":"Ben Gotow", "provider":"imap", "settings":{"imap_username":"securemail@defendcyber.space","imap_host":"imap.secureserver.net","imap_port":143,"smtp_host":"smtpout.secureserver.net","smtp_port":25,"smtp_username":"securemail@defendcyber.space", "smtp_password":"IHate2Gmail!","imap_password":"IHate2Gmail!","ssl_required":false}}' "http://localhost:5100/auth?client_id=123" -curl -k -X POST -H "Content-Type: application/json" -d '{"email":"inboxapptest4@gmail.com", "name":"Ben Gotow", "provider":"imap", "settings":{"imap_username":"inboxapptest4@gmail.com","imap_host":"imap.gmail.com","imap_port":993,"smtp_host":"smtp.gmail.com","smtp_port":465,"smtp_username":"inboxapptest4@gmail.com", "smtp_password":"ihategmail","imap_password":"ihategmail","ssl_required":true}}' "http://localhost:5100/auth?client_id=123"