├── .editorconfig ├── .github └── stale.yml ├── .gitignore ├── .snyk ├── .travis.yml ├── LICENSE ├── README.md ├── coffeelint.json ├── gulpfile.coffee ├── main.js ├── package-lock.json ├── package.json ├── resources ├── custom.css ├── desktop.ejs └── linux │ ├── after-install.sh │ ├── after-remove.sh │ └── app.desktop ├── src ├── icons │ ├── icon-read_016.png │ ├── icon-read_016_blue.png │ ├── icon-read_020.png │ ├── icon-read_020_blue.png │ ├── icon-read_032.png │ ├── icon-read_032_blue.png │ ├── icon-read_128.png │ ├── icon-read_128_blue.png │ ├── icon-unread_016.png │ ├── icon-unread_020.png │ ├── icon-unread_032.png │ ├── icon-unread_128.png │ ├── icon.icns │ ├── icon.ico │ ├── icon.png │ ├── icon_016.png │ ├── icon_032.png │ ├── icon_048.png │ ├── icon_128.png │ ├── icon_256.png │ ├── icon_512.png │ ├── icons.iconsproj │ ├── osx-icon-read-Template_016.png │ ├── osx-icon-read-Template_032.png │ ├── osx-icon-unread-Template_016.png │ ├── osx-icon-unread-Template_032.png │ ├── yak.png │ ├── yak.svg │ ├── yak_icon.icns │ ├── yakyak-logo-read.svg │ ├── yakyak-logo-read_blue.svg │ ├── yakyak-logo.ai │ ├── yakyak-logo.svg │ ├── yakyak-logo.svg.png │ ├── yakyak-osx-read.svg │ └── yakyak-osx-unread.svg ├── locales │ ├── ar.json │ ├── bn.json │ ├── br.json │ ├── cs.json │ ├── da.json │ ├── de.json │ ├── en.json │ ├── en.json.template │ ├── es.json │ ├── et.json │ ├── fi.json │ ├── fr.json │ ├── he.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pl.json │ ├── pt.json │ ├── pt_BR.json │ ├── ro.json │ ├── ru.json │ ├── sk.json │ ├── sl_SI.json │ ├── sv_SE.json │ ├── ta.json │ ├── te.json │ ├── tr.json │ ├── uk_UA.json │ ├── zh_CN.json │ └── zh_TW.json ├── login.coffee ├── main.coffee ├── media │ └── new_message.ogg ├── seqreq.coffee └── ui │ ├── about.html │ ├── app.coffee │ ├── css │ ├── manifest.less │ ├── material-icons │ │ ├── MaterialIcons-Regular.eot │ │ ├── MaterialIcons-Regular.ijmap │ │ ├── MaterialIcons-Regular.svg │ │ ├── MaterialIcons-Regular.ttf │ │ ├── MaterialIcons-Regular.woff │ │ ├── MaterialIcons-Regular.woff2 │ │ ├── README.md │ │ └── material-icons.css │ ├── noto-color-emoji │ │ ├── LICENSE_OFL.txt │ │ ├── NotoColorEmoji.ttf │ │ └── noto-color-emoji.less │ ├── roboto-fontface │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── bower.json │ │ ├── css │ │ │ ├── mixins.less │ │ │ ├── mixins.scss │ │ │ ├── roboto-fontface-black-italic.less │ │ │ ├── roboto-fontface-black-italic.scss │ │ │ ├── roboto-fontface-black.less │ │ │ ├── roboto-fontface-black.scss │ │ │ ├── roboto-fontface-bold-italic.less │ │ │ ├── roboto-fontface-bold-italic.scss │ │ │ ├── roboto-fontface-bold.less │ │ │ ├── roboto-fontface-bold.scss │ │ │ ├── roboto-fontface-light-italic.less │ │ │ ├── roboto-fontface-light-italic.scss │ │ │ ├── roboto-fontface-light.less │ │ │ ├── roboto-fontface-light.scss │ │ │ ├── roboto-fontface-medium-italic.less │ │ │ ├── roboto-fontface-medium-italic.scss │ │ │ ├── roboto-fontface-medium.less │ │ │ ├── roboto-fontface-medium.scss │ │ │ ├── roboto-fontface-regular-italic.less │ │ │ ├── roboto-fontface-regular-italic.scss │ │ │ ├── roboto-fontface-regular.less │ │ │ ├── roboto-fontface-regular.scss │ │ │ ├── roboto-fontface-thin-italic.less │ │ │ ├── roboto-fontface-thin-italic.scss │ │ │ ├── roboto-fontface-thin.less │ │ │ ├── roboto-fontface-thin.scss │ │ │ ├── roboto-fontface.css │ │ │ ├── roboto-fontface.less │ │ │ └── roboto-fontface.scss │ │ ├── fonts │ │ │ ├── Roboto-Black.eot │ │ │ ├── Roboto-Black.svg │ │ │ ├── Roboto-Black.ttf │ │ │ ├── Roboto-Black.woff │ │ │ ├── Roboto-Black.woff2 │ │ │ ├── Roboto-BlackItalic.eot │ │ │ ├── Roboto-BlackItalic.svg │ │ │ ├── Roboto-BlackItalic.ttf │ │ │ ├── Roboto-BlackItalic.woff │ │ │ ├── Roboto-BlackItalic.woff2 │ │ │ ├── Roboto-Bold.eot │ │ │ ├── Roboto-Bold.svg │ │ │ ├── Roboto-Bold.ttf │ │ │ ├── Roboto-Bold.woff │ │ │ ├── Roboto-Bold.woff2 │ │ │ ├── Roboto-BoldItalic.eot │ │ │ ├── Roboto-BoldItalic.svg │ │ │ ├── Roboto-BoldItalic.ttf │ │ │ ├── Roboto-BoldItalic.woff │ │ │ ├── Roboto-BoldItalic.woff2 │ │ │ ├── Roboto-Light.eot │ │ │ ├── Roboto-Light.svg │ │ │ ├── Roboto-Light.ttf │ │ │ ├── Roboto-Light.woff │ │ │ ├── Roboto-Light.woff2 │ │ │ ├── Roboto-LightItalic.eot │ │ │ ├── Roboto-LightItalic.svg │ │ │ ├── Roboto-LightItalic.ttf │ │ │ ├── Roboto-LightItalic.woff │ │ │ ├── Roboto-LightItalic.woff2 │ │ │ ├── Roboto-Medium.eot │ │ │ ├── Roboto-Medium.svg │ │ │ ├── Roboto-Medium.ttf │ │ │ ├── Roboto-Medium.woff │ │ │ ├── Roboto-Medium.woff2 │ │ │ ├── Roboto-MediumItalic.eot │ │ │ ├── Roboto-MediumItalic.svg │ │ │ ├── Roboto-MediumItalic.ttf │ │ │ ├── Roboto-MediumItalic.woff │ │ │ ├── Roboto-MediumItalic.woff2 │ │ │ ├── Roboto-Regular.eot │ │ │ ├── Roboto-Regular.svg │ │ │ ├── Roboto-Regular.ttf │ │ │ ├── Roboto-Regular.woff │ │ │ ├── Roboto-Regular.woff2 │ │ │ ├── Roboto-RegularItalic.eot │ │ │ ├── Roboto-RegularItalic.svg │ │ │ ├── Roboto-RegularItalic.ttf │ │ │ ├── Roboto-RegularItalic.woff │ │ │ ├── Roboto-RegularItalic.woff2 │ │ │ ├── Roboto-Thin.eot │ │ │ ├── Roboto-Thin.svg │ │ │ ├── Roboto-Thin.ttf │ │ │ ├── Roboto-Thin.woff │ │ │ ├── Roboto-Thin.woff2 │ │ │ ├── Roboto-ThinItalic.eot │ │ │ ├── Roboto-ThinItalic.svg │ │ │ ├── Roboto-ThinItalic.ttf │ │ │ ├── Roboto-ThinItalic.woff │ │ │ └── Roboto-ThinItalic.woff2 │ │ ├── package.json │ │ └── test.sh │ └── yakyak │ │ ├── about.less │ │ ├── applayout.less │ │ ├── colors.less │ │ ├── controls.less │ │ ├── convadd.less │ │ ├── convhead.less │ │ ├── convlist.less │ │ ├── fonts.less │ │ ├── global.less │ │ ├── input.less │ │ ├── listhead.less │ │ ├── messages.less │ │ └── typinginfo.less │ ├── dispatcher.coffee │ ├── emojishortcode.coffee │ ├── events.coffee │ ├── images │ └── photo.jpg │ ├── index.html │ ├── models │ ├── connection.coffee │ ├── conv.coffee │ ├── convsettings.coffee │ ├── entity.coffee │ ├── index.coffee │ ├── menuhandler.coffee │ ├── notify.coffee │ ├── userinput.coffee │ └── viewstate.coffee │ ├── util.coffee │ ├── version.coffee │ └── views │ ├── about.coffee │ ├── applayout.coffee │ ├── conninfo.coffee │ ├── contextmenu.coffee │ ├── controller.coffee │ ├── controls.coffee │ ├── convadd.coffee │ ├── convhead.coffee │ ├── convlist.coffee │ ├── dockicon.coffee │ ├── emojicategories.coffee │ ├── index.coffee │ ├── input.coffee │ ├── listhead.coffee │ ├── menu.coffee │ ├── messages.coffee │ ├── notifications.coffee │ ├── startup.coffee │ ├── trayicon.coffee │ └── typinginfo.coffee └── test ├── global.coffee ├── init.json ├── test-conv-funcs.coffee ├── test-conv-init.coffee ├── test-entity-funcs.coffee ├── test-entity-init.coffee └── test-userinput.coffee /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | 5 | charset = utf-8 6 | end_of_line = lf 7 | 8 | trim_trailing_whitespace = true 9 | insert_final_newline = false 10 | 11 | indent_style = space 12 | 13 | # 4 space indentation 14 | [*.coffee] 15 | indent_size = 4 16 | 17 | [{package.json,.travis.yml}] 18 | indent_size = 2 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | indent_size = 2 23 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 60 5 | # Number of days of inactivity before a stale Issue or Pull Request is closed 6 | daysUntilClose: 60 7 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 8 | exemptLabels: 9 | - pinned 10 | # Label to use when marking as stale 11 | staleLabel: inactive 12 | # Comment to post when marking as stale. Set to `false` to disable 13 | markComment: > 14 | This issue has been automatically marked as stale because it has not had 15 | recent activity. It will be closed if no further activity occurs. Thank you 16 | for your contributions. 17 | # Comment to post when removing the stale label. Set to `false` to disable 18 | unmarkComment: false 19 | # Comment to post when closing a stale Issue or Pull Request. Set to `false` to disable 20 | closeComment: false 21 | # Limit to only `issues` or `pulls` 22 | only: issues 23 | 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | node_modules 15 | npm-debug.log 16 | public 17 | .DS_Store 18 | .#* 19 | \#* 20 | bower_components 21 | doc 22 | 23 | Yakyak.app 24 | app 25 | dist 26 | scrape-emojipedia.org.py 27 | .idea/ 28 | -------------------------------------------------------------------------------- /.snyk: -------------------------------------------------------------------------------- 1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. 2 | version: v1.14.1 3 | ignore: {} 4 | # patches apply the minimum changes required to fix a vulnerability 5 | patch: 6 | SNYK-JS-LODASH-567746: 7 | - electron-installer-flatpak > async > lodash: 8 | patched: '2020-06-30T13:32:26.801Z' 9 | - electron-installer-flatpak > flatpak-bundler > lodash: 10 | patched: '2020-06-30T13:32:26.801Z' 11 | - electron-installer-flatpak > lodash: 12 | patched: '2020-06-30T13:32:26.801Z' 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 15 4 | notifications: 5 | email: false 6 | 7 | # 8 | # precise does not have wine1.6-i386 9 | # it would be necessary to add wine ppa 10 | # 11 | # https://docs.travis-ci.com/user/trusty-ci-environment/#Using-Trusty 12 | dist: bionic 13 | sudo: required 14 | 15 | before_install: 16 | - npm i -g coffee-script 17 | 18 | before_deploy: 19 | # YAKYAK_VERSION is used in the files to copy 20 | - export YAKYAK_VERSION=`node -e "console.log(require('./package.json').version);"` 21 | # branch 22 | - export YAKYAK_BRANCH=`node -e "console.log(require('./package.json').branch);"` 23 | # debug information for the electron packager 24 | - export DEBUG=electron-packager 25 | # allows to have wine with 32-bits 26 | - sudo dpkg --add-architecture i386 27 | - sudo apt-get -qq update 28 | - sudo rm /var/lib/dpkg/lock 29 | - sudo apt-get install -y -qq wine-stable zip xvfb fakeroot 30 | - wine wineboot --init # WINEARCH=win32 31 | # 32 | # actuall building of the binaries 33 | - npm run -loglevel ${NPM_DEBUG_LEVEL} deploy:linux-x64 34 | - npm run -loglevel ${NPM_DEBUG_LEVEL} deploy:linux-arm64 35 | - npm run -loglevel ${NPM_DEBUG_LEVEL} deploy:linux-ia32 36 | - npm run -loglevel ${NPM_DEBUG_LEVEL} deploy:darwin-x64 37 | - npm run -loglevel ${NPM_DEBUG_LEVEL} deploy:darwin-arm64 38 | - xvfb-run npm run -loglevel ${NPM_DEBUG_LEVEL} deploy:win32 39 | # 40 | # RPM and DEB binaries 41 | # install fpm 42 | - gem install --no-document fpm 43 | # needed to deploy on rpm / pacman 44 | - sudo apt-get install -y -qq rpm libarchive-tools 45 | # 46 | - npm run -loglevel ${NPM_DEBUG_LEVEL} gulp deploy:linux-x64:rpm:nodep 47 | - npm run -loglevel ${NPM_DEBUG_LEVEL} gulp deploy:linux-x64:deb:nodep 48 | - npm run -loglevel ${NPM_DEBUG_LEVEL} gulp deploy:linux-x64:pacman:nodep 49 | - npm run -loglevel ${NPM_DEBUG_LEVEL} gulp deploy:linux-ia32:rpm:nodep 50 | - npm run -loglevel ${NPM_DEBUG_LEVEL} gulp deploy:linux-ia32:deb:nodep 51 | - npm run -loglevel ${NPM_DEBUG_LEVEL} gulp deploy:linux-ia32:pacman:nodep 52 | # 53 | # Flatpak 54 | # install flatpak dependencies 55 | # 56 | - sudo add-apt-repository ppa:alexlarsson/flatpak -y 57 | - sudo apt-get update -qq 58 | - sudo apt-get install flatpak-builder flatpak elfutils -qq 59 | - flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo 60 | ## 64-bits 61 | - flatpak --user --noninteractive install flathub org.freedesktop.Sdk/x86_64/19.08 62 | - flatpak --user --noninteractive install flathub org.freedesktop.Platform/x86_64/19.08 63 | ## 32-bits 64 | #- flatpak --user install flathub org.freedesktop.Sdk/i386/1.6 65 | #- flatpak --user install flathub org.freedesktop.Platform/i386/1.6 66 | # 67 | - npm run -loglevel ${NPM_DEBUG_LEVEL} gulp deploy:linux-x64:flatpak:nodep 68 | #- npm run -loglevel ${NPM_DEBUG_LEVEL} gulp deploy:linux-ia32:flatpak:nodep 69 | 70 | deploy: 71 | provider: releases 72 | # overwrite pre-existing files 73 | overwrite: true 74 | # always create a pre-release 75 | prerelease: true 76 | # necessary to deploy in github, otherwise it would clean everything 77 | skip_cleanup: true 78 | # secret API_KEY stored in Travis CI 79 | api_key: "${API_KEY}" 80 | # files to copy to github release 81 | file: 82 | - "dist/yakyak-${YAKYAK_VERSION}-osx-x64.zip" 83 | - "dist/yakyak-${YAKYAK_VERSION}-osx-arm64.zip" 84 | # 85 | - "dist/yakyak-${YAKYAK_VERSION}-linux-x64.tar.gz" 86 | - "dist/yakyak-${YAKYAK_VERSION}-linux-x64.rpm" 87 | - "dist/yakyak-${YAKYAK_VERSION}-linux-amd64.deb" 88 | - "dist/yakyak-${YAKYAK_VERSION}-linux-x64-pacman.tar.gz" 89 | # 90 | - "dist/yakyak-${YAKYAK_VERSION}-win32-x64.zip" 91 | # 92 | - "dist/yakyak-${YAKYAK_VERSION}-linux-ia32.tar.gz" 93 | - "dist/yakyak-${YAKYAK_VERSION}-linux-i386.deb" 94 | - "dist/yakyak-${YAKYAK_VERSION}-linux-ia32.rpm" 95 | - "dist/yakyak-${YAKYAK_VERSION}-linux-ia32-pacman.tar.gz" 96 | # 97 | - "dist/yakyak-${YAKYAK_VERSION}-win32-ia32.zip" 98 | # 99 | - "dist/com.github.yakyak.YakYak_${YAKYAK_BRANCH}_x64.flatpak" 100 | # - "dist/com.github.yakyak.YakYak_master_ia32.flatpak" 101 | # only trigger on tags 102 | on: 103 | tags: true 104 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2016 Davide Bertola, Martin Algesten 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | YakYak 2 | ====== 3 | 4 | [![Build Status](https://travis-ci.org/yakyak/yakyak.svg)](https://travis-ci.org/yakyak/yakyak) 5 | [![Known Vulnerabilities](https://snyk.io/test/github/yakyak/yakyak/badge.svg?targetFile=package.json)](https://snyk.io/test/github/yakyak/yakyak?targetFile=package.json) 6 | 7 | > Desktop client for Google Hangouts *(not Google Hangouts Chat, which is a [different](https://github.com/yakyak/yakyak/issues/1037) thing)* 8 | 9 | ![sshot](https://cloud.githubusercontent.com/assets/123929/16032313/cdba46c2-3204-11e6-912f-a72fef60563a.png) 10 | 11 | (This app is in no way associated with or endorsed by Google) 12 | 13 | ## Install it 14 | 15 | We provide [prebuilt binaries](https://github.com/yakyak/yakyak/releases) for macOS, Linux 32 / 64 and Windows 32 / 64. This is the [latest release](https://github.com/yakyak/yakyak/releases/latest) 16 | 17 | Check out our wiki for [additional installation methods](https://github.com/yakyak/yakyak/wiki) 18 | 19 | We love [bug reports](https://github.com/yakyak/yakyak/issues)! 20 | 21 | ## What does it do: 22 | 23 | * Send/receive chat messages 24 | * Send/receive SMS/MMS (Project Fi / Google Voice) -- basic functionality 25 | * Create/change conversations (rename, add people) 26 | * Leave/delete conversation 27 | * Notifications (using native OS notifications) 28 | * Toggle notifications on/off 29 | * Drag-drop, copy-paste or attach-button for image upload. 30 | * Hangupsbot sync room aware (no bot name, proper user pics) 31 | * Show inline images 32 | * Send presence/focus/typing/activeclient to behave like a proper client 33 | * History scrollback 34 | * Video/audio integration (open in chrome) 35 | * Focus/typing indications (mainly a design issue. keep it clean) 36 | * Offer alternative color schemes 37 | * Translations in 22 languages so far: 38 | * English / Portuguese _(Portugal and Brazil)_ / French / Spanish / Czech / German / Polish / Russian / Hebrew / Ukrainian / Slovenian / Korean / Tamil / Romanian / Swedish / Japanese / Italian / Danish / Bengali / Slovak / Turkish / Arabic / Telugu / Finnish /Breton / Simplified chinese 39 | * We're looking for volunteers to translate the app to new languages 40 | 41 | ![sshot1](https://cloud.githubusercontent.com/assets/123929/16032393/991d63f8-3205-11e6-98bf-31f1b57cdc96.png) 42 | 43 | ![sshot2](https://cloud.githubusercontent.com/assets/123929/16032394/9e2ac08e-3205-11e6-81cc-fd4cb37441b5.png) 44 | 45 | **NOTE** 46 | 47 | Yakyak may show up as iOS Device and Google may alert you that *"some iOS Device is trying to use your account"*. This is normal as yakyak is an unofficial client and it mimics the behaviour of an iOS device in order to establish a communication with Google Hangout APIs. 48 | 49 | 50 | ## Credits 51 | 52 | #### Main authors 53 | 54 | * [André Veríssimo](https://github.com/averissimo) 55 | * [Davide Bertola](https://github.com/davibe) 56 | * [Martin Algesten](https://github.com/algesten) 57 | 58 | #### Contributors 59 | 60 | * [David Banham](https://github.com/davidbanham) 61 | * [Max Kueng](https://github.com/maxkueng) 62 | * [Arnaud Riu](https://github.com/arnriu) 63 | * [Austin Guevara](https://github.com/austin-guevara) 64 | * [Mathias Tillman](https://github.com/HomerSp) 65 | 66 | ## Developing 67 | 68 | This is an open source project. Please help us! 69 | 70 | It is written in coffeescript (nodejs) based on 71 | [hangupsjs](https://github.com/algesten/hangupsjs) using 72 | [trifl](http://algesten.github.io/trifl/) on top of 73 | [electron (atom shell)](https://github.com/electron/electron). 74 | 75 | ### How can you help? 76 | 77 | You can improve YakYak in many ways: 78 | 79 | * Core functionality 80 | * Interface *(example: new themes only require choosing less than 20 colors)* 81 | * Bug fixing 82 | * Translations *(new translation only need 117 strings)* 83 | 84 | Send a pull request, start a conversation with a 85 | [new issue](https://github.com/yakyak/yakyak/issues/new) or participate on a 86 | [ongoing conversation](https://github.com/yakyak/yakyak/issues). 87 | 88 | ### Setup 89 | 90 | Requirements: 91 | 92 | - Node.js (v4 or v6) 93 | 94 | ```bash 95 | $ npm install 96 | $ npm run gulp 97 | ``` 98 | 99 | ### Continuous build 100 | 101 | ```bash 102 | $ npm run gulp watch 103 | ``` 104 | 105 | ### Run it 106 | 107 | ```bash 108 | $ npm run electron app 109 | ``` 110 | 111 | ### Build Binaries for Deployment 112 | 113 | *Supported platforms:* Windows (*win32*), Mac OS X (*darwin*), Linux (*linux*) 114 | 115 | *Suported architectures:* 64-bits (*x64*), 32-bits (*ia32*) 116 | 117 | ```bash 118 | # Building for all platforms and architectures 119 | $ npm run deploy 120 | 121 | # You can also build specific builds by using 122 | # deploy:- 123 | # example: 124 | $ npm run deploy:darwin-x64 125 | ``` 126 | 127 | If you have [fpm](https://github.com/jordansissel/fpm) installed (`gem install fpm`), you can also build RPM, Deb, or Arch packages: 128 | 129 | ```bash 130 | $ npm run deploy:linux-x64:rpm 131 | $ npm run deploy:linux-x64:deb 132 | $ npm run deploy:linux-x64:pacman 133 | ``` 134 | 135 | *note:* if you are building *Windows* binaries in *Linux* or *Mac OS X*, Wine (1.6 or higher) must be installed. It also requires a 32-bit Wine installation when building Windows 32-bit binary. 136 | 137 | ### Structure 138 | 139 | | Location | Description | 140 | |-----------|------------------------------------------| 141 | | `src/` | Is where sources live | 142 | | `src/ui/` | Holds renderer code (client side) | 143 | | `dist/` | Everything is compiled to this directory | 144 | 145 | ### Acknowledgement 146 | 147 | - All the users and developers of YakYak 148 | - ["You wouldn't believe"](https://notificationsounds.com/notification-sounds/you-wouldnt-believe-510) as the 'new message' sound for some platforms and is licensed under CC 149 | -------------------------------------------------------------------------------- /coffeelint.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrow_spacing": { 3 | "level": "ignore" 4 | }, 5 | "braces_spacing": { 6 | "level": "ignore", 7 | "spaces": 0, 8 | "empty_object_spaces": 0 9 | }, 10 | "camel_case_classes": { 11 | "level": "error" 12 | }, 13 | "coffeescript_error": { 14 | "level": "error" 15 | }, 16 | "colon_assignment_spacing": { 17 | "level": "ignore", 18 | "spacing": { 19 | "left": 0, 20 | "right": 0 21 | } 22 | }, 23 | "cyclomatic_complexity": { 24 | "level": "ignore", 25 | "value": 10 26 | }, 27 | "duplicate_key": { 28 | "level": "error" 29 | }, 30 | "empty_constructor_needs_parens": { 31 | "level": "ignore" 32 | }, 33 | "ensure_comprehensions": { 34 | "level": "warn" 35 | }, 36 | "eol_last": { 37 | "level": "ignore" 38 | }, 39 | "indentation": { 40 | "value": 4, 41 | "level": "error" 42 | }, 43 | "line_endings": { 44 | "level": "ignore", 45 | "value": "unix" 46 | }, 47 | "max_line_length": { 48 | "value": 800, 49 | "level": "error", 50 | "limitComments": true 51 | }, 52 | "missing_fat_arrows": { 53 | "level": "ignore", 54 | "is_strict": false 55 | }, 56 | "newlines_after_classes": { 57 | "value": 3, 58 | "level": "ignore" 59 | }, 60 | "no_backticks": { 61 | "level": "error" 62 | }, 63 | "no_debugger": { 64 | "level": "warn", 65 | "console": false 66 | }, 67 | "no_empty_functions": { 68 | "level": "ignore" 69 | }, 70 | "no_empty_param_list": { 71 | "level": "ignore" 72 | }, 73 | "no_implicit_braces": { 74 | "level": "ignore", 75 | "strict": true 76 | }, 77 | "no_implicit_parens": { 78 | "level": "ignore", 79 | "strict": true 80 | }, 81 | "no_interpolation_in_single_quotes": { 82 | "level": "ignore" 83 | }, 84 | "no_nested_string_interpolation": { 85 | "level": "warn" 86 | }, 87 | "no_plusplus": { 88 | "level": "ignore" 89 | }, 90 | "no_private_function_fat_arrows": { 91 | "level": "warn" 92 | }, 93 | "no_stand_alone_at": { 94 | "level": "ignore" 95 | }, 96 | "no_tabs": { 97 | "level": "error" 98 | }, 99 | "no_this": { 100 | "level": "ignore" 101 | }, 102 | "no_throwing_strings": { 103 | "level": "error" 104 | }, 105 | "no_trailing_semicolons": { 106 | "level": "error" 107 | }, 108 | "no_trailing_whitespace": { 109 | "level": "error", 110 | "allowed_in_comments": false, 111 | "allowed_in_empty_lines": true 112 | }, 113 | "no_unnecessary_double_quotes": { 114 | "level": "ignore" 115 | }, 116 | "no_unnecessary_fat_arrows": { 117 | "level": "warn" 118 | }, 119 | "non_empty_constructor_needs_parens": { 120 | "level": "ignore" 121 | }, 122 | "prefer_english_operator": { 123 | "level": "ignore", 124 | "doubleNotLevel": "ignore" 125 | }, 126 | "space_operators": { 127 | "level": "ignore" 128 | }, 129 | "spacing_after_comma": { 130 | "level": "ignore" 131 | }, 132 | "transform_messes_up_line_numbers": { 133 | "level": "warn" 134 | }, 135 | "check_scope": { 136 | "module": "coffeescope2", 137 | "level": "warn", 138 | "environments": ["es5"], 139 | "globals": { 140 | "jQuery": true, 141 | "$": true, 142 | "console": true, 143 | "require": true, 144 | "module": true, 145 | "__dirname": true, 146 | "process": true 147 | }, 148 | "overwrite": true, 149 | "shadow": true, 150 | "shadow_builtins": false, 151 | "shadow_exceptions": ["err", "next"], 152 | "undefined": true, 153 | "hoist_local": true, 154 | "hoist_parent": true, 155 | "unused_variables": false, 156 | "unused_arguments": false, 157 | "unused_classes": true 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | // Dummy file. The real one is built from src/main.coffee to output dir 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yakyak", 3 | "version": "1.5.12-beta", 4 | "branch": "beta", 5 | "description": "Chat client for Google Hangouts", 6 | "main": "main.js", 7 | "scripts": { 8 | "start": "gulp && electron app", 9 | "dev": "electron app --debug", 10 | "gulp": "gulp", 11 | "electron": "electron", 12 | "test": "mocha", 13 | "clean": "gulp clean", 14 | "deploy": "gulp deploy", 15 | "deploy:full": "gulp deploy:full", 16 | "deploy:linux": "gulp deploy:linux", 17 | "deploy:win32": "gulp deploy:win32", 18 | "deploy:darwin": "gulp deploy:darwin", 19 | "deploy:linux-arm64": "gulp deploy:linux-arm64", 20 | "deploy:linux-x64": "gulp deploy:linux-x64", 21 | "deploy:linux-x64:deb": "gulp deploy:linux-x64:deb", 22 | "deploy:linux-x64:flatpak": "gulp deploy:linux-x64:flatpak", 23 | "deploy:linux-x64:rpm": "gulp deploy:linux-x64:rpm", 24 | "deploy:linux-x64:pacman": "gulp deploy:linux-x64:pacman", 25 | "deploy:linux-ia32": "gulp deploy:linux-ia32", 26 | "deploy:linux-ia32:deb": "gulp deploy:linux-ia32:deb", 27 | "deploy:linux-ia32:flatpak": "gulp deploy:linux-ia32:flatpak", 28 | "deploy:linux-ia32:rpm": "gulp deploy:linux-ia32:rpm", 29 | "deploy:linux-ia32:pacman": "gulp deploy:linux-ia32:pacman", 30 | "deploy:win32-x64": "gulp deploy:win32-x64", 31 | "deploy:win32-ia32": "gulp deploy:win32-ia32", 32 | "deploy:darwin-x64": "gulp deploy:darwin-x64", 33 | "deploy:darwin-arm64": "gulp deploy:darwin-arm64", 34 | "snyk-protect": "snyk protect" 35 | }, 36 | "repository": { 37 | "type": "git", 38 | "url": "https://github.com/yakyak/yakyak" 39 | }, 40 | "keywords": [ 41 | "hangouts", 42 | "chat", 43 | "client" 44 | ], 45 | "author": "Martin Algesten", 46 | "license": "MIT", 47 | "bugs": { 48 | "url": "https://github.com/yakyak/yakyak/issues" 49 | }, 50 | "mocha": { 51 | "reporter": "list", 52 | "timeout": 500, 53 | "spec": "test/**/*.coffee", 54 | "require": [ 55 | "coffeescript/register" 56 | ] 57 | }, 58 | "homepage": "https://github.com/yakyak/yakyak", 59 | "greenkeeper": { 60 | "ignore": [ 61 | "trifl" 62 | ] 63 | }, 64 | "dependencies": { 65 | "auto-launch": "^5.0.5", 66 | "autosize": "^4.0.3", 67 | "bog": "^1.0.0", 68 | "got": "^11.8.3", 69 | "hangupsjs": "github:yakyak/hangupsjs", 70 | "i18n": "^0.13.3", 71 | "mime-types": "^2.1.31", 72 | "moment": "^2.28.0", 73 | "node-notifier": "^9.0.1", 74 | "notr": "^1.1.2", 75 | "q": "^1.5.1", 76 | "snyk": "^1.667.0", 77 | "tmp": "^0.2.1", 78 | "trifl": "0.0.10", 79 | "uber-url-regex": "^5.0.9" 80 | }, 81 | "optionalDependencies": { 82 | "@malept/electron-installer-flatpak": "^0.11.2", 83 | "electron-installer-debian": "^3.1.0" 84 | }, 85 | "devDependencies": { 86 | "chai": "^4.2.0", 87 | "coffeescript": "^2.5.1", 88 | "electron": "^13.5.0", 89 | "electron-packager": "^15.1.0", 90 | "gulp": "^4.0.2", 91 | "gulp-changed": "^4.0.2", 92 | "gulp-coffee": "^3.0.3", 93 | "gulp-concat": "^2.6.0", 94 | "gulp-filter": "^6.0.0", 95 | "gulp-gzip": "^1.4.2", 96 | "gulp-less": "^4.0.1", 97 | "gulp-livereload": "4.0.2", 98 | "gulp-print": "^5.0.2", 99 | "gulp-reinstall": "^0.2.0", 100 | "gulp-rename": "^2.0.0", 101 | "gulp-sourcemaps": "^2.6.5", 102 | "gulp-spawn": "^0.4.5", 103 | "gulp-zip": "^5.0.2", 104 | "mocha": "^7.2.0", 105 | "rimraf": "^3.0.2", 106 | "sinon": "^9.0.2", 107 | "sinon-chai": "^3.5.0" 108 | }, 109 | "snyk": true 110 | } 111 | -------------------------------------------------------------------------------- /resources/custom.css: -------------------------------------------------------------------------------- 1 | html[theme="custom"] { 2 | --white: #ffffff; 3 | --ghostwhite: #f4f4f4; 4 | --lightgrey: #e8e8e8; 5 | --altgrey: #C5CED4; 6 | --grey: #b4b4b4; 7 | --darkgrey: #777777; 8 | --lightblack: #455a64; 9 | --black: #263238; 10 | --green: #0f9d58; 11 | --darkgreen: #0b6e3e; 12 | --red: #f44336; 13 | --darkred: #ea1c0d; 14 | --link: #3879d9; 15 | --tweetbg: #55acee; 16 | --tweettext: #fff; 17 | --instagrambg: #55acee; 18 | --instagramtext: #fff; 19 | } 20 | -------------------------------------------------------------------------------- /resources/desktop.ejs: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | <% if (productName) { %>Name=<%= productName %> 3 | <% } %><% if (description) { %>Comment=<%= description %> 4 | <% } %><% if (genericName) { %>GenericName=<%= genericName %> 5 | <% } %><% if (name) { %>Exec=<%= name %> 6 | Icon=<%= name %> 7 | <% } %>Type=Application 8 | StartupNotify=true 9 | <% if (categories && categories.length) { %>Categories=<%= categories.join(';') %>; 10 | <% } %><% if (mimeType && mimeType.length) { %>MimeType=<%= mimeType.join(';') %>; 11 | <% } %> 12 | -------------------------------------------------------------------------------- /resources/linux/after-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Link to the binary 4 | ln -sf /opt/yakyak/yakyak /usr/bin/yakyak 5 | 6 | # Update icon cache 7 | /bin/touch --no-create /usr/share/icons/hicolor &>/dev/null 8 | /usr/bin/gtk-update-icon-cache /usr/share/icons/hicolor &>/dev/null || : 9 | -------------------------------------------------------------------------------- /resources/linux/after-remove.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Delete the link to the binary 4 | rm -f /usr/bin/yakyak 5 | -------------------------------------------------------------------------------- /resources/linux/app.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=YakYak 3 | Comment=Chat client for Google Hangouts 4 | GenericName=YakYak 5 | Exec=/opt/yakyak/yakyak %U 6 | Icon=yakyak 7 | Terminal=false 8 | Type=Application 9 | StartupNotify=true 10 | #StartupWMClass=YakYak 11 | Keywords=hangouts;messenger 12 | Categories=Network;InstantMessaging 13 | X-GNOME-UsesNotifications=true 14 | -------------------------------------------------------------------------------- /src/icons/icon-read_016.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon-read_016.png -------------------------------------------------------------------------------- /src/icons/icon-read_016_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon-read_016_blue.png -------------------------------------------------------------------------------- /src/icons/icon-read_020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon-read_020.png -------------------------------------------------------------------------------- /src/icons/icon-read_020_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon-read_020_blue.png -------------------------------------------------------------------------------- /src/icons/icon-read_032.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon-read_032.png -------------------------------------------------------------------------------- /src/icons/icon-read_032_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon-read_032_blue.png -------------------------------------------------------------------------------- /src/icons/icon-read_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon-read_128.png -------------------------------------------------------------------------------- /src/icons/icon-read_128_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon-read_128_blue.png -------------------------------------------------------------------------------- /src/icons/icon-unread_016.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon-unread_016.png -------------------------------------------------------------------------------- /src/icons/icon-unread_020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon-unread_020.png -------------------------------------------------------------------------------- /src/icons/icon-unread_032.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon-unread_032.png -------------------------------------------------------------------------------- /src/icons/icon-unread_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon-unread_128.png -------------------------------------------------------------------------------- /src/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon.icns -------------------------------------------------------------------------------- /src/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon.ico -------------------------------------------------------------------------------- /src/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon.png -------------------------------------------------------------------------------- /src/icons/icon_016.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon_016.png -------------------------------------------------------------------------------- /src/icons/icon_032.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon_032.png -------------------------------------------------------------------------------- /src/icons/icon_048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon_048.png -------------------------------------------------------------------------------- /src/icons/icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon_128.png -------------------------------------------------------------------------------- /src/icons/icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon_256.png -------------------------------------------------------------------------------- /src/icons/icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icon_512.png -------------------------------------------------------------------------------- /src/icons/icons.iconsproj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/icons.iconsproj -------------------------------------------------------------------------------- /src/icons/osx-icon-read-Template_016.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/osx-icon-read-Template_016.png -------------------------------------------------------------------------------- /src/icons/osx-icon-read-Template_032.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/osx-icon-read-Template_032.png -------------------------------------------------------------------------------- /src/icons/osx-icon-unread-Template_016.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/osx-icon-unread-Template_016.png -------------------------------------------------------------------------------- /src/icons/osx-icon-unread-Template_032.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/osx-icon-unread-Template_032.png -------------------------------------------------------------------------------- /src/icons/yak.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/yak.png -------------------------------------------------------------------------------- /src/icons/yak_icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/yak_icon.icns -------------------------------------------------------------------------------- /src/icons/yakyak-logo-read.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 68 | -------------------------------------------------------------------------------- /src/icons/yakyak-logo-read_blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml -------------------------------------------------------------------------------- /src/icons/yakyak-logo.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/yakyak-logo.ai -------------------------------------------------------------------------------- /src/icons/yakyak-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml -------------------------------------------------------------------------------- /src/icons/yakyak-logo.svg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/icons/yakyak-logo.svg.png -------------------------------------------------------------------------------- /src/icons/yakyak-osx-read.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml -------------------------------------------------------------------------------- /src/icons/yakyak-osx-unread.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml -------------------------------------------------------------------------------- /src/locales/ar.json: -------------------------------------------------------------------------------- 1 | { 2 | "__MyLocaleLanguage__": "العربية", 3 | "__maintaner__": "http://github.com/belaassal", 4 | 5 | "title": "YakYak - وسيط Hangouts", 6 | "description": "وسيط نظام سطح المكتب لجوجل Hangouts", 7 | 8 | "settings": "الإعدادات", 9 | "recent": "حديث", 10 | "details": "التفاصيل", 11 | 12 | "connection": { 13 | "connecting": "جاري الإتصال", 14 | "loading": "تحميل الأعضاء", 15 | "not_connected": "غير متصل (تحقق من الإتصال) ." 16 | }, 17 | 18 | "input": { 19 | "message": "رسالة", 20 | "emoticons": "إظهار الرموز", 21 | "image": "إدراج صورة", 22 | "typing": "يكتب" 23 | }, 24 | "conversation": { 25 | "title": { 26 | "one": "محادثة", 27 | "other": "محادثات" 28 | }, 29 | "numbered": "محادثة %d", 30 | "options": "خيارات المحادثة", 31 | "setting": "إعدادات المحادثة", 32 | "add": "إضافة محادثة", 33 | "new": "محادثة جديدة", 34 | "name": "إسم المحادثة", 35 | "edit": "تعديل المحادثة", 36 | "multiuser": "إنشاء دردشة متعددة الأعضاء", 37 | "delete": " حذف المحادثة", 38 | "delete_confirm": "هل تود حقا حذف المحادثة ؟", 39 | "delete_error": "خطأ عند حذف المحادثة", 40 | "leave": "مغادرة المحادثة", 41 | "leave_confirm": "هل تود حقا مغادرة المحادثة ؟", 42 | "search": "البحث عن أشخاص", 43 | "new_message": "إستلام رسالة جديدة", 44 | "new_attachment": "إستلام رسالة جديدة (صورة أو فيديو)", 45 | "open_link": "فتح الرابط في المتصفح...", 46 | "no_preview_image_click_to_open": "معاينة الصورة غير مفعلة : انقر لفتحها في المتصفح" 47 | }, 48 | "notification": { 49 | "one": "إشعار", 50 | "other": "إشعارات" 51 | }, 52 | "favorite": { 53 | "title": { 54 | "one": "مفضلة", 55 | "other": "المفضلة" 56 | }, 57 | "star_it": "إضافة مفضلة / حذف مفضلة" 58 | }, 59 | "actions": { 60 | "ok": "موافق", 61 | "cancel": "إلغاء" 62 | }, 63 | "call": { 64 | "incoming": "مكالمة واردة", 65 | "incoming_from": "مكالمة واردة من %s", 66 | "accepted": "مقبولة", 67 | "accept": "قبول", 68 | "rejected": "مرفوضة", 69 | "reject": "رفض", 70 | "missed": "مكالمة فائتة من %s" 71 | }, 72 | "menu": { 73 | "title": "قائمة", 74 | "file": { 75 | "title": "YakYak", 76 | "hide": "إخفاء YakYak", 77 | "hide_others": "إخفاء الآخرين", 78 | "show": "عرض الكل", 79 | "inspector": "فتح المراقب", 80 | "logout": "تسجيل خروج", 81 | "quit": "غادر" 82 | }, 83 | "edit": { 84 | "title": "تعديل", 85 | "undo": "إلغاء", 86 | "redo": "رجوع", 87 | "paste": "لصق", 88 | "cut": "قص", 89 | "copy": "نسخ", 90 | "copy_image_link": "نسخ رابط الصورة", 91 | "copy_link": "نسخ الرابط", 92 | "copy_image": "نسخ الصورة", 93 | "select_all": "إنتقي الكل", 94 | "language": "اللغة (Language)", 95 | "dateformat": "إستخدام نسق تاريخ النظام" 96 | }, 97 | "view": { 98 | "title": "عرض", 99 | "emoji": "تحويل النص إلى رمز", 100 | "suggestemoji": "إقتراح الرموز التعبيرية عند الكتابة", 101 | "showimagepreview": "إظهار معاينة الصورة", 102 | "hide_dock": "إخفاء أيقونة الرصيف", 103 | "fullscreen": "وضع كامل الشاشة", 104 | "tray": { 105 | "main": "أيقونة الصينية", 106 | "show_tray": "تبديل النافذة إظهار / إخفاء", 107 | "show_window": "إظهار النافذة", 108 | "toggle_minimize": "التصغير إلى الصينية", 109 | "start_minimize": "التصغير إلى الصينية عند التشغيل", 110 | "close": "الغلق إلى الصينية" 111 | }, 112 | "escape": { 113 | "title": "سلوك مفتاح الهروب", 114 | "hide": "إخفاء النافذة", 115 | "clear": "مسح المدخلات", 116 | "default": "default when tray is not showing" 117 | }, 118 | "conversation": { 119 | "title": "قائمة المحادثة", 120 | "new": "محادثة جديدة", 121 | "timestamp": "إظهار زمن المحادثة", 122 | "last": "إظهار أخر رسالة في المحادثة", 123 | "previous": "المحادثة السابقة", 124 | "next": "المحادثة التالية", 125 | "select": "إنتقاء محادثة", 126 | "thumbnails": { 127 | "show": "إظهار الصور المصغرة", 128 | "only": "إظهار الصور المصغرة فقط", 129 | "animated": "إظهار الصور المصغرة المتحركة" 130 | } 131 | }, 132 | "notification": { 133 | "title": "نافذة الإشعارت", 134 | "show": "عرض الإشعارت", 135 | "toggle": "تبديل الإشعارت", 136 | "message": "إظهار الرسالة في الإشعارت", 137 | "username": "إظهار إسم المستخدم في الإشعارت", 138 | "mute": "تعطيل الصوت في الإشعارت", 139 | "avatar": "إظهار أيقونة المستخدم في الإشعارت", 140 | "icon": "إظهار أيقونة YakYak في الإشعارت", 141 | "custom_sound": " إستخدام الصوت المصمم ل YakYak في الإشعارت" 142 | }, 143 | "color_scheme": { 144 | "title": "نظام الألوان", 145 | "default": "الأصلي", 146 | "blue": "أزرق", 147 | "dark": "داكن", 148 | "material": "Material", 149 | "pop": "Pop", 150 | "gruvy": "Gruvy" 151 | }, 152 | "font": { 153 | "title": "حجم الخط", 154 | "extra_small": "صغير جدا", 155 | "small": "صغير", 156 | "medium": "متوسط", 157 | "large": "كبير", 158 | "extra_large": "كبير جدا" 159 | }, 160 | "zoom": { 161 | "in": "تكبير", 162 | "out": "تصغير", 163 | "reset": "الحجم الأصلي" 164 | } 165 | }, 166 | "help": { 167 | "title": "مساعدة", 168 | "about": { 169 | "title": "حول YakYak", 170 | "newer": "يتوفر إصدار أحدث ، يرجى الترقية من %s إلى %s", 171 | "license": "رخصة", 172 | "authors": "المؤلفون الرئيسيون", 173 | "contributors": "المساهمون", 174 | "startup": "فتح عند بدء التشغيل" 175 | } 176 | }, 177 | "window": { 178 | "title": "نافذة", 179 | "minimize": "تصغير", 180 | "close": "غلق", 181 | "front": "تقديم الكل" 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/locales/bn.json: -------------------------------------------------------------------------------- 1 | { 2 | "__MyLocaleLanguage__": "বাংলা", 3 | "__maintaner__": "http://github.com/digiwizkid", 4 | 5 | "title": "YakYak - Hangouts ক্লায়েন্ট", 6 | "description": "Google Hangouts এর জন্য ডেস্কটপ ক্লায়েন্ট", 7 | 8 | "settings": "সেটিংস", 9 | "recent": "সাম্প্রতিক", 10 | "details": "বিস্তারিত", 11 | 12 | "connection": { 13 | "connecting": "সংযুক্ত হচ্ছে", 14 | "loading": "পরিচিতি লোড হচ্ছে", 15 | "not_connected": "সংযুক্ত নয় (সংযোগ পরীক্ষা করুন))" 16 | }, 17 | 18 | "input": { 19 | "message": "বার্তা", 20 | "emoticons": "ইমোটিকন দেখান", 21 | "image": "চিত্র সংযুক্ত করুন", 22 | "typing": "টাইপ করছেন" 23 | }, 24 | "conversation": { 25 | "title": { 26 | "one": "কথোপকথন", 27 | "other": "কথোপকথন" 28 | }, 29 | "numbered": "কথোপকথন %d", 30 | "options": "কথোপকথন বিকল্প", 31 | "setting": "কথোপকথন সেটিংস", 32 | "add": "নতুন কথোপকথন যোগ করুন", 33 | "new": "নতুন কথোপকথন", 34 | "name": "কথোপকথনের নাম", 35 | "edit": "কথোপকথন সম্পাদনা", 36 | "multiuser": "Multiuser চ্যাট তৈরি করুন", 37 | "delete": "কথোপকথন মুছুন", 38 | "delete_confirm": "সত্যিই কথোপকথন মুছে ফেলতে চান?", 39 | "leave": "কথোপকথন ত্যাগ করুন", 40 | "leave_confirm": "সত্যিই কথোপকথন ত্যাগ করতে চান?", 41 | "search": "লোকেদের অনুসন্ধান করুন", 42 | "new_message": "নতুন বার্তা", 43 | "new_attachment": "নতুন মেসেজ এসেছে (ছবি অথবা ভিডিও)", 44 | "open_link": "ব্রাউজার এ লিঙ্ক টি খোলা হছে...", 45 | "no_preview_image_click_to_open": "ছবির প্রিভিউ বন্ধ করা আছেঃ ব্রাউজার এ খোলার জন্য ক্লিক করুন" 46 | }, 47 | "notification": { 48 | "one": "প্রজ্ঞাপন", 49 | "other": "বিজ্ঞপ্তিগুলি" 50 | }, 51 | "favorite": { 52 | "title": { 53 | "one": "প্রিয়", 54 | "other": "প্রিয়" 55 | }, 56 | "star_it": "Star / unstar" 57 | }, 58 | "actions": { 59 | "ok": "ঠিক আছে", 60 | "cancel": "বাতিল" 61 | }, 62 | "call": { 63 | "incoming": "ইনকামিং কল", 64 | "incoming_from": "ইনকামিং কল from %s", 65 | "accepted": "গৃহীত", 66 | "accept": "গ্রহণ", 67 | "rejected": "প্রত্যাখ্যাত", 68 | "reject": "প্রত্যাখ্যান", 69 | "missed": "মিসড কল from %s" 70 | }, 71 | "menu": { 72 | "title": "মেনু", 73 | "file": { 74 | "title": "YakYak", 75 | "hide": "Hide YakYak", 76 | "hide_others": "Hide Others", 77 | "show": "সব দেখান", 78 | "inspector": "ইন্সপেক্টর খুলুন", 79 | "logout": "লগ-আউট", 80 | "quit": "ত্যাগ করুন" 81 | }, 82 | "edit": { 83 | "title": "সম্পাদন", 84 | "undo": "পূর্বাবস্থায় করুন", 85 | "redo": "পুনরায় করুন", 86 | "paste": "পেস্ট", 87 | "cut": "কাট", 88 | "copy": "কপি", 89 | "copy_image_link": "ছবির লিঙ্ক কপি করুন", 90 | "copy_link": "লিঙ্ক কপি করুন", 91 | "copy_image": "ছবি কপি করুন", 92 | "select_all": "সব নির্বাচন করুন", 93 | "language": "ভাষা (Language)", 94 | "dateformat": "Use system date format" 95 | }, 96 | "view": { 97 | "title": "View", 98 | "emoji": "পাঠ্য ইমোজিতে রূপান্তর করুন", 99 | "suggestemoji": "Suggest emoji on typing", 100 | "showimagepreview": "ছবির প্রিভিউ দেখান", 101 | "hide_dock": "ডক আইকন লুকান", 102 | "fullscreen": "Toggle Fullscreen", 103 | "tray": { 104 | "show_tray": "ট্রে আইকন দেখান", 105 | "show_window": "window দেখান", 106 | "toggle_minimize": "Toggle minimize to tray", 107 | "start_minimize": "Start minimized to tray", 108 | "close": "Close to tray" 109 | }, 110 | "escape": { 111 | "title": "Escape key behavior", 112 | "hide": "Hides window", 113 | "clear": "ইনপুট মুছে ফেলে", 114 | "default": "default when tray is not showing" 115 | }, 116 | "conversation": { 117 | "title": "কথোপকথন তালিকা", 118 | "timestamp": "কথোপকথন টাইমস্ট্যাম্প দেখান", 119 | "last": "কথোপকথন শেষ বার্তা দেখান", 120 | "previous": "পূর্ববর্তী কথোপকথন", 121 | "next": "পরবর্তী কথোপকথন", 122 | "select": "কথোপকথন নির্বাচন করুন", 123 | "thumbnails": { 124 | "show": "থাম্বনেইলগুলি দেখান", 125 | "only": "শুধুমাত্র থাম্বনেল দেখান", 126 | "animated": "অ্যানিমেটেড থাম্বনেল দেখান" 127 | } 128 | }, 129 | "notification": { 130 | "title": "পপ-আপ বিজ্ঞপ্তি", 131 | "show": "বিজ্ঞপ্তিগুলি দেখান", 132 | "toggle": "বিজ্ঞপ্তিগুলি টগল করুন", 133 | "message": "বিজ্ঞপ্তিগুলিতে বার্তা দেখান", 134 | "username": "বিজ্ঞপ্তিগুলির মধ্যে ব্যবহারকারীর নাম দেখান", 135 | "mute": "বিজ্ঞপ্তিগুলিতে শব্দ অক্ষম করুন", 136 | "avatar": "বিজ্ঞপ্তিগুলিতে ব্যবহারকারী অবতার আইকন দেখান", 137 | "icon": "বিজ্ঞপ্তিগুলিতে YakYak আইকন দেখান", 138 | "custom_sound": "বিজ্ঞপ্তি জন্য YakYak কাস্টম শব্দ ব্যবহার করুন" 139 | }, 140 | "color_scheme": { 141 | "title": "রঙ স্কিম", 142 | "default": "আসল", 143 | "blue": "নীল", 144 | "dark": "অন্ধকার", 145 | "material": "Material" 146 | }, 147 | "font": { 148 | "title": "অক্ষরের আকার", 149 | "extra_small": "অতিরিক্ত ছোট", 150 | "small": "ছোট", 151 | "medium": "মাঝারি", 152 | "large": "বড়", 153 | "extra_large": "অতিরিক্ত বড়" 154 | }, 155 | "zoom": { 156 | "in": "জুম ইন", 157 | "out": "জুম আউট", 158 | "reset": "আসল আকার" 159 | } 160 | }, 161 | "help": { 162 | "title": "সহায়তা", 163 | "about": { 164 | "title": "YakYak সম্পর্কে", 165 | "newer": "একটি নতুন সংস্করণ উপলব্ধ, দয়া করে %s থেকে %s এ আপগ্রেড করুন", 166 | "license": "লাইসেন্স", 167 | "authors": "Main authors", 168 | "contributors": "অবদানকারী", 169 | "startup": "Startup এ খুলুন" 170 | } 171 | }, 172 | "window": { 173 | "title": "Window", 174 | "minimize": "Minimize", 175 | "close": "বন্ধ করুন", 176 | "front": "সমস্ত সামনে আনুন" 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "__MyLocaleLanguage__": "English", 3 | "__maintaner__": "http://github.com/yakyak" 4 | } 5 | -------------------------------------------------------------------------------- /src/locales/fi.json: -------------------------------------------------------------------------------- 1 | { 2 | "__MyLocaleLanguage__": "Finnish", 3 | "__maintaner__": "https://github.com/survivor303", 4 | 5 | "title": "YakYak - Hangouts sovellus", 6 | "description": "Työpöytäsovellus Google Hangouts palvelulle", 7 | 8 | "settings": "Asetukset", 9 | "recent": "Viimeisimmät", 10 | "details": "Asetukset", 11 | "connection": { 12 | "connecting": "Yhdistää", 13 | "loading": "Lataa kontakteja", 14 | "not_connected": "Ei yhdistänyt (Tarkista yhteys!)" 15 | }, 16 | "input": { 17 | "message": "Viesti", 18 | "emoticons": "Näytä hymiöt", 19 | "image": "Liitä kuva", 20 | "typing": "Kirjoittaa" 21 | }, 22 | "conversation": { 23 | "title": { 24 | "one": "Keskustelu", 25 | "other": "Keskustelut" 26 | }, 27 | "numbered": "Keskustelu %d", 28 | "options": "Keskustelun valinnat", 29 | "setting": "Keskustelun asetukset", 30 | "add": "Lisää uusi keskustelu", 31 | "new": "Uusi keskustelu", 32 | "name": "Keskustelun nimi", 33 | "edit": "Muokkaa keskustelua", 34 | "multiuser": "Luo ryhmä", 35 | "delete": "Poista keskustelu", 36 | "delete_confirm": "Haluatko varmasti poistaa tämän keskustelun?", 37 | "leave": "Lähde keskustelusta", 38 | "leave_confirm": "Haluatko varmasti lähteä keskustelusta?", 39 | "search": "Etsi ihmisiä", 40 | "new_message": "Uusi viesti vastaanotettu", 41 | "new_attachment": "Uusi media vastaanotettu (kuva tai video)", 42 | "open_link": "Avataan linkkiä selaimeen...", 43 | "settings": "Conversation settings", 44 | "no_preview_image_click_to_open": "Ei kuvien esikatselua: Klikkaa avataksesi selaimessa" 45 | }, 46 | "notification": { 47 | "one": "Ilmoitus", 48 | "other": "Ilmoituksia" 49 | }, 50 | "favorite": { 51 | "title": { 52 | "one": "Suosikki", 53 | "other": "Suosikit" 54 | }, 55 | "star_it": "Aseta/Poista tähti" 56 | }, 57 | "actions": { 58 | "ok": "ok", 59 | "cancel": "Peruuta" 60 | }, 61 | "call": { 62 | "incoming": "Tuleva puhelu", 63 | "incoming_from": "Tuleva puhelu käyttäjältä %s", 64 | "accepted": "Hyväksytty", 65 | "accept": "Vastaa", 66 | "rejected": "Ei hyväksytty", 67 | "reject": "Älä vastaa", 68 | "missed": "Ei vastattu puhelu käyttäjältä %s" 69 | }, 70 | "menu": { 71 | "title": "Valikko", 72 | "file": { 73 | "title": "YakYak", 74 | "hide": "Piilota YakYak", 75 | "hide_others": "Piilota muut", 76 | "show": "Näytä kaikki", 77 | "inspector": "Avaa Inspectorissa", 78 | "logout": "Kirjaudu ulos", 79 | "quit": "Lopeta" 80 | }, 81 | "edit": { 82 | "title": "Muokkaa", 83 | "undo": "Kumoa", 84 | "redo": "Tee uudelleen", 85 | "paste": "Liitä", 86 | "cut": "Leikkaa", 87 | "copy": "Kopioi", 88 | "copy_image_link": "Kopioi kuvan linkki", 89 | "copy_link": "Kopioi Linkki", 90 | "copy_image": "Kopioi kuva", 91 | "select_all": "Valitse kaikki", 92 | "language": "Sovelluksen kieli", 93 | "dateformat": "Käytä järjestelmän aikaa" 94 | }, 95 | "view": { 96 | "title": "Näytä", 97 | "emoji": "Muuta teksti hymiöksi", 98 | "suggestemoji": "Suosittele hymiötä kirjoittaessa", 99 | "showimagepreview": "Näytä kuvien esikatselu", 100 | "hide_dock": "Piilota dockin kuvake", 101 | "fullscreen": "Aseta kokoruudulle", 102 | "tray": { 103 | "main": "Ilmaisinalueen kuvake", 104 | "show_tray": "Näytä/Piilota ikkuna", 105 | "show_window": "Näytä ikkuna", 106 | "toggle_minimize": "Piilota ilmaisinalueelle", 107 | "start_minimize": "Käynnistä pienennettynä ilmaisinalueelle", 108 | "close": "Sulje ilmaisinalueelle" 109 | }, 110 | "escape": { 111 | "title": "Esc näppäimien toiminta", 112 | "hide": "Piilottaa ikkunan", 113 | "clear": "Tyhjentää tekstikentän", 114 | "default": "Oletus kun ilmaisinalueen kuvaketta ei ole käytössä" 115 | }, 116 | "conversation": { 117 | "title": "Keskustelulistaus", 118 | "new": "Uusi keskustelu", 119 | "timestamp": "Näytä keskustelun aikaleima", 120 | "last": "Näytä keskustelun viimeisin viesti", 121 | "previous": "Edellinen keskustelu", 122 | "next": "Seuraava keskustelu", 123 | "select": "Valitse keskustelu", 124 | "thumbnails": { 125 | "show": "Näytä keskustelukuvakkeet", 126 | "only": "Näytä vain keskustelukuvakkeet", 127 | "animated": "Näytä animoidut keskustelukuvakkeet" 128 | } 129 | }, 130 | "notification": { 131 | "title": "Pop-Up ilmoitukset", 132 | "show": "Näytä ilmoitukset", 133 | "toggle": "Käytä ilmoituksia", 134 | "message": "Näytä viesti ilmoituksissa", 135 | "username": "Näytä käyttäjänimet ilmoituksissa", 136 | "mute": "Poista ilmoitusäänet käytöstä", 137 | "avatar": "Näytä käyttäjäkuvake ilmoituksissa", 138 | "icon": "Näytä YakYak kuvake ilmoituksissa", 139 | "custom_sound": "Käytä YakYakin omaa ääntä ilmoituksiin" 140 | }, 141 | "color_scheme": { 142 | "title": "Väriteemat", 143 | "default": "Oletus", 144 | "blue": "Sininen", 145 | "dark": "Pimeä", 146 | "material": "Material", 147 | "pop": "Pop", 148 | "gruvy": "Gruvy" 149 | }, 150 | "font": { 151 | "title": "Fontin koko", 152 | "extra_small": "Pienin", 153 | "small": "Pieni", 154 | "medium": "Keskikoko", 155 | "large": "Suuri", 156 | "extra_large": "Suurin" 157 | }, 158 | "zoom": { 159 | "in": "Suurenna sisään", 160 | "out": "Surenna ulos", 161 | "reset": "Alkuperäinen koko" 162 | } 163 | }, 164 | "help": { 165 | "title": "Apua", 166 | "about": { 167 | "title": "Tietoja YakYak sovelluksesta", 168 | "newer": "Uusi versio saatavilla, hae päivitys täältä %s to %s", 169 | "license": "Lisenssi", 170 | "authors": "Pääkehittäjät", 171 | "contributors": "Avustajat", 172 | "startup": "Käynnistä käynnistyksen yhteydessä" 173 | } 174 | }, 175 | "window": { 176 | "title": "Ikkuna", 177 | "minimize": "Pienennä", 178 | "close": "Sulje", 179 | "front": "Tuo kaikki päällimäiseksi" 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/locales/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "__MyLocaleLanguage__": "日本語", 3 | "__maintaner__": "http://github.com/jefu7", 4 | 5 | "title": "YakYak - ヤックヤック ハングアウト クライアント", 6 | "description": "グーグル ハングアウト デスクトップ ソフト", 7 | 8 | "settings": "設定", 9 | "recent": "最近", 10 | "details": "詳細", 11 | 12 | "connection" :{ 13 | "connecting": "接続中", 14 | "loading": "ローディング", 15 | "not_connected": "接続不可 (確認要)" 16 | }, 17 | 18 | "input": { 19 | "message": "メッセージ", 20 | "emoticons": "絵文字を表示", 21 | "image": "イメージを添付", 22 | "typing": "は入力中" 23 | }, 24 | "conversation": { 25 | "title": { 26 | "one": "チャット", 27 | "other": "チャット" 28 | }, 29 | "numbered": "チャット %d", 30 | "options": "チャットのオプション", 31 | "setting": "チャットの設定", 32 | "add": "新チャットを追加", 33 | "new": "新チャット", 34 | "name": "チャット名", 35 | "edit": "チャットを編集", 36 | "multiuser": "多数人チャット", 37 | "delete": "チャットを削除", 38 | "delete_confirm": "本当にこのチャットを削除しますか?", 39 | "leave": "チャットをやめる", 40 | "leave_confirm": "本当にこのチャットをやめますか?", 41 | "search": "コンタクト検索", 42 | "new_message": "新メッセージ", 43 | "new_attachment": "New message received (image or video)", 44 | "open_link": "Opening the link in the browser...", 45 | "no_preview_image_click_to_open": "Image preview is disabled: click to open it in the browser" 46 | }, 47 | "notification": { 48 | "one": "通知", 49 | "other": "通知" 50 | }, 51 | "favorite": { 52 | "title": { 53 | "one": "お気に入り", 54 | "other": "お気に入り" 55 | }, 56 | "star_it": "お気に入り" 57 | }, 58 | "actions": { 59 | "ok": "確認", 60 | "cancel": "中止" 61 | }, 62 | "call": { 63 | "incoming": "電話着信", 64 | "incoming_from": "%s から電話着信", 65 | "accepted": "受信", 66 | "accept": "受ける", 67 | "rejected": "受信拒否", 68 | "reject": "拒否", 69 | "missed": "%s から着信あり" 70 | }, 71 | "menu": { 72 | "title": "メニュー", 73 | "file": { 74 | "title": "ヤックヤック", 75 | "hide": "ヤックヤックを隠す", 76 | "hide_others": "他を隠す", 77 | "show": "すべてを表示", 78 | "inspector": "インスペクターを開く", 79 | "logout": "ロッグアウト", 80 | "quit": "終了" 81 | }, 82 | "edit": { 83 | "title": "編集", 84 | "undo": "元に戻す", 85 | "redo": "やり直し", 86 | "paste": "ペースト", 87 | "cut": "カット", 88 | "copy": "コピー", 89 | "copy_image_link": "イメージのリンクをコピー", 90 | "copy_link": "リンクをコピー", 91 | "copy_image": "イメージをコピー", 92 | "select_all": "すべてを選択", 93 | "language": "言語 (Language)", 94 | "dateformat": "システムの日付形式を使用する" 95 | }, 96 | "view": { 97 | "title": "表示", 98 | "emoji": "絵文字に変換", 99 | "suggestemoji": "入力する時絵文字を勧める", 100 | "showimagepreview": "Show image preview", 101 | "hide_dock": "ドックのアイコンを隠す", 102 | "fullscreen": "全画面を表示・中止", 103 | "tray": { 104 | "show_tray": "アイコンを表示", 105 | "show_window": "ウインドウを表示", 106 | "toggle_minimize": "アイコンに隠す", 107 | "start_minimize": "起動時アイコンに隠す", 108 | "close": "アイコンに閉じる" 109 | }, 110 | "escape": { 111 | "title": "escキーの行動", 112 | "hide": "ウインドウを隠す", 113 | "clear": "入力を消す", 114 | "default": "アイコンを表示しない時の標準" 115 | }, 116 | "conversation": { 117 | "title": "チャットリスト", 118 | "timestamp": "チャット日時を表示", 119 | "last": "最後のチャットを表示", 120 | "previous": "前のチャット", 121 | "next": "次のチャット", 122 | "select": "チャットを選択", 123 | "thumbnails": { 124 | "show": "サムネイルを表示", 125 | "only": "サムネイルのみを表示", 126 | "animated": "動画サムネイルを表示" 127 | } 128 | }, 129 | "notification": { 130 | "title": "通知", 131 | "show": "通知を表示", 132 | "toggle": "通知 入/切", 133 | "message": "通知にメッセージを表示", 134 | "username": "通知にユーザー名を表示", 135 | "mute": "通知音を消音", 136 | "avatar": "通知にユーザーのアバターを表示", 137 | "icon": "通知にヤックヤックのアイコンを表示", 138 | "custom_sound": "通知の際ヤックヤックの音を使用" 139 | }, 140 | "color_scheme": { 141 | "title": "カラー設定", 142 | "default": "標準", 143 | "blue": "青", 144 | "dark": "暗い", 145 | "material": "生地" 146 | }, 147 | "font": { 148 | "title": "フォント", 149 | "extra_small": "極小", 150 | "small": "小", 151 | "medium": "中", 152 | "large": "大", 153 | "extra_large": "極大" 154 | }, 155 | "zoom": { 156 | "in": "ズームイン", 157 | "out": "ズームアウト", 158 | "reset": "標準" 159 | } 160 | }, 161 | "help": { 162 | "title": "ヘルプ", 163 | "about": { 164 | "title": "ヤックヤックについて", 165 | "newer": "新しいバージョンが利用可能です(%s から a %s)。", 166 | "license": "ライセンス", 167 | "authors": "主な作者", 168 | "contributors": "貢献者", 169 | "startup": "起動時に開く" 170 | } 171 | }, 172 | "window": { 173 | "title": "ウインドウ", 174 | "minimize": "しまう", 175 | "close": "閉じる", 176 | "front": "最前にする" 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/locales/ko.json: -------------------------------------------------------------------------------- 1 | { 2 | "__MyLocaleLanguage__": "한국어", 3 | "__maintaner__": "http://github.com/drkein", 4 | 5 | "title": "YakYak - 행아웃 클라이언트", 6 | "description": "구글 행아웃을 위한 데스크탑 클라이언트", 7 | 8 | "settings": "설정", 9 | "recent": "최근", 10 | "details": "상세", 11 | 12 | "connection" :{ 13 | "connecting": "연결중", 14 | "loading": "연락처 로딩", 15 | "not_connected": "연결 안됨 (연결 확인)" 16 | }, 17 | 18 | "input": { 19 | "message": "메시지", 20 | "emoticons": "이모티콘 보기", 21 | "image": "이미지 첨부", 22 | "typing": "입력중" 23 | }, 24 | "conversation": { 25 | "title": { 26 | "one": "대화", 27 | "other": "대화" 28 | }, 29 | "numbered": "대화 %d", 30 | "options": "대화 옵션", 31 | "setting": "대화 설정", 32 | "add": "새 대화 추가", 33 | "new": "새 대화", 34 | "name": "대화 이름", 35 | "edit": "대화 편집", 36 | "multiuser": "그룹 채팅 생성", 37 | "delete": "대화 삭제", 38 | "delete_confirm": "정말로 대화를 삭제 하시겠습니까?", 39 | "leave": "대화 나가기", 40 | "leave_confirm": "정말로 대화를 나가시겠습니까?", 41 | "search": "사용자 검색", 42 | "new_message": "새 메시지", 43 | "new_attachment": "New message received (image or video)", 44 | "open_link": "Opening the link in the browser...", 45 | "no_preview_image_click_to_open": "Image preview is disabled: click to open it in the browser" 46 | }, 47 | "notification": { 48 | "one": "알림", 49 | "other": "알림" 50 | }, 51 | "favorite": { 52 | "title": { 53 | "one": "즐겨찾기", 54 | "other": "즐겨찾기" 55 | }, 56 | "star_it": "추가 / 삭제" 57 | }, 58 | "actions": { 59 | "ok": "확인", 60 | "cancel": "취소" 61 | }, 62 | "call": { 63 | "incoming": "전화 수신", 64 | "incoming_from": "전화 수신 %s", 65 | "accepted": "수락됨", 66 | "accept": "수락", 67 | "rejected": "거절됨", 68 | "reject": "거절", 69 | "missed": "부재중 전화 %s" 70 | }, 71 | "menu": { 72 | "title": "메뉴", 73 | "file": { 74 | "title": "YakYak", 75 | "hide": "YakYak 숨기기", 76 | "hide_others": "다른창 숨기기", 77 | "show": "모두 보기", 78 | "inspector": "개발도구 열기", 79 | "logout": "로그아웃", 80 | "quit": "종료" 81 | }, 82 | "edit": { 83 | "title": "편집", 84 | "undo": "실행 취소", 85 | "redo": "실행 복구", 86 | "paste": "붙이기", 87 | "cut": "자르기", 88 | "copy": "복사", 89 | "copy_image_link": "이미지 링크 복사", 90 | "copy_link": "링크 복사", 91 | "copy_image": "이미지 복사", 92 | "select_all": "전체 선택", 93 | "language": "한국어 (Language)", 94 | "dateformat": "Use system date format" 95 | }, 96 | "view": { 97 | "title": "보기", 98 | "emoji": "텍스트를 이모지로 변환", 99 | "suggestemoji": "Suggest emoji on typing", 100 | "showimagepreview": "Show image preview", 101 | "hide_dock": "독 아이콘 숨기기", 102 | "fullscreen": "전체화면 전환", 103 | "tray": { 104 | "show_tray": "트레이 아이콘 보기", 105 | "show_window": "윈도우 보기", 106 | "toggle_minimize": "트레이 최소화 토글", 107 | "start_minimize": "트레이 최소화", 108 | "close": "트레이로 닫기" 109 | }, 110 | "escape": { 111 | "title": "ESC키 동작", 112 | "hide": "윈도우 숨기기", 113 | "clear": "입력창 지우기", 114 | "default": "트레이 숨김시 기본동작" 115 | }, 116 | "conversation": { 117 | "title": "대화 목록", 118 | "timestamp": "대화 시간 표시", 119 | "last": "마지막 대화 메시지 보기", 120 | "previous": "이전 대화", 121 | "next": "다음 대화", 122 | "select": "대화 선택", 123 | "thumbnails": { 124 | "show": "썸네일 보기", 125 | "only": "썸네일만 보기", 126 | "animated": "썸네일 애니메이션" 127 | } 128 | }, 129 | "notification": { 130 | "title": "팝업 알림", 131 | "show": "알림 보기", 132 | "toggle": "알림 토글", 133 | "message": "알림에 메시지 보기", 134 | "username": "알림에 이름 보기", 135 | "mute": "알림에 소리 끄기", 136 | "avatar": "알림에 사용자 아바타 보기", 137 | "icon": "알림에 YakYak 아이콘 보기", 138 | "custom_sound": "알림에 YakYak 사운드 사용" 139 | }, 140 | "color_scheme": { 141 | "title": "색상 스킴", 142 | "default": "오리지널", 143 | "blue": "블루", 144 | "dark": "다크", 145 | "material": "매터리얼" 146 | }, 147 | "font": { 148 | "title": "폰트 크기", 149 | "extra_small": "매우 작음", 150 | "small": "작음", 151 | "medium": "중간", 152 | "large": "큼", 153 | "extra_large": "매우 큼" 154 | }, 155 | "zoom": { 156 | "in": "줌 인", 157 | "out": "줌 아웃", 158 | "reset": "실제 크기" 159 | } 160 | }, 161 | "help": { 162 | "title": "도움말", 163 | "about": { 164 | "title": "YakYak에 대해서", 165 | "newer": "새로운 버전이 있습니다. %s 에서 %s 로 업그레이드 해주세요.", 166 | "license": "라이센스", 167 | "authors": "주 저자", 168 | "contributors": "기여자", 169 | "startup": "시스템 시작시 열기" 170 | } 171 | }, 172 | "window": { 173 | "title": "윈도우", 174 | "minimize": "최소화", 175 | "close": "닫기", 176 | "front": "모두 앞으로 보내기" 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/locales/pl.json: -------------------------------------------------------------------------------- 1 | { 2 | "__MyLocaleLanguage__": "Polski", 3 | "__maintaner__": "https://github.com/aPoCoMiLogin", 4 | 5 | "title": "YakYak - Komunikator dla Hangouts", 6 | "description": "Komunikator dla Google Hangouts", 7 | 8 | "settings": "Preferencje", 9 | "recent": "Ostatnie", 10 | "details": "Szczegóły", 11 | 12 | "connection" :{ 13 | "connecting": "Łączę", 14 | "loading": "Pobieram kontakty", 15 | "not_connected": "Brak połączenia (sprawdź połączenie)" 16 | }, 17 | 18 | "input": { 19 | "message": "Wiadomość", 20 | "emoticons": "Pokaż emotikony", 21 | "image": "Dołącz zdjęcie", 22 | "typing": "pisze" 23 | }, 24 | "conversation": { 25 | "title": { 26 | "one": "Rozmowa", 27 | "other": "Rozmowy" 28 | }, 29 | "numbered": "Rozmowa %d", 30 | "options": "Opcje rozmowy", 31 | "setting": "Ustawienia rozmowy", 32 | "add": "Dodaj nową rozmowę", 33 | "new": "Nowa rozmowa", 34 | "name": "Nazwa rozmowy", 35 | "edit": "Edycja rozmowy", 36 | "multiuser": "Utwórz rozmowę grupową", 37 | "delete": "Usuń rozmowę", 38 | "delete_confirm": "Na pewno chcesz usunąć rozmowę?", 39 | "leave": "Opuść rozmowę", 40 | "leave_confirm": "Na pewno chcesz opuścić rozmowę?", 41 | "search": "Szukaj uczestników", 42 | "new_message": "Nowa Wiadomość", 43 | "new_attachment": "New message received (image or video)", 44 | "open_link": "Opening the link in the browser...", 45 | "no_preview_image_click_to_open": "Image preview is disabled: click to open it in the browser" 46 | }, 47 | "notification": { 48 | "one": "Powiadomienie", 49 | "other": "Powiadomienia" 50 | }, 51 | "favorite": { 52 | "title": { 53 | "one": "Ulubiony", 54 | "other": "Ulubione" 55 | }, 56 | "star_it": "Zaznacz/Odznacz gwiazdką" 57 | }, 58 | "actions": { 59 | "ok": "OK", 60 | "cancel": "Anuluj" 61 | }, 62 | "call": { 63 | "incoming": "Połączenie przychodzące", 64 | "incoming_from": "Połączenie przychodzące od %s", 65 | "accepted": "Zaakceptowane", 66 | "accept": "Akceptuj", 67 | "rejected": "Odrzucone", 68 | "reject": "Odrzuć", 69 | "missed": "Nieodebrane połączenie od %s" 70 | }, 71 | "menu": { 72 | "title": "Menu", 73 | "file": { 74 | "title": "YakYak", 75 | "hide": "Ukryj YakYak", 76 | "hide_others": "Ukryj pozostałe", 77 | "show": "Pokaż wszystkie", 78 | "inspector": "Otwórz panel Inspektora", 79 | "logout": "Wyloguj", 80 | "quit": "Zakończ" 81 | }, 82 | "edit": { 83 | "title": "Edycja", 84 | "undo": "Cofnij", 85 | "redo": "Przywróć", 86 | "paste": "Wklej", 87 | "cut": "Wytnij", 88 | "copy": "Kopiuj", 89 | "copy_image_link": "Kopiuj adres obrazu", 90 | "copy_link": "Kopiuj adres linku", 91 | "copy_image": "Kopiuj adres linku", 92 | "select_all": "Zaznacz wszystko", 93 | "language": "Język (Language)", 94 | "dateformat": "Use system date format" 95 | }, 96 | "view": { 97 | "title": "Widok", 98 | "emoji": "Zamień tekst na emoji", 99 | "suggestemoji": "Suggest emoji on typing", 100 | "showimagepreview": "Show image preview", 101 | "hide_dock": "Ukryj ikonę w Docku", 102 | "fullscreen": "Tryb Pełnoekranowy", 103 | "tray": { 104 | "show_tray": "Pokaż ikonę w menu statusu", 105 | "show_window": "Pokaż okno", 106 | "toggle_minimize": "Minimalizuj do menu statusu", 107 | "start_minimize": "Uruchom zminimalizowany", 108 | "close": "Zamknij do menu statusu" 109 | }, 110 | "escape": { 111 | "title": "Zachowanie klawisza Esc", 112 | "hide": "Ukrywa okno", 113 | "clear": "Czyści wpisany tekst", 114 | "default": "domyślne gdy brak ikony w menu" 115 | }, 116 | "conversation": { 117 | "title": "Lista rozmów", 118 | "timestamp": "Pokaż datę i godzinę w rozmowach", 119 | "last": "Pokaż ostatnią wiadomość", 120 | "previous": "Poprzednia rozmowa", 121 | "next": "Następna rozmowa", 122 | "select": "Wybierz rozmowę", 123 | "thumbnails": { 124 | "show": "Pokaż miniaturki", 125 | "only": "Tylko miniaturki", 126 | "animated": "Pokaż animowane miniaturki" 127 | } 128 | }, 129 | "notification": { 130 | "title": "Wyskakujące powiadomienia", 131 | "show": "Pokaż powiadomienia", 132 | "toggle": "Przełącz powiadomienia", 133 | "message": "Pokaż wiadomość w powiadomieniach", 134 | "username": "Pokaż nadawcę w powiadomieniach", 135 | "mute": "Wyłącz dźwięki powiadomień", 136 | "avatar": "Pokaż avatar nadawcy w powiadomieniach", 137 | "icon": "Pokaż ikonę YakYak w powiadomieniach", 138 | "custom_sound": "Użyj niestandardowego dźwięku powiadomień" 139 | }, 140 | "color_scheme": { 141 | "title": "Schemat kolorów", 142 | "default": "Domyślny", 143 | "blue": "Niebieski", 144 | "dark": "Ciemny", 145 | "material": "Material" 146 | }, 147 | "font": { 148 | "title": "Czcionka", 149 | "extra_small": "Bardzo Mała", 150 | "small": "Mała", 151 | "medium": "Średnia", 152 | "large": "Duża", 153 | "extra_large": "Bardzo Duża" 154 | }, 155 | "zoom": { 156 | "in": "Powiększ", 157 | "out": "Pomniejsz", 158 | "reset": "Wielkość rzeczywista" 159 | } 160 | }, 161 | "help": { 162 | "title": "Pomoc", 163 | "about": { 164 | "title": "O YakYak", 165 | "newer": "Dostępna jest nowa wersja, uaktualnij z %s do %s", 166 | "license": "Licencja", 167 | "authors": "Główni autorzy", 168 | "contributors": "Współautorzy", 169 | "startup": "Otwórz podczas uruchamiania systemu" 170 | } 171 | }, 172 | "window": { 173 | "title": "Okno", 174 | "minimize": "Minimalizuj okno", 175 | "close": "Zamknij", 176 | "front": "Umieść wszystko na wierzchu" 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/locales/zh_CN.json: -------------------------------------------------------------------------------- 1 | { 2 | "__MyLocaleLanguage__": "简体中文", 3 | "__maintaner__": "http://github.com/HenryHu", 4 | 5 | "title": "YakYak - Hangouts 客户端", 6 | "description": "Google Hangouts 桌面客户端", 7 | 8 | "settings": "设置", 9 | "recent": "最近", 10 | "details": "详细", 11 | 12 | "connection" :{ 13 | "connecting": "连接中", 14 | "loading": "正在载入联系人", 15 | "not_connected": "未连接 (请检查网络)" 16 | }, 17 | 18 | "input": { 19 | "message": "消息", 20 | "emoticons": "显示表情", 21 | "image": "附加图片", 22 | "typing": "正在输入" 23 | }, 24 | "conversation": { 25 | "title": { 26 | "one": "对话", 27 | "other": "对话" 28 | }, 29 | "numbered": "对话 %d", 30 | "options": "对话选项", 31 | "setting": "对话设置", 32 | "add": "发起新对话", 33 | "new": "新对话", 34 | "name": "对话名称", 35 | "edit": "编辑对话", 36 | "multiuser": "创建多用户聊天", 37 | "delete": "删除对话", 38 | "delete_confirm": "真的要删除对话吗?", 39 | "leave": "离开对话", 40 | "leave_confirm": "真的要离开对话吗?", 41 | "search": "搜索用户", 42 | "new_message": "收到新消息", 43 | "new_attachment": "收到新消息 (图片或视频)", 44 | "open_link": "在浏览器中打开链接……", 45 | "no_preview_image_click_to_open": "Image preview is disabled: click to open it in the browser" 46 | }, 47 | "notification": { 48 | "one": "通知", 49 | "other": "通知" 50 | }, 51 | "favorite": { 52 | "title": { 53 | "one": "收藏", 54 | "other": "收藏" 55 | }, 56 | "star_it": "星号标记 / 取消星号标记" 57 | }, 58 | "actions": { 59 | "ok": "好", 60 | "cancel": "取消" 61 | }, 62 | "call": { 63 | "incoming": "来电", 64 | "incoming_from": "自 %s 的来电", 65 | "accepted": "已接受", 66 | "accept": "接受", 67 | "rejected": "已拒绝", 68 | "reject": "拒绝", 69 | "missed": "错过了自 %s 的来电" 70 | }, 71 | "menu": { 72 | "title": "菜单", 73 | "file": { 74 | "title": "YakYak", 75 | "hide": "隐藏 YakYak", 76 | "hide_others": "隐藏其他", 77 | "show": "显示所有", 78 | "inspector": "打开 Inspector", 79 | "logout": "注销", 80 | "quit": "退出" 81 | }, 82 | "edit": { 83 | "title": "编辑", 84 | "undo": "撤销", 85 | "redo": "重做", 86 | "paste": "粘贴", 87 | "cut": "剪切", 88 | "copy": "复制", 89 | "copy_image_link": "复制图像链接", 90 | "copy_link": "复制链接", 91 | "copy_image": "复制图像", 92 | "select_all": "选择所有", 93 | "language": "语言 (Language)", 94 | "dateformat": "使用系统日期格式" 95 | }, 96 | "view": { 97 | "title": "视图", 98 | "emoji": "将文字转化为表情", 99 | "suggestemoji": "输入时建议表情", 100 | "showimagepreview": "Show image preview", 101 | "hide_dock": "隐藏停靠栏图标", 102 | "fullscreen": "切换全屏", 103 | "tray": { 104 | "main": "托盘图标", 105 | "show_tray": "切换窗口显示/隐藏", 106 | "show_window": "显示窗口", 107 | "toggle_minimize": "切换是否最小化到托盘", 108 | "start_minimize": "启动时最小化到托盘", 109 | "close": "关闭到托盘" 110 | }, 111 | "escape": { 112 | "title": "Esc 键行为", 113 | "hide": "隐藏窗口", 114 | "clear": "清空输入", 115 | "default": "托盘图标不显示时此为默认行为" 116 | }, 117 | "conversation": { 118 | "title": "对话列表", 119 | "timestamp": "显示对话时间戳", 120 | "last": "显示对话的最后消息", 121 | "previous": "上一个对话", 122 | "next": "下一个对话", 123 | "select": "选择对话", 124 | "thumbnails": { 125 | "show": "显示缩略图", 126 | "only": "仅显示缩略图", 127 | "animated": "显示动态缩略图" 128 | } 129 | }, 130 | "notification": { 131 | "title": "弹出式通知", 132 | "show": "显示通知", 133 | "toggle": "切换通知", 134 | "message": "在通知中显示消息", 135 | "username": "在通知中显示用户名", 136 | "mute": "禁用通知声音", 137 | "avatar": "在通知中显示用户头像", 138 | "icon": "在通知中显示 YakYak 图标", 139 | "custom_sound": "使用 YakYak 自定义提示音" 140 | }, 141 | "color_scheme": { 142 | "title": "颜色主题", 143 | "default": "原版", 144 | "blue": "蓝色", 145 | "dark": "深色", 146 | "material": "Material", 147 | "pop": "Pop", 148 | "gruvy": "Gruvy" 149 | }, 150 | "font": { 151 | "title": "文字大小", 152 | "extra_small": "极小", 153 | "small": "小", 154 | "medium": "中", 155 | "large": "大", 156 | "extra_large": "极大" 157 | }, 158 | "zoom": { 159 | "in": "放大", 160 | "out": "缩小", 161 | "reset": "标准大小" 162 | } 163 | }, 164 | "help": { 165 | "title": "帮助", 166 | "about": { 167 | "title": "关于 YakYak", 168 | "newer": "有新版本可用,请从 %s 升级到 %s", 169 | "license": "协议", 170 | "authors": "主要作者", 171 | "contributors": "贡献者", 172 | "startup": "启动时开启" 173 | } 174 | }, 175 | "window": { 176 | "title": "窗口", 177 | "minimize": "最小化", 178 | "close": "关闭", 179 | "front": "全部前置" 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/locales/zh_TW.json: -------------------------------------------------------------------------------- 1 | { 2 | "__MyLocaleLanguage__": "繁體中文", 3 | "__maintaner__": "http://github.com/olivertzeng", 4 | 5 | "title": "YakYak - Hangouts 客戶端", 6 | "description": "Google Hangouts 桌面客戶端", 7 | 8 | "settings": "選項", 9 | "recent": "最近", 10 | "details": "資訊", 11 | 12 | "connection" :{ 13 | "connecting": "連接中", 14 | "loading": "正在載入聯絡人", 15 | "not_connected": "無法連接" 16 | }, 17 | 18 | "input": { 19 | "message": "訊息", 20 | "emoticons": "表情", 21 | "image": "圖片", 22 | "typing": "輸入中" 23 | }, 24 | "conversation": { 25 | "title": { 26 | "one": "對話", 27 | "other": "對話" 28 | }, 29 | "numbered": "對話 %d", 30 | "options": "對話選項", 31 | "setting": "對話選項", 32 | "add": "發起新的對話", 33 | "new": "發起新的對話", 34 | "name": "對話名稱", 35 | "edit": "編輯", 36 | "multiuser": "新增群組", 37 | "delete": "刪除", 38 | "delete_confirm": "真的要刪除對話媽?", 39 | "leave": "離開", 40 | "leave_confirm": "真的要離開對話媽?", 41 | "search": "搜尋使用者", 42 | "new_message": "收到新消息", 43 | "new_attachment": "收到新消息 (图片或视频)", 44 | "open_link": "在瀏覽器中打開連結……", 45 | "no_preview_image_click_to_open": "圖片檢視器已停用,點節圖片已在瀏覽氣上打開連結" 46 | }, 47 | "notification": { 48 | "one": "通知", 49 | "other": "通知" 50 | }, 51 | "favorite": { 52 | "title": { 53 | "one": "收藏", 54 | "other": "收藏" 55 | }, 56 | "star_it": "收藏 / 取消收藏" 57 | }, 58 | "actions": { 59 | "ok": "好", 60 | "cancel": "取消" 61 | }, 62 | "call": { 63 | "incoming": "來電", 64 | "incoming_from": "自 %s 的來電", 65 | "accepted": "已接受", 66 | "accept": "接受", 67 | "rejected": "已拒絕", 68 | "reject": "拒絕", 69 | "missed": "自 %s 的未接來電" 70 | }, 71 | "menu": { 72 | "title": "選項", 73 | "file": { 74 | "title": "YakYak", 75 | "hide": "隱藏 YakYak", 76 | "hide_others": "隱藏其他", 77 | "show": "顯示", 78 | "inspector": "開啟 Inspector", 79 | "logout": "登出", 80 | "quit": "退出" 81 | }, 82 | "edit": { 83 | "title": "編輯", 84 | "undo": "復原", 85 | "redo": "重做", 86 | "paste": "貼上", 87 | "cut": "剪下", 88 | "copy": "複製", 89 | "copy_image_link": "複製圖片連結", 90 | "copy_link": "複製連結", 91 | "copy_image": "複製圖片", 92 | "select_all": "全選", 93 | "language": "語言 (Language)", 94 | "dateformat": "使用系統日期格式" 95 | }, 96 | "view": { 97 | "title": "查看", 98 | "emoji": "表情", 99 | "suggestemoji": "輸入時建議表情", 100 | "showimagepreview": "顯示圖片檢視器", 101 | "hide_dock": "隱藏側欄", 102 | "fullscreen": "全螢幕", 103 | "tray": { 104 | "main": "托盤圖示", 105 | "show_tray": "切換視窗顯示/隱藏", 106 | "show_window": "顯示視窗", 107 | "toggle_minimize": "切換是否最小化到托盤", 108 | "start_minimize": "啟動時最小化到托盤", 109 | "close": "關閉到托盤" 110 | }, 111 | "escape": { 112 | "title": "Esc 鍵行為", 113 | "hide": "隱藏視窗", 114 | "clear": "清空", 115 | "default": "預設" 116 | }, 117 | "conversation": { 118 | "title": "群組列表", 119 | "timestamp": "時間戳", 120 | "last": "顯示對話最新訊息", 121 | "previous": "上一則對話", 122 | "next": "下一則對話", 123 | "select": "選擇", 124 | "thumbnails": { 125 | "show": "顯示縮圖", 126 | "only": "只顯示縮圖", 127 | "animated": "顯示動態縮圖" 128 | } 129 | }, 130 | "notification": { 131 | "title": "通知", 132 | "show": "顯示通知", 133 | "toggle": "切換通知", 134 | "message": "在通知中顯示訊息", 135 | "username": "在通知中顯示使用者", 136 | "mute": "禁用通知鈴聲", 137 | "avatar": "在通知中顯示使用者頭像", 138 | "icon": "在通知中顯示 YakYak 圖示", 139 | "custom_sound": "使用 YakYak 自訂提示音" 140 | }, 141 | "color_scheme": { 142 | "title": "顏色主題", 143 | "default": "預設", 144 | "blue": "藍色", 145 | "dark": "深色", 146 | "material": "材質", 147 | "pop": "咖啡", 148 | "gruvy": "黑色" 149 | }, 150 | "font": { 151 | "title": "文字大小", 152 | "extra_small": "極小", 153 | "small": "小", 154 | "medium": "中", 155 | "large": "大", 156 | "extra_large": "極大" 157 | }, 158 | "zoom": { 159 | "in": "放大", 160 | "out": "縮小", 161 | "reset": "重設" 162 | } 163 | }, 164 | "help": { 165 | "title": "幫助", 166 | "about": { 167 | "title": "關於", 168 | "newer": "有新版本可用,請從 %s 升級到 %s", 169 | "license": "證照", 170 | "authors": "作者", 171 | "contributors": "貢獻者", 172 | "startup": "開機時自動啟動" 173 | } 174 | }, 175 | "window": { 176 | "title": "視窗", 177 | "minimize": "最小化", 178 | "close": "關閉", 179 | "front": "全部前置" 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/login.coffee: -------------------------------------------------------------------------------- 1 | Client = require 'hangupsjs' 2 | Q = require 'q' 3 | {session} = require('electron') 4 | app = require('electron').app 5 | 6 | # Current programmatic login workflow is described here 7 | # https://github.com/tdryer/hangups/issues/260#issuecomment-246578670 8 | LOGIN_URL = "https://accounts.google.com/o/oauth2/programmatic_auth?hl=en&scope=https%3A%2F%2Fwww.google.com%2Faccounts%2FOAuthLogin+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&client_id=936475272427.apps.googleusercontent.com&access_type=offline&delegated_client_id=183697946088-m3jnlsqshjhh5lbvg05k46q1k4qqtrgn.apps.googleusercontent.com&top_level_cookie=1" 9 | 10 | # Hack the user agent so this works again 11 | # Credit to https://github.com/yakyak/yakyak/issues/1087#issuecomment-565170640 12 | # 13 | # WARN:: This should be removed in the long term. 14 | AGENT = app.userAgentFallback 15 | .replace('Chrome', 'Chromium') 16 | 17 | # promise for one-time oauth token 18 | module.exports = (mainWindow) -> Q.Promise (rs, reject) -> 19 | 20 | mainWindow.webContents.on 'did-finish-load', onDidFinishLoad = -> 21 | 22 | # the url that just finished loading 23 | url = mainWindow.getURL() 24 | console.log 'login: did-finish-load', url 25 | 26 | if url.indexOf('/signin/rejected') > 0 and url.indexOf('rrk=47') > 0 27 | console.error 'javascript disabled, testing logout...' 28 | reject 'logout' 29 | 30 | else if url.indexOf('/o/oauth2/programmatic_auth') > 0 31 | console.log 'login: programmatic auth' 32 | # get the cookie from browser session, it has to be there 33 | session.defaultSession.cookies.get({}) 34 | .then (values=[]) -> 35 | oauth_code = false 36 | for value in values 37 | if value.name is 'oauth_code' 38 | oauth_code = value.value 39 | rs(oauth_code) if oauth_code 40 | .catch (err) -> 41 | console.log 'login: ERROR retrieving cookies::', err 42 | 43 | # redirect to google oauth 44 | options = {"userAgent": AGENT} 45 | mainWindow.loadURL LOGIN_URL, options 46 | -------------------------------------------------------------------------------- /src/media/new_message.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/media/new_message.ogg -------------------------------------------------------------------------------- /src/seqreq.coffee: -------------------------------------------------------------------------------- 1 | 2 | totallyunique = (as...) -> String(Date.now()) + (Math.random() * 1000000) 3 | 4 | # fn is expected to return a promised that finishes 5 | # when fn is finished. 6 | # 7 | # retry is whether we retry failures of fn 8 | # 9 | # dedupe is a function that mashes the arguments to fn 10 | # into a dedupe value. 11 | module.exports = (fn, retry, dedupe = totallyunique) -> 12 | 13 | queue = [] # the queue of args to exec 14 | deduped = [] # the dedupe(args) for deduping 15 | execing = false # flag indicating whether execNext is running 16 | 17 | # will perpetually exec next until queue is empty 18 | execNext = -> 19 | unless queue.length 20 | execing = false 21 | return 22 | execing = true 23 | args = queue[0] # next args to try 24 | fn(args...).then -> 25 | # it finished, drop args 26 | queue.shift(); deduped.shift() 27 | .fail (err) -> 28 | # it failed. 29 | # no retry? then just drop args 30 | (queue.shift(); deduped.shift()) unless retry 31 | .then -> 32 | execNext() 33 | 34 | (as...) -> 35 | d = dedupe as... 36 | if (i = deduped.indexOf(d)) >= 0 37 | # replace entry, notice this can replace 38 | # a currently execing entry 39 | queue[i] = as 40 | else 41 | # push a new entry 42 | queue.push as 43 | deduped.push d 44 | execNext() unless execing 45 | -------------------------------------------------------------------------------- /src/ui/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | YakYak 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ui/css/manifest.less: -------------------------------------------------------------------------------- 1 | @import 'css/material-icons/material-icons.css'; 2 | @import 'roboto-fontface/css/roboto-fontface'; 3 | @import 'noto-color-emoji/noto-color-emoji'; 4 | @roboto-font-path: 'css/roboto-fontface/fonts'; 5 | 6 | @import 'yakyak/colors'; 7 | @import 'yakyak/fonts'; 8 | @import 'yakyak/global'; 9 | @import 'yakyak/applayout'; 10 | @import 'yakyak/controls'; 11 | @import 'yakyak/convadd'; 12 | @import 'yakyak/convhead'; 13 | @import 'yakyak/convlist'; 14 | @import 'yakyak/input'; 15 | @import 'yakyak/listhead'; 16 | @import 'yakyak/messages'; 17 | @import 'yakyak/typinginfo'; 18 | @import 'yakyak/about'; 19 | -------------------------------------------------------------------------------- /src/ui/css/material-icons/MaterialIcons-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/material-icons/MaterialIcons-Regular.eot -------------------------------------------------------------------------------- /src/ui/css/material-icons/MaterialIcons-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/material-icons/MaterialIcons-Regular.ttf -------------------------------------------------------------------------------- /src/ui/css/material-icons/MaterialIcons-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/material-icons/MaterialIcons-Regular.woff -------------------------------------------------------------------------------- /src/ui/css/material-icons/MaterialIcons-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/material-icons/MaterialIcons-Regular.woff2 -------------------------------------------------------------------------------- /src/ui/css/material-icons/README.md: -------------------------------------------------------------------------------- 1 | The recommended way to use the Material Icons font is by linking to the web font hosted on Google Fonts: 2 | 3 | ```html 4 | 6 | ``` 7 | 8 | Read more in our full usage guide: 9 | http://google.github.io/material-design-icons/#icon-font-for-the-web 10 | -------------------------------------------------------------------------------- /src/ui/css/material-icons/material-icons.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Material Icons'; 3 | font-style: normal; 4 | font-weight: 400; 5 | src: url(MaterialIcons-Regular.eot); /* For IE6-8 */ 6 | src: local('Material Icons'), 7 | local('MaterialIcons-Regular'), 8 | url(MaterialIcons-Regular.woff2) format('woff2'), 9 | url(MaterialIcons-Regular.woff) format('woff'), 10 | url(MaterialIcons-Regular.ttf) format('truetype'); 11 | } 12 | 13 | .material-icons { 14 | font-family: 'Material Icons'; 15 | font-weight: normal; 16 | font-style: normal; 17 | font-size: 24px; /* Preferred icon size */ 18 | display: inline-block; 19 | width: 1em; 20 | height: 1em; 21 | line-height: 1; 22 | text-transform: none; 23 | letter-spacing: normal; 24 | word-wrap: normal; 25 | white-space: nowrap; 26 | direction: ltr; 27 | 28 | /* Support for all WebKit browsers. */ 29 | -webkit-font-smoothing: antialiased; 30 | /* Support for Safari and Chrome. */ 31 | text-rendering: optimizeLegibility; 32 | 33 | /* Support for Firefox. */ 34 | -moz-osx-font-smoothing: grayscale; 35 | 36 | /* Support for IE. */ 37 | font-feature-settings: 'liga'; 38 | } 39 | -------------------------------------------------------------------------------- /src/ui/css/noto-color-emoji/LICENSE_OFL.txt: -------------------------------------------------------------------------------- 1 | This Font Software is licensed under the SIL Open Font License, 2 | Version 1.1. 3 | 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | ----------------------------------------------------------- 8 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 9 | ----------------------------------------------------------- 10 | 11 | PREAMBLE 12 | The goals of the Open Font License (OFL) are to stimulate worldwide 13 | development of collaborative font projects, to support the font 14 | creation efforts of academic and linguistic communities, and to 15 | provide a free and open framework in which fonts may be shared and 16 | improved in partnership with others. 17 | 18 | The OFL allows the licensed fonts to be used, studied, modified and 19 | redistributed freely as long as they are not sold by themselves. The 20 | fonts, including any derivative works, can be bundled, embedded, 21 | redistributed and/or sold with any software provided that any reserved 22 | names are not used by derivative works. The fonts and derivatives, 23 | however, cannot be released under any other type of license. The 24 | requirement for fonts to remain under this license does not apply to 25 | any document created using the fonts or their derivatives. 26 | 27 | DEFINITIONS 28 | "Font Software" refers to the set of files released by the Copyright 29 | Holder(s) under this license and clearly marked as such. This may 30 | include source files, build scripts and documentation. 31 | 32 | "Reserved Font Name" refers to any names specified as such after the 33 | copyright statement(s). 34 | 35 | "Original Version" refers to the collection of Font Software 36 | components as distributed by the Copyright Holder(s). 37 | 38 | "Modified Version" refers to any derivative made by adding to, 39 | deleting, or substituting -- in part or in whole -- any of the 40 | components of the Original Version, by changing formats or by porting 41 | the Font Software to a new environment. 42 | 43 | "Author" refers to any designer, engineer, programmer, technical 44 | writer or other person who contributed to the Font Software. 45 | 46 | PERMISSION & CONDITIONS 47 | Permission is hereby granted, free of charge, to any person obtaining 48 | a copy of the Font Software, to use, study, copy, merge, embed, 49 | modify, redistribute, and sell modified and unmodified copies of the 50 | Font Software, subject to the following conditions: 51 | 52 | 1) Neither the Font Software nor any of its individual components, in 53 | Original or Modified Versions, may be sold by itself. 54 | 55 | 2) Original or Modified Versions of the Font Software may be bundled, 56 | redistributed and/or sold with any software, provided that each copy 57 | contains the above copyright notice and this license. These can be 58 | included either as stand-alone text files, human-readable headers or 59 | in the appropriate machine-readable metadata fields within text or 60 | binary files as long as those fields can be easily viewed by the user. 61 | 62 | 3) No Modified Version of the Font Software may use the Reserved Font 63 | Name(s) unless explicit written permission is granted by the 64 | corresponding Copyright Holder. This restriction only applies to the 65 | primary font name as presented to the users. 66 | 67 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 68 | Software shall not be used to promote, endorse or advertise any 69 | Modified Version, except to acknowledge the contribution(s) of the 70 | Copyright Holder(s) and the Author(s) or with their explicit written 71 | permission. 72 | 73 | 5) The Font Software, modified or unmodified, in part or in whole, 74 | must be distributed entirely under this license, and must not be 75 | distributed under any other license. The requirement for fonts to 76 | remain under this license does not apply to any document created using 77 | the Font Software. 78 | 79 | TERMINATION 80 | This license becomes null and void if any of the above conditions are 81 | not met. 82 | 83 | DISCLAIMER 84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 88 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 92 | OTHER DEALINGS IN THE FONT SOFTWARE. 93 | -------------------------------------------------------------------------------- /src/ui/css/noto-color-emoji/NotoColorEmoji.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/noto-color-emoji/NotoColorEmoji.ttf -------------------------------------------------------------------------------- /src/ui/css/noto-color-emoji/noto-color-emoji.less: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'NotoColorEmoji'; 3 | src: url('./css/noto-color-emoji/NotoColorEmoji.ttf') format('truetype'); 4 | font-weight: normal; 5 | font-style: normal; 6 | } 7 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/.npmignore: -------------------------------------------------------------------------------- 1 | # Node 2 | node_modules/ 3 | npm-debug.log 4 | 5 | # OS specific trash 6 | .DS_Store 7 | ._.DS_Store 8 | Thumbs.db 9 | 10 | # Sass-specific 11 | .sass-cache 12 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/README.md: -------------------------------------------------------------------------------- 1 | # Roboto fontface 2 | 3 | A simple package providing the [Roboto](http://www.google.com/fonts/specimen/Roboto) fontface. The font was created by [Christian Robertson](https://plus.google.com/110879635926653430880/about). 4 | 5 | ## Installing 6 | 7 | Assuming you have [NodeJS](http://nodejs.org/), [NPM](https://www.npmjs.com/) and [Bower](http://bower.io/) installed globally just open up a terminal, navigate to your projects root directory and then execute 8 | 9 | ``` 10 | # install via NPM 11 | $ npm install roboto-fontface --save 12 | 13 | # install via Bower 14 | $ bower install roboto-fontface --save 15 | ``` 16 | 17 | 18 | ## Usage 19 | 20 | There're several files in the `css/` subdirectory. Import them in your project 21 | to have access to "Roboto" font face: 22 | 23 | * `roboto-fontface.css` - whole font family compiled to CSS 24 | * `roboto-fontface.scss` - whole font family in SCSS 25 | * `roboto-fontface.less` - whole font family in LESS 26 | 27 | Importing whole family may be unnecessary and lead to huge build, so if you are 28 | using SCSS or LESS, you can import only individual weights by importing one 29 | of the following files: 30 | 31 | * `roboto-fontface-(thin|light|regular|medium|bold|black).scss` 32 | * `roboto-fontface-(thin|light|regular|medium|bold|black).less` 33 | 34 | Their italic variants can be imported by adding `-italic` suffix: 35 | 36 | * `roboto-fontface-(thin|light|regular|medium|bold|black)-italic.scss` 37 | * `roboto-fontface-(thin|light|regular|medium|bold|black)-italic.less` 38 | 39 | ## Hinting 40 | 41 | Some of the included font files have [hinting](http://en.wikipedia.org/wiki/Font_hinting). 42 | 43 | | Files | Hinting | 44 | |----------|---------| 45 | | `.eot` | ? | 46 | | `.svg` | no | 47 | | `.ttf` | ? | 48 | | `.woff` | yes | 49 | | `.woff2` | ? | 50 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "roboto-fontface", 3 | "description": "A simple package providing the Roboto fontface.", 4 | "version": "0.4.5", 5 | "main": [ 6 | "./css/roboto-fontface.css", 7 | "./fonts/Roboto-Black.eot", 8 | "./fonts/Roboto-Black.svg", 9 | "./fonts/Roboto-Black.ttf", 10 | "./fonts/Roboto-Black.woff", 11 | "./fonts/Roboto-Black.woff2", 12 | "./fonts/Roboto-BlackItalic.eot", 13 | "./fonts/Roboto-BlackItalic.svg", 14 | "./fonts/Roboto-BlackItalic.ttf", 15 | "./fonts/Roboto-BlackItalic.woff", 16 | "./fonts/Roboto-BlackItalic.woff2", 17 | "./fonts/Roboto-Bold.eot", 18 | "./fonts/Roboto-Bold.svg", 19 | "./fonts/Roboto-Bold.ttf", 20 | "./fonts/Roboto-Bold.woff", 21 | "./fonts/Roboto-Bold.woff2", 22 | "./fonts/Roboto-BoldItalic.eot", 23 | "./fonts/Roboto-BoldItalic.svg", 24 | "./fonts/Roboto-BoldItalic.ttf", 25 | "./fonts/Roboto-BoldItalic.woff", 26 | "./fonts/Roboto-BoldItalic.woff2", 27 | "./fonts/Roboto-Light.eot", 28 | "./fonts/Roboto-Light.svg", 29 | "./fonts/Roboto-Light.ttf", 30 | "./fonts/Roboto-Light.woff", 31 | "./fonts/Roboto-Light.woff2", 32 | "./fonts/Roboto-LightItalic.eot", 33 | "./fonts/Roboto-LightItalic.svg", 34 | "./fonts/Roboto-LightItalic.ttf", 35 | "./fonts/Roboto-LightItalic.woff", 36 | "./fonts/Roboto-LightItalic.woff2", 37 | "./fonts/Roboto-Medium.eot", 38 | "./fonts/Roboto-Medium.svg", 39 | "./fonts/Roboto-Medium.ttf", 40 | "./fonts/Roboto-Medium.woff", 41 | "./fonts/Roboto-Medium.woff2", 42 | "./fonts/Roboto-MediumItalic.eot", 43 | "./fonts/Roboto-MediumItalic.svg", 44 | "./fonts/Roboto-MediumItalic.ttf", 45 | "./fonts/Roboto-MediumItalic.woff", 46 | "./fonts/Roboto-MediumItalic.woff2", 47 | "./fonts/Roboto-Regular.eot", 48 | "./fonts/Roboto-Regular.svg", 49 | "./fonts/Roboto-Regular.ttf", 50 | "./fonts/Roboto-Regular.woff", 51 | "./fonts/Roboto-Regular.woff2", 52 | "./fonts/Roboto-RegularItalic.eot", 53 | "./fonts/Roboto-RegularItalic.svg", 54 | "./fonts/Roboto-RegularItalic.ttf", 55 | "./fonts/Roboto-RegularItalic.woff", 56 | "./fonts/Roboto-RegularItalic.woff2", 57 | "./fonts/Roboto-Thin.eot", 58 | "./fonts/Roboto-Thin.svg", 59 | "./fonts/Roboto-Thin.ttf", 60 | "./fonts/Roboto-Thin.woff", 61 | "./fonts/Roboto-Thin.woff2", 62 | "./fonts/Roboto-ThinItalic.eot", 63 | "./fonts/Roboto-ThinItalic.svg", 64 | "./fonts/Roboto-ThinItalic.ttf", 65 | "./fonts/Roboto-ThinItalic.woff", 66 | "./fonts/Roboto-ThinItalic.woff2" 67 | ], 68 | "ignore": [ 69 | "**/.*", 70 | "node_modules", 71 | "bower_components", 72 | "test", 73 | "tests" 74 | ] 75 | } 76 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/mixins.less: -------------------------------------------------------------------------------- 1 | @roboto-font-path: '../fonts'; 2 | 3 | .roboto-font(@type, @weight, @style) { 4 | @font-face { 5 | font-family: 'Roboto'; 6 | src: url('@{roboto-font-path}/Roboto-@{type}.eot'); 7 | src: local('Roboto @{type}'), 8 | local('Roboto-@{type}'), 9 | url('@{roboto-font-path}/Roboto-@{type}.eot?#iefix') format('embedded-opentype'), 10 | url('@{roboto-font-path}/Roboto-@{type}.woff2') format('woff2'), 11 | url('@{roboto-font-path}/Roboto-@{type}.woff') format('woff'), 12 | url('@{roboto-font-path}/Roboto-@{type}.ttf') format('truetype'), 13 | url('@{roboto-font-path}/Roboto-@{type}.svg#Roboto') format('svg'); 14 | font-weight: @weight; 15 | font-style: @style; 16 | } 17 | 18 | @font-face { 19 | font-family: 'Roboto-@{type}'; 20 | src: url('@{roboto-font-path}/Roboto-@{type}.eot'); 21 | src: local('Roboto @{type}'), 22 | local('Roboto-@{type}'), 23 | url('@{roboto-font-path}/Roboto-@{type}.eot?#iefix') format('embedded-opentype'), 24 | url('@{roboto-font-path}/Roboto-@{type}.woff2') format('woff2'), 25 | url('@{roboto-font-path}/Roboto-@{type}.woff') format('woff'), 26 | url('@{roboto-font-path}/Roboto-@{type}.ttf') format('truetype'), 27 | url('@{roboto-font-path}/Roboto-@{type}.svg#Roboto') format('svg'); 28 | } 29 | } 30 | 31 | .roboto-font-pair(@type, @weight) { 32 | .roboto-font('@{type}', @weight, normal); 33 | .roboto-font('@{type}Italic', @weight, italic); 34 | } 35 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/mixins.scss: -------------------------------------------------------------------------------- 1 | $roboto-font-path: '../fonts' !default; 2 | 3 | @mixin roboto-font($type, $weight, $style) { 4 | @font-face { 5 | font-family: 'Roboto'; 6 | src: url('#{$roboto-font-path}/Roboto-#{$type}.eot'); 7 | src: local('Roboto #{$type}'), 8 | local('Roboto-#{$type}'), 9 | url('#{$roboto-font-path}/Roboto-#{$type}.eot?#iefix') format('embedded-opentype'), 10 | url('#{$roboto-font-path}/Roboto-#{$type}.woff2') format('woff2'), 11 | url('#{$roboto-font-path}/Roboto-#{$type}.woff') format('woff'), 12 | url('#{$roboto-font-path}/Roboto-#{$type}.ttf') format('truetype'), 13 | url('#{$roboto-font-path}/Roboto-#{$type}.svg#Roboto') format('svg'); 14 | font-weight: $weight; 15 | font-style: $style; 16 | } 17 | 18 | @font-face { 19 | font-family: 'Roboto-#{$type}'; 20 | src: url('#{$roboto-font-path}/Roboto-#{$type}.eot'); 21 | src: local('Roboto #{$type}'), 22 | local('Roboto-#{$type}'), 23 | url('#{$roboto-font-path}/Roboto-#{$type}.eot?#iefix') format('embedded-opentype'), 24 | url('#{$roboto-font-path}/Roboto-#{$type}.woff2') format('woff2'), 25 | url('#{$roboto-font-path}/Roboto-#{$type}.woff') format('woff'), 26 | url('#{$roboto-font-path}/Roboto-#{$type}.ttf') format('truetype'), 27 | url('#{$roboto-font-path}/Roboto-#{$type}.svg#Roboto') format('svg'); 28 | } 29 | } 30 | 31 | @mixin roboto-font-pair($type, $weight) { 32 | @include roboto-font($type, $weight, normal); 33 | @include roboto-font(#{$type}Italic, $weight, italic); 34 | } 35 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-black-italic.less: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | .roboto-font('BlackItalic', 900, italic); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-black-italic.scss: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | @include roboto-font('BlackItalic', 900, italic); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-black.less: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | .roboto-font('Black', 900, normal); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-black.scss: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | @include roboto-font('Black', 900, normal); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-bold-italic.less: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | .roboto-font('BoldItalic', 700, italic); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-bold-italic.scss: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | @include roboto-font('BoldItalic', 700, italic); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-bold.less: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | .roboto-font('Bold', 700, normal); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-bold.scss: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | @include roboto-font('Bold', 700, normal); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-light-italic.less: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | .roboto-font('LightItalic', 300, italic); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-light-italic.scss: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | @include roboto-font('LightItalic', 300, italic); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-light.less: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | .roboto-font('Light', 300, normal); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-light.scss: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | @include roboto-font('Light', 300, normal); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-medium-italic.less: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | .roboto-font('MediumItalic', 500, italic); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-medium-italic.scss: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | @include roboto-font('MediumItalic', 500, italic); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-medium.less: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | .roboto-font('Medium', 500, normal); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-medium.scss: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | @include roboto-font('Medium', 500, normal); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-regular-italic.less: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | .roboto-font('RegularItalic', 400, italic); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-regular-italic.scss: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | @include roboto-font('RegularItalic', 400, italic); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-regular.less: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | .roboto-font('Regular', 400, normal); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-regular.scss: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | @include roboto-font('Regular', 400, normal); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-thin-italic.less: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | .roboto-font('ThinItalic', 100, italic); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-thin-italic.scss: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | @include roboto-font('ThinItalic', 100, italic); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-thin.less: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | .roboto-font('Thin', 100, normal); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface-thin.scss: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | @include roboto-font('Thin', 100, normal); 4 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface.less: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | .roboto-font-pair('Thin', 100); 4 | .roboto-font-pair('Light', 300); 5 | .roboto-font-pair('Regular', 400); 6 | .roboto-font-pair('Medium', 500); 7 | .roboto-font-pair('Bold', 700); 8 | .roboto-font-pair('Black', 900); 9 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/css/roboto-fontface.scss: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | 3 | @include roboto-font-pair('Thin', 100); 4 | @include roboto-font-pair('Light', 300); 5 | @include roboto-font-pair('Regular', 400); 6 | @include roboto-font-pair('Medium', 500); 7 | @include roboto-font-pair('Bold', 700); 8 | @include roboto-font-pair('Black', 900); 9 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Black.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Black.eot -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Black.ttf -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Black.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Black.woff -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Black.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Black.woff2 -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-BlackItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-BlackItalic.eot -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-BlackItalic.ttf -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-BlackItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-BlackItalic.woff -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-BlackItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-BlackItalic.woff2 -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Bold.eot -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Bold.woff -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Bold.woff2 -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-BoldItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-BoldItalic.eot -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-BoldItalic.ttf -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-BoldItalic.woff -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-BoldItalic.woff2 -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Light.eot -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Light.ttf -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Light.woff -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Light.woff2 -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-LightItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-LightItalic.eot -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-LightItalic.ttf -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-LightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-LightItalic.woff -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-LightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-LightItalic.woff2 -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Medium.eot -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Medium.ttf -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Medium.woff -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Medium.woff2 -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-MediumItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-MediumItalic.eot -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-MediumItalic.ttf -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-MediumItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-MediumItalic.woff -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-MediumItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-MediumItalic.woff2 -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Regular.eot -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Regular.woff -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Regular.woff2 -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-RegularItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-RegularItalic.eot -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-RegularItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-RegularItalic.ttf -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-RegularItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-RegularItalic.woff -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-RegularItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-RegularItalic.woff2 -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Thin.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Thin.eot -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Thin.ttf -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Thin.woff -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-Thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-Thin.woff2 -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-ThinItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-ThinItalic.eot -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-ThinItalic.ttf -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-ThinItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-ThinItalic.woff -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/fonts/Roboto-ThinItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/css/roboto-fontface/fonts/Roboto-ThinItalic.woff2 -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "roboto-fontface", 3 | "version": "0.4.5", 4 | "description": "A simple package providing the Roboto fontface.", 5 | "main": "css/roboto-fontface.css", 6 | "scripts": { 7 | "test": "./test.sh" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/choffmeister/roboto-fontface-bower.git" 12 | }, 13 | "keywords": [ 14 | "roboto", 15 | "font", 16 | "fontface" 17 | ], 18 | "author": { 19 | "name": "Christian Hoffmeister", 20 | "email": "mail@choffmeister.de", 21 | "url": "http://choffmeister.de/" 22 | }, 23 | "license": "Apache-2.0", 24 | "bugs": { 25 | "url": "https://github.com/choffmeister/roboto-fontface-bower/issues" 26 | }, 27 | "homepage": "https://github.com/choffmeister/roboto-fontface-bower", 28 | "devDependencies": { 29 | "less": "2.6.0", 30 | "node-sass": "3.4.2" 31 | }, 32 | "gitHead": "ff80b6f5d70c50257cda439fa16d3bd854f9c8ec", 33 | "_id": "roboto-fontface@0.4.5", 34 | "_shasum": "44d52f2e33a990b196a88f6676d8c47ecb6beb0a", 35 | "_from": "roboto-fontface@*", 36 | "_npmVersion": "3.7.2", 37 | "_nodeVersion": "5.3.0", 38 | "_npmUser": { 39 | "name": "choffmeister", 40 | "email": "mail@choffmeister.de" 41 | }, 42 | "dist": { 43 | "shasum": "44d52f2e33a990b196a88f6676d8c47ecb6beb0a", 44 | "tarball": "http://registry.npmjs.org/roboto-fontface/-/roboto-fontface-0.4.5.tgz" 45 | }, 46 | "maintainers": [ 47 | { 48 | "name": "choffmeister", 49 | "email": "mail@choffmeister.de" 50 | } 51 | ], 52 | "_npmOperationalInternal": { 53 | "host": "packages-6-west.internal.npmjs.com", 54 | "tmp": "tmp/roboto-fontface-0.4.5.tgz_1456579028731_0.8449889721814543" 55 | }, 56 | "directories": {}, 57 | "_resolved": "https://registry.npmjs.org/roboto-fontface/-/roboto-fontface-0.4.5.tgz" 58 | } 59 | -------------------------------------------------------------------------------- /src/ui/css/roboto-fontface/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 3 | 4 | for FILE in $DIR/css/*.less; do 5 | echo "less $FILE" 6 | lessc "$FILE" >/dev/null 7 | done 8 | 9 | for FILE in $DIR/css/*.scss; do 10 | echo "sass $FILE" 11 | node-sass "$FILE" >/dev/null 12 | done 13 | -------------------------------------------------------------------------------- /src/ui/css/yakyak/about.less: -------------------------------------------------------------------------------- 1 | .about { 2 | li { 3 | list-style: none; 4 | } 5 | margin: auto; 6 | text-align: center; 7 | img { 8 | height: 4em; 9 | } 10 | div { 11 | margin-bottom: .5em; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/ui/css/yakyak/colors.less: -------------------------------------------------------------------------------- 1 | // colors 2 | html{ 3 | --yakyakgreen: #0f9d58; 4 | // 5 | --white: #ffffff; 6 | --ghostwhite: #f4f4f4; 7 | --lightgrey: #e8e8e8; 8 | --altgrey: #C5CED4; 9 | --grey: #b4b4b4; 10 | --darkgrey: #777777; 11 | --lightblack: #455a64; 12 | --black: #263238; 13 | --green: #0f9d58; 14 | --online: var(--green); 15 | --darkgreen: #0b6e3e; 16 | --red: #f44336; 17 | --darkred: #ea1c0d; 18 | --link: #3879d9; 19 | --tweetbg: #55acee; 20 | --tweettext: #fff; 21 | --instagrambg: #55acee; 22 | --instagramtext: #fff; 23 | // 24 | --headerbarheight: 35px; 25 | 26 | &[theme="blue"] { 27 | --green: #03A9F4; 28 | --darkgreen: #0286c2; 29 | } 30 | 31 | &[theme="dark"] { 32 | --white: #1D242A; 33 | --ghostwhite: #2d3d46; 34 | --lightgrey: #242d34; 35 | --altgrey: #333940; 36 | --grey: #333940; 37 | --darkgrey: #4a525c; 38 | --lightblack: #475866; 39 | --black: #A2A2A2; 40 | --green: #161c20; 41 | --online: var(--yakyakgreen); 42 | --darkgreen: #0f1316; 43 | --red: #996DBF; 44 | --darkred: #804cad; 45 | --link: #F9C15C; 46 | --tweetbg: #644104; 47 | --instagrambg: #333940; 48 | 49 | .listhead, 50 | .listhead button, 51 | .convhead .name, 52 | .controls .button, 53 | .convhead .headwrap .optionwrapper > .button, 54 | .convadd .button{ 55 | color: var(--black); 56 | } 57 | 58 | .convhead .win-buttons button{ 59 | background-color: var(--black); 60 | } 61 | } 62 | 63 | &[theme="darker"] { 64 | --white: #181A1B; 65 | --ghostwhite: #2d3d46; 66 | --lightgrey: #1E1F21; 67 | --altgrey: #222A2E; 68 | --grey: #222A2E; 69 | --darkgrey: #cccccc; 70 | --lightblack: #cccccc; 71 | --black: #ffffff; 72 | --green: #0b6e3e; 73 | --online: var(--yakyakgreen); 74 | --darkgreen: #0f1316; 75 | --red: #f44336; 76 | --darkred: #ea1c0d; 77 | --link: #F9C15C; 78 | --tweetbg: #644104; 79 | --instagrambg: #333940; 80 | 81 | .listhead, 82 | .listhead button, 83 | .convhead .name, 84 | .controls .button, 85 | .convhead .headwrap .optionwrapper > .button, 86 | .convadd .button{ 87 | color: var(--black); 88 | } 89 | 90 | .convhead .win-buttons button{ 91 | background-color: var(--black); 92 | } 93 | } 94 | 95 | &[theme="material"] { 96 | --white: #202B31; 97 | --ghostwhite: #2d3d46; 98 | --lightgrey: #28353B; 99 | --altgrey: #2B3E41; 100 | --grey: #303F47; 101 | --darkgrey: #617E8A; 102 | --lightblack: #455a64; 103 | --black: #99B1B7; 104 | --green: #7BC8BF; 105 | --darkgreen: #57b9ae; 106 | --red: #996DBF; 107 | --darkred: #804cad; 108 | --link: #F9C15C; 109 | --tweetbg: #644104; 110 | --instagrambg: #333940; 111 | } 112 | 113 | &[theme="gruvy"] { 114 | --white: #303030; 115 | --ghostwhite: #458588; 116 | --lightgrey: #282828; 117 | --altgrey: #3c3836; 118 | --grey: #928374; 119 | --darkgrey: #83a598; 120 | --lightblack: #d5c4a1; 121 | --black: #ebdbb2; 122 | --green: #458588; 123 | --online: #b8bb26; 124 | --darkgreen: #3b6d6f; 125 | --red: #fb4934; 126 | --darkred: #cc241d; 127 | --link: #8ec07c; 128 | --tweetbg: #458588; 129 | --instagrambg: #b16286; 130 | 131 | .listhead, 132 | .listhead button, 133 | .convhead .name, 134 | .controls .button, 135 | .convhead .headwrap .optionwrapper > .button, 136 | .convadd .button { 137 | color: var(--black); 138 | } 139 | 140 | .convhead .win-buttons button{ 141 | background-color: var(--black); 142 | } 143 | } 144 | &[theme="pop"] { 145 | --white: #4C4845; 146 | --ghostwhite: #403B39; 147 | --lightgrey: #393634; 148 | --altgrey: #3F3B39; 149 | --grey: #FAEBCB; 150 | --darkgrey: #50858B; 151 | --lightblack: #FAEBCB; 152 | --black: #A2A2A2; 153 | --green: #3C3937; 154 | --online: var(--yakyakgreen); 155 | --darkgreen: #3F975E; 156 | --red: #3C3937; 157 | --darkred: #CE440D; 158 | --link: #F9C15C; 159 | --tweetbg: #644104; 160 | --instagrambg: #333940; 161 | 162 | .listhead, 163 | .listhead button, 164 | .convhead .name, 165 | .controls .button, 166 | .convhead .headwrap .optionwrapper > .button, 167 | .convadd .button{ 168 | color: var(--black); 169 | } 170 | 171 | .convhead .win-buttons button{ 172 | background-color: var(--black); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/ui/css/yakyak/controls.less: -------------------------------------------------------------------------------- 1 | .controls { 2 | position: relative; 3 | .button{ 4 | position: absolute; 5 | right: 15px; 6 | bottom: 15px; 7 | border: none; 8 | background: var(--green); 9 | color: var(--white); 10 | height: var(--headerbarheight); 11 | line-height: 44px; 12 | font-size: 20px; 13 | width: var(--headerbarheight); 14 | border-radius: 100%; 15 | box-shadow: 0 3px 3px 0 rgba(0, 0, 0, .4); 16 | text-align: center; 17 | cursor: pointer; 18 | transition: all .3s ease; 19 | &:hover{ 20 | box-shadow: 0 0 0 0 rgba(0, 0, 0, .4); 21 | } 22 | .material-icons { 23 | vertical-align: middle; 24 | height: 100%; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/ui/css/yakyak/convadd.less: -------------------------------------------------------------------------------- 1 | .convadd { 2 | .initials { 3 | border-radius: 50%; 4 | width: 50px; 5 | height: 50px; 6 | text-align: center; 7 | font-size: 17px; 8 | line-height: 50px; 9 | color: var(--white); 10 | background: var(--grey); 11 | text-transform: uppercase; 12 | } 13 | padding: 20px 20px 0; 14 | h1{ 15 | margin-top: 30px; 16 | margin-bottom: 20px; 17 | font-size: 20px; 18 | font-weight: normal; 19 | text-transform: initial; 20 | letter-spacing: 0; 21 | color: var(--lightblack); 22 | } 23 | .input { 24 | display: flex; 25 | justify-content: center; 26 | align-items: center; 27 | > div { 28 | width: 100%; 29 | padding: 0; 30 | input { 31 | width: 100%; 32 | resize: none; 33 | padding: 8px 10px; 34 | font-size: 16px; 35 | line-height: 22px; 36 | outline: none; 37 | border: 1px solid var(--grey); 38 | margin-bottom: 1em; 39 | } 40 | } 41 | } 42 | 43 | button { 44 | padding: 10px; 45 | font-size: 16px; 46 | font-family: inherit; 47 | font-weight: 300; 48 | width: 150px; 49 | padding: 8px; 50 | background: var(--lightgrey); 51 | &:enabled { 52 | cursor: pointer; 53 | } 54 | } 55 | 56 | 57 | ul { 58 | display: flex; 59 | flex-wrap: wrap; 60 | margin: 10px -4px 20px -4px; 61 | li.ct-local { 62 | background: #AED581; 63 | &:hover { 64 | background: #C5E1A5; 65 | } 66 | } 67 | li.ct-local.selected { 68 | background-color: #81C784; 69 | } 70 | li.selected { 71 | background-color: var(--altgrey); 72 | } 73 | li { 74 | text-align: center; 75 | padding: 10px; 76 | cursor: pointer; 77 | width: 85px; 78 | height: 126px; 79 | margin: 4px; 80 | overflow: hidden; 81 | border-radius: 5px; 82 | background: var(--white); 83 | &:hover { 84 | background: var(--ghostwhite); 85 | } 86 | display: flex; 87 | flex-flow: column; 88 | align-items: center; 89 | justify-content: space-between; 90 | > .avatar img { 91 | width: 50px; 92 | height: 50px; 93 | background-size: cover; 94 | border-radius: 50%; 95 | flex: none; 96 | position: relative; 97 | } 98 | > .avatar .initials { 99 | position: relative; 100 | } 101 | > p { 102 | overflow: hidden; 103 | text-overflow: ellipsis; 104 | -webkit-line-clamp: 2; 105 | -webkit-box-orient: vertical; 106 | } 107 | } 108 | } 109 | 110 | .button{ 111 | width: 100%; 112 | background: var(--green); 113 | color: var(--white); 114 | margin-bottom: 10px; 115 | height: 40px; 116 | line-height: 40px; 117 | text-align: center; 118 | border-radius: 10px; 119 | cursor: pointer; 120 | &:hover{ 121 | background: var(--darkgreen); 122 | } 123 | .material-icons{ 124 | font-size: 20px; 125 | vertical-align: middle; 126 | position: relative; 127 | top: -2px; 128 | margin-right: 4px; 129 | } 130 | } 131 | 132 | .leave .button{ 133 | background: var(--red); 134 | &:hover{ 135 | background: var(--darkred); 136 | } 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/ui/css/yakyak/convhead.less: -------------------------------------------------------------------------------- 1 | .convhead{ 2 | width: 100%; 3 | position: relative; 4 | height: ~"calc(var(--headerbarheight) / var(--zoom))"; 5 | cursor: default; 6 | -webkit-app-region: drag; 7 | -webkit-user-select: none; 8 | 9 | .headwrap { 10 | display: flex; 11 | flex-flow: row nowrap; 12 | background: var(--green); 13 | height: ~"calc(var(--headerbarheight) / var(--zoom))"; 14 | line-height: ~"calc(var(--headerbarheight) / var(--zoom))"; 15 | } 16 | 17 | .headwrap .button { 18 | transition: all .1s ease; 19 | } 20 | 21 | .headwrap .button:hover { 22 | background-color: rgba(255, 255, 255, 0.25); 23 | } 24 | 25 | .headwrap > .namewrapper { 26 | display: flex; 27 | flex-flow: column nowrap; 28 | flex-grow: 1; 29 | position: relative; 30 | overflow: hidden; 31 | text-align: center; 32 | } 33 | 34 | .headwrap > .namewrapper > span { 35 | z-index: 2; 36 | line-height: initial; 37 | color: var(--white); 38 | } 39 | 40 | .headwrap > .namewrapper > .name { 41 | margin: auto; 42 | font-size: ~"calc(16px / var(--zoom))"; 43 | font-weight: bold; 44 | .material-icons{ 45 | font-size: .82em; 46 | margin-right: 2px; 47 | } 48 | } 49 | 50 | .headwrap > .namewrapper > .active { 51 | padding: 1px 0px; 52 | font-size: ~"calc(12px / var(--zoom))"; 53 | font-style: italic; 54 | } 55 | 56 | .headwrap > .optionwrapper{ 57 | position: relative; 58 | -webkit-app-region: no-drag; 59 | z-index: 2; 60 | 61 | > .button { 62 | width: 50px; 63 | height: 100%; 64 | cursor: pointer; 65 | color: var(--white); 66 | text-align: center; 67 | .material-icons { 68 | position: relative; 69 | top: calc((var(--headerbarheight) - 24px) / var(--zoom) / 2); 70 | font-size: ~"calc(24px / var(--zoom))"; 71 | } 72 | } 73 | } 74 | 75 | .convoptions{ 76 | background: var(--white); 77 | position: absolute; 78 | top: calc(var(--headerbarheight) / var(--zoom)); 79 | right: 0px; 80 | z-index: 1; 81 | border-bottom: 1px solid rgba(0, 0, 0, .1); 82 | text-align: right; 83 | transition: all .3s ease; 84 | opacity: 0.0; 85 | visibility: hidden; 86 | &.open{ 87 | opacity: 1.0; 88 | visibility: visible; 89 | } 90 | .button { 91 | position: relative; 92 | display: flex; 93 | flex-flow: row nowrap; 94 | vertical-align: top; 95 | color: var(--darkgrey); 96 | width: auto; 97 | padding: 0 10px; 98 | cursor: pointer; 99 | &:hover{ 100 | color: var(--lightblack); 101 | } 102 | &:last-child{ 103 | border: none; 104 | } 105 | .material-icons{ 106 | font-size: 20px; 107 | display: inline-block; 108 | position: relative; 109 | top: 5px; 110 | } 111 | .option-label{ 112 | font-size: 13px; 113 | font-weight: normal; 114 | display: inline-block; 115 | width: auto; 116 | margin-left: 4px; 117 | } 118 | } 119 | } 120 | 121 | .win-buttons{ 122 | display: flex; 123 | flex-flow: row nowrap; 124 | button{ 125 | -webkit-mask-size: ~"calc(20px / var(--zoom))"; 126 | -webkit-mask-position: center; 127 | -webkit-mask-repeat: no-repeat; 128 | -webkit-app-region: no-drag; 129 | width: 50px; 130 | height: 100%; 131 | cursor: pointer; 132 | padding: 0 1.5em 0 1.5em;; 133 | background-color: var(--white); 134 | } 135 | #win-minimize{ 136 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' baseProfile='full'%3E%3Cpath d='M20 14H4v-4h16'/%3E%3C/svg%3E"); 137 | } 138 | #win-maximize{ 139 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' baseProfile='full'%3E%3Cpath d='M4 4l16 .00001V20H4V4zm2.00001 4L6 18h12V8H6.00001z'/%3E%3C/svg%3E"); 140 | } 141 | #win-restore{ 142 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cpath d='M4 8h4V4h12v12h-4v4H4V8m12 0v6h2V6h-8v2h6M6 12v6h8v-6H6z'/%3E%3C/svg%3E"); 143 | display: none; 144 | } 145 | #win-close{ 146 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' baseProfile='full'%3E%3Cpath d='M13.4579 12.0001l5.542 5.5432L19 19l-1.4574.0002-5.5417-5.5429-5.54305 5.5431L5 19l-.00008-1.4558 5.54398-5.544-5.54398-5.54255L5 4.99996l1.45671.00025 5.54409 5.54289 5.5429-5.54289L19 4.99996l-.0002 1.45834-5.5419 5.5418z'/%3E%3C/svg%3E"); 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/ui/css/yakyak/fonts.less: -------------------------------------------------------------------------------- 1 | // colors 2 | html { 3 | --emojifont: Roboto, "Helvetica Neue", NotoColorEmoji, 'Segoe UI Emoji', Helvetica, Arial, "Lucida Grande", sans-serif; 4 | --globalfont: Roboto, "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; 5 | 6 | &[font-size="x-small"] { 7 | --fontsize: 10px; 8 | } 9 | 10 | &[font-size="small"] { 11 | --fontsize: 12px; 12 | } 13 | 14 | &[font-size="medium"] { 15 | --fontsize: 14px; 16 | } 17 | 18 | &[font-size="large"] { 19 | --fontsize: 16px; 20 | } 21 | 22 | &[font-size="x-large"] { 23 | --fontsize: 18px; 24 | } 25 | 26 | .f-small { 27 | font-size: .7em; 28 | } 29 | .f-no-bold { 30 | font-weight: normal; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/ui/css/yakyak/global.less: -------------------------------------------------------------------------------- 1 | // minimal reset 2 | * { 3 | padding: 0; 4 | margin: 0; 5 | border: 0; 6 | } 7 | html { 8 | box-sizing: border-box; 9 | } 10 | *, *:before, *:after { 11 | box-sizing: inherit; 12 | } 13 | 14 | html, body { 15 | height: 100%; 16 | overflow: hidden; 17 | } 18 | 19 | body { 20 | background: #fff; 21 | font-family: var(--globalfont); 22 | color: var(--black); 23 | font-smoothing: antialiased; 24 | text-rendering: optimizeLegibility; 25 | -webkit-font-smoothing: subpixel-antialiased; 26 | font-weight: 400; 27 | font-size: var(--fontsize); 28 | line-height: 20px; 29 | -webkit-font-smoothing: antialiased; 30 | } 31 | 32 | h1 { 33 | font-weight: 100; 34 | letter-spacing: 0.05em; 35 | text-transform: uppercase; 36 | margin-top: 8px; 37 | margin-bottom: 1.5em; 38 | } 39 | 40 | ::-webkit-input-placeholder { 41 | color: #ddd; 42 | } 43 | 44 | input, textarea { 45 | font-family: inherit; 46 | } 47 | 48 | a { 49 | color: var(--link); 50 | text-decoration: none; 51 | } 52 | 53 | body .notr { 54 | box-shadow: 0 0 2px rgba(0,0,0,0.4); 55 | min-width: 1px; 56 | min-height: 1px; 57 | padding: 9px 14px; 58 | border-radius: 5px; 59 | border: none; 60 | color: #666; 61 | font-size: 0.9em; 62 | .material-icons{ 63 | vertical-align: top; 64 | font-size: 20px; 65 | } 66 | } 67 | body .notrstack { 68 | min-width: 1px; 69 | } 70 | 71 | *:focus { 72 | outline: none; 73 | } 74 | 75 | img.emoji { 76 | height: 1em; 77 | width: 1em; 78 | margin: 0 .05em 0 .1em; 79 | vertical-align: -0.1em; 80 | } 81 | 82 | .avatar { 83 | .initials { 84 | &[data-first-letter="-1"] { background-color: #354458} 85 | &[data-first-letter="0"] { background-color: #69D2E7} 86 | &[data-first-letter="1"] { background-color: #F2AE72} 87 | &[data-first-letter="2"] { background-color: #28ABE3} 88 | &[data-first-letter="3"] { background-color: #F38630} 89 | &[data-first-letter="4"] { background-color: #FA6900} 90 | &[data-first-letter="5"] { background-color: #542733} 91 | &[data-first-letter="6"] { background-color: #1FDA9A} 92 | &[data-first-letter="7"] { background-color: #DB3340} 93 | &[data-first-letter="8"] { background-color: #E8B71A} 94 | &[data-first-letter="9"] { background-color: #588C73} 95 | &[data-first-letter="10"] { background-color: #D96459} 96 | &[data-first-letter="11"] { background-color: #8C4646} 97 | &[data-first-letter="12"] { background-color: #D0C91F} 98 | &[data-first-letter="13"] { background-color: #609194} 99 | &[data-first-letter="14"] { background-color: #29ABA4} 100 | &[data-first-letter="15"] { background-color: #DA4624} 101 | &[data-first-letter="16"] { background-color: #BCCF02} 102 | &[data-first-letter="17"] { background-color: #FFD041} 103 | &[data-first-letter="18"] { background-color: #DD5F32} 104 | &[data-first-letter="19"] { background-color: #9B539C} 105 | &[data-first-letter="20"] { background-color: #00A03E} 106 | &[data-first-letter="21"] { background-color: #92B06A} 107 | &[data-first-letter="23"] { background-color: #8BAD39} 108 | &[data-first-letter="24"] { background-color: #6E0000} 109 | &[data-first-letter="25"] { background-color: #1352A2} 110 | } 111 | img, .initials { 112 | object-fit: cover; 113 | border-radius: 50%; 114 | width: 50px; 115 | height: 50px; 116 | color: var(--white); 117 | background: var(--grey); 118 | position: absolute; 119 | text-align: center; 120 | text-transform: uppercase; 121 | justify-content: center; 122 | align-items: center; 123 | // 124 | line-height: 41px; 125 | font-size: 15px; 126 | &.fallback { 127 | display:none; 128 | } 129 | } 130 | &.fallback-on img{ 131 | display: none; 132 | } 133 | &.fallback-on .initials.fallback { 134 | display: inherit; 135 | } 136 | } 137 | 138 | .close-me { 139 | font-size: .95em; 140 | color: #777; 141 | position:absolute; 142 | top: 0; 143 | right: 0; 144 | cursor: pointer; 145 | } 146 | -------------------------------------------------------------------------------- /src/ui/css/yakyak/listhead.less: -------------------------------------------------------------------------------- 1 | .listhead { 2 | background: var(--green); 3 | color: var(--white); 4 | height: ~"calc(var(--headerbarheight) / var(--zoom))"; 5 | line-height: ~"calc(var(--headerbarheight) / var(--zoom))"; 6 | position: relative; 7 | text-align: center; 8 | font-size: ~"calc(16px / var(--zoom))"; 9 | font-weight: bold; 10 | cursor: default; 11 | -webkit-app-region: drag; 12 | -webkit-user-select: none; 13 | z-index: 2; // hides the separator in the header 14 | button{ 15 | -webkit-app-region: no-drag; 16 | background: none; 17 | color: var(--white); 18 | height: ~"calc(var(--headerbarheight) / var(--zoom))"; 19 | padding: 0 .5em; 20 | position: absolute; 21 | left: 0; 22 | cursor: pointer; 23 | .material-icon{ 24 | font-size: ~"calc(24px / var(--zoom))"; 25 | } 26 | } 27 | 28 | .minimal & span{ 29 | display: none; 30 | } 31 | 32 | } 33 | 34 | .osx { 35 | .listhead { 36 | .listheadlabel { 37 | padding-left: ~"calc(77px / var(--zoom))"; 38 | text-align: left; 39 | overflow: hidden; 40 | text-overflow: ellipsis; 41 | padding-right: 10px; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/ui/css/yakyak/typinginfo.less: -------------------------------------------------------------------------------- 1 | 2 | .typing { 3 | height: 20px; 4 | line-height: 20px; 5 | font-size: 10px; 6 | padding: 0 10px; 7 | transition: font-weight 0.5s, opacity .5s ease; 8 | color: var(--darkgrey); 9 | text-align: center; 10 | background: var(--white); 11 | margin-left: 15px; 12 | margin-bottom: 5px; 13 | border-radius: 15px; 14 | opacity: 0; 15 | &.typingnow{ 16 | opacity: 1; 17 | } 18 | .material-icons{ 19 | font-size: 22px; 20 | vertical-align: top; 21 | position: relative; 22 | top: -2px; 23 | } 24 | .typing_TYPING { 25 | font-weight: 500; 26 | } 27 | .typing_PAUSED, .typing_STOPPED { 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/ui/events.coffee: -------------------------------------------------------------------------------- 1 | 2 | # events propagated from server to renderer 3 | module.exports =' 4 | connecting 5 | connected 6 | connect_failed 7 | noop 8 | chat_message 9 | conversation_rename 10 | notification_level 11 | delete 12 | membership_change 13 | client_conversation 14 | 15 | focus 16 | typing 17 | watermark 18 | presence 19 | self_presence 20 | 21 | hangout_event 22 | conversation_notification 23 | reply_to_invite 24 | invitation_watermark 25 | '.split(' ') 26 | -------------------------------------------------------------------------------- /src/ui/images/photo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yakyak/yakyak/8f8cbc081c66ca00b5e9520a9bef5f2bc1d64dfd/src/ui/images/photo.jpg -------------------------------------------------------------------------------- /src/ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | YakYak 6 | 7 | 8 | 9 | 10 |
11 | 42 |
43 |

An error has occured

44 |
45 |

If you are seeing this then an error has occured with YakYak.

46 |

To see what happened, open the Inspector and check the messages in the console tab.

47 |

How to report

48 |

Find if this error has already been reported at 49 | 54 | 55 | our issue tracker 56 | and join the discussion, or create a new one if you are the first (please include a screenshot of the console tab in the Inspector). 57 |

58 |

Sorry for this.

59 |

  YakYak team and community

60 |
61 |
62 |
63 | 64 | 65 | -------------------------------------------------------------------------------- /src/ui/models/connection.coffee: -------------------------------------------------------------------------------- 1 | 2 | {tryparse, later} = require '../util' 3 | 4 | STATE = 5 | CONNECTING: 'connecting' # exactly match corresponding event name 6 | CONNECTED: 'connected' # exactly match corresponding event name 7 | CONNECT_FAILED: 'connect_failed' # exactly match corresponding event name 8 | 9 | EVENT_STATE = 10 | IN_SYNC: 'in_sync' # when we certain we have connection/events 11 | MISSING_SOME: 'missing_some' # when more than 40 secs without any event 12 | MISSING_ALL: 'missing_all' # when more than 10 minutes without any event 13 | 14 | TIME_SOME = 40 * 1000 # 40 secs 15 | TIME_ALL = 10 * 60 * 1000 # 10 mins 16 | 17 | merge = (t, os...) -> t[k] = v for k,v of o when v not in [null, undefined] for o in os; t 18 | 19 | info = 20 | connecting: 'Connecting…' 21 | connected: 'Connected' 22 | connect_failed: 'Not connected' 23 | unknown: 'Unknown' 24 | 25 | module.exports = exp = 26 | state: null # current connection state 27 | eventState: null # current event state 28 | lastActive: tryparse(localStorage.lastActive) ? 0 # last activity timestamp 29 | wasConnected: false 30 | 31 | setState: (state) -> 32 | return if @state == state 33 | @state = state 34 | if @wasConnected and state == STATE.CONNECTED 35 | later -> action 'syncrecentconversations' 36 | @wasConnected = @wasConnected or state == STATE.CONNECTED 37 | updated 'connection' 38 | 39 | setWindowOnline: (wonline) -> 40 | return if @wonline == wonline 41 | @wonline = wonline 42 | unless @wonline 43 | @setState STATE.CONNECT_FAILED 44 | 45 | infoText: -> info[@state] ? info.unknown 46 | 47 | setLastActive: (active) -> 48 | return if @lastActive == active 49 | @lastActive = localStorage.lastActive = active 50 | 51 | setEventState: (state) -> 52 | return if @eventState == state 53 | @eventState = state 54 | if state == EVENT_STATE.IN_SYNC 55 | @setLastActive Date.now() unless @lastActive 56 | else if state == EVENT_STATE.MISSING_SOME 57 | # if we have a gap of more than 40 seconds we try getting 58 | # any events we may have missed during that gap. notice 59 | # that we get 'noop' every 20-30 seconds, so there is no 60 | # reason for a gap of 40 seconds. 61 | later -> action 'syncallnewevents', @lastActive 62 | else if state == EVENT_STATE.MISSING_ALL 63 | # if we have a gap of more than 10 minutes, we will 64 | # reinitialize all convs using syncrecentconversations 65 | # (sort of like client startup) 66 | later -> action 'syncrecentconversations' 67 | later -> checkEventState() 68 | updated 'connection' 69 | 70 | merge exp, STATE 71 | merge exp, EVENT_STATE 72 | 73 | # averissimo: not sure when checkEventState is actually called 74 | # I believe this is deprecated code, or it would be better to extend the timer 75 | # to 30s or so 76 | checkTimer = null 77 | checkEventState = -> 78 | elapsed = Date.now() - exp.lastActive 79 | clearTimeout checkTimer if checkTimer 80 | if elapsed >= TIME_ALL 81 | wrapAction -> exp.setEventState EVENT_STATE.MISSING_ALL 82 | else if elapsed >= TIME_SOME 83 | wrapAction -> exp.setEventState EVENT_STATE.MISSING_SOME 84 | else 85 | wrapAction -> exp.setEventState EVENT_STATE.IN_SYNC 86 | checkTimer = setTimeout checkEventState, 1000 87 | 88 | wrapAction = (f) -> 89 | handle 'connwrap', -> f() 90 | action 'connwrap' 91 | -------------------------------------------------------------------------------- /src/ui/models/convsettings.coffee: -------------------------------------------------------------------------------- 1 | entity = require './entity' 2 | 3 | module.exports = exp = { 4 | # This handles the data of conversation add / edit 5 | # where you can specify participants conversation name, etc 6 | searchedEntities: [] 7 | selectedEntities: [] 8 | initialName: null 9 | initialSearchQuery: null 10 | name: "" 11 | searchQuery: "" 12 | id: null 13 | group: false 14 | 15 | setSearchedEntities: (entities) -> 16 | @searchedEntities = entities or [] 17 | updated 'searchedentities' 18 | 19 | addSelectedEntity: (entity) -> 20 | id = entity.id?.chat_id or entity # may pass id directly 21 | exists = (e for e in @selectedEntities when e.id.chat_id == id).length != 0 22 | if not exists 23 | @selectedEntities.push entity 24 | @group = @selectedEntities.length > 1 25 | updated 'convsettings' 26 | 27 | removeSelectedEntity: (entity) -> 28 | id = entity.id?.chat_id or entity # may pass id directly 29 | # if the conversation we are editing is one to one we don't want 30 | # to remove the selected entity 31 | @selectedEntities = (e for e in @selectedEntities when e.id.chat_id != id) 32 | @group = @selectedEntities.length > 1 33 | updated 'selectedEntities' 34 | 35 | setSelectedEntities: (entities) -> 36 | @group = entities.length > 1 37 | @selectedEntities = entities or [] # no need to update 38 | 39 | setGroup: (val) -> @group = val; updated 'convsettings' 40 | 41 | setInitialName: (name) -> @initialName = name 42 | getInitialName: -> v = @initialName; @initialName = null; v 43 | 44 | setInitialSearchQuery: (query) -> @initialSearchQuery = query 45 | getInitialSearchQuery: -> v = @initialSearchQuery; @initialSearchQuery = null; v 46 | 47 | setName: (name) -> @name = name 48 | 49 | setSearchQuery: (query) -> @searchQuery = query 50 | 51 | loadConversation: (c) -> 52 | c.participant_data.forEach (p) => 53 | id = p.id.chat_id or p.id.gaia_id 54 | if entity.isSelf id then return 55 | p = entity[id] 56 | @selectedEntities.push 57 | id: chat_id: id 58 | properties: 59 | photo_url: p.photo_url 60 | display_name: p.display_name or p.fallback_name 61 | @group = @selectedEntities.length > 1 62 | @id = c.conversation_id?.id or c.id?.id 63 | @initialName = @name = c.name or "" 64 | @initialSearchQuery = "" 65 | 66 | updated 'convsettings' 67 | 68 | reset: -> 69 | @searchedEntities = [] 70 | @selectedEntities = [] 71 | @initialName = "" 72 | @initialSearchQuery = "" 73 | @searchQuery = "" 74 | @name = "" 75 | @id = null 76 | @group = false 77 | updated 'convsettings' 78 | 79 | 80 | } 81 | 82 | -------------------------------------------------------------------------------- /src/ui/models/entity.coffee: -------------------------------------------------------------------------------- 1 | 2 | merge = (t, os...) -> t[k] = v for k,v of o when v not in [null, undefined] for o in os; t 3 | shallowif = (o, f) -> r = {}; r[k] = v for k, v of o when f(k,v); r 4 | 5 | lookup = {} 6 | 7 | domerge = (id, props) -> lookup[id] = merge (lookup[id] ? {}), props 8 | 9 | add = (entity, opts = silent:false) -> 10 | {gaia_id, chat_id} = entity?.id ? {} 11 | return null unless gaia_id or chat_id 12 | 13 | # ensure there is at least something 14 | lookup[chat_id] = {} unless lookup[chat_id] 15 | 16 | # dereference .properties to be on main obj 17 | if entity.properties 18 | domerge chat_id, entity.properties 19 | 20 | # merge rest of props 21 | clone = shallowif entity, (k) -> k not in ['id', 'properties'] 22 | domerge chat_id, clone 23 | 24 | lookup[chat_id].id = chat_id 25 | 26 | # handle different chat_id to gaia_id 27 | lookup[gaia_id] = lookup[chat_id] if chat_id != gaia_id 28 | 29 | updated 'entity' unless opts.silent 30 | 31 | # return the result 32 | lookup[chat_id] 33 | 34 | 35 | needEntity = do -> 36 | tim = null 37 | gather = [] 38 | fetch = -> 39 | tim = null 40 | action 'getentity', gather 41 | gather = [] 42 | (id, wait=1000) -> 43 | return if lookup[id]?.fetching 44 | if lookup[id] 45 | lookup[id].fetching = true 46 | else 47 | lookup[id] = { 48 | id: id 49 | fetching: true 50 | } 51 | clearTimeout tim if tim 52 | tim = setTimeout fetch, wait 53 | gather.push id 54 | 55 | list = -> 56 | v for k, v of lookup when typeof v == 'object' 57 | 58 | funcs = 59 | count: -> 60 | c = 0; (c++ for k, v of lookup when typeof v == 'object'); c 61 | 62 | list: list 63 | 64 | setPresence: (id, p) -> 65 | return needEntity(id) if not lookup[id] 66 | lookup[id].presence = p 67 | updated 'entity' 68 | 69 | setLastSeen: (id, lastseen) -> 70 | return needEntity(id) if not lookup[id] 71 | lookup[id].lastseen = lastseen 72 | cutoff = Date.now() / 1000 - (60 * 15) 73 | if lastseen > cutoff 74 | lookup[id].presence = true 75 | 76 | updated 'entity' 77 | 78 | isSelf: (chat_id) -> return !!lookup.self and lookup[chat_id] == lookup.self 79 | 80 | updatePresence: -> 81 | changed = false 82 | cutoff = Math.floor(Date.now() / 1000) - (60 * 15) 83 | for k, v of lookup when v?.presence? and v.presence and v.lastseen? and not @isSelf(k) 84 | if v.lastseen <= cutoff 85 | v.presence = false 86 | changed = true 87 | 88 | updated 'entity' if changed 89 | 90 | _reset: -> 91 | delete lookup[k] for k, v of lookup when typeof v == 'object' 92 | updated 'entity' 93 | null 94 | 95 | _initFromSelfEntity: (self) -> 96 | updated 'entity' 97 | lookup.self = add self 98 | 99 | _initFromEntities: (entities) -> 100 | c = 0 101 | countIf = (a) -> c++ if a 102 | countIf add entity for entity in entities 103 | updated 'entity' 104 | c 105 | 106 | add: add 107 | needEntity: needEntity 108 | 109 | module.exports = merge lookup, funcs 110 | -------------------------------------------------------------------------------- /src/ui/models/index.coffee: -------------------------------------------------------------------------------- 1 | 2 | entity = require './entity' 3 | conv = require './conv' 4 | viewstate = require './viewstate' 5 | userinput = require './userinput' 6 | connection = require './connection' 7 | convsettings = require './convsettings' 8 | notify = require './notify' 9 | 10 | module.exports = {entity, conv, viewstate, userinput, connection, convsettings, notify} 11 | 12 | window?.models = module.exports 13 | -------------------------------------------------------------------------------- /src/ui/models/menuhandler.coffee: -------------------------------------------------------------------------------- 1 | { Menu } = require('electron') 2 | 3 | menuaction = (mainWindow, it) -> 4 | params = (it.action.params ? []).map (p) -> 5 | if p is ':checked' 6 | it.checked 7 | else if p is ':value' 8 | it.value 9 | else 10 | p 11 | 12 | mainWindow.webContents.send 'menuaction', it.action.name, params 13 | 14 | processMenu = (mainWindow, template) => 15 | (template ? []).forEach (e) -> 16 | if e.submenu? 17 | processMenu mainWindow, e.submenu 18 | return 19 | 20 | if not e.action? 21 | return 22 | 23 | e.click = (it) -> 24 | menuaction mainWindow, it 25 | 26 | module.exports = (mainWindow, template) -> 27 | processMenu mainWindow, template 28 | 29 | Menu.buildFromTemplate template -------------------------------------------------------------------------------- /src/ui/models/notify.coffee: -------------------------------------------------------------------------------- 1 | 2 | tonotify = [] 3 | 4 | module.exports = 5 | addToNotify: (ev) -> tonotify.push ev 6 | popToNotify: -> 7 | return [] unless tonotify.length 8 | t = tonotify 9 | tonotify = [] 10 | return t 11 | -------------------------------------------------------------------------------- /src/ui/models/userinput.coffee: -------------------------------------------------------------------------------- 1 | urlRegexp = require 'uber-url-regex' 2 | {MessageBuilder, OffTheRecordStatus,MessageActionType,ClientDeliveryMediumType} = require 'hangupsjs' 3 | viewstate = require './viewstate' 4 | conv = require './conv' 5 | 6 | randomid = -> Math.round Math.random() * Math.pow(2,32) 7 | 8 | split_first = (str, token) -> 9 | start = str.indexOf token 10 | first = str.substr 0, start 11 | last = str.substr start + token.length 12 | [first, last] 13 | 14 | parse = (mb, txt) -> 15 | lines = txt.split /\r?\n/ 16 | last = lines.length - 1 17 | for line, index in lines 18 | urls = line.match urlRegexp() 19 | if urls? 20 | for url in urls 21 | [before, after] = split_first line, url 22 | if before then mb.text(before) 23 | line = after 24 | mb.link url, url 25 | mb.text line if line 26 | mb.linebreak() unless index is last 27 | null 28 | 29 | buildChatMessage = (sender, txt) -> 30 | conv_id = viewstate.selectedConv 31 | conversation_state = conv[conv_id]?.self_conversation_state 32 | delivery_medium = ClientDeliveryMediumType[conversation_state?.delivery_medium_option?[0]?.delivery_medium?.delivery_medium_type] 33 | if not delivery_medium 34 | delivery_medium = ClientDeliveryMediumType.BABEL 35 | action = null 36 | if /^\/me\s/.test txt 37 | txt = txt.replace /^\/me/, sender.first_name 38 | action = MessageActionType.ME_ACTION 39 | mb = new MessageBuilder(action) 40 | parse mb, txt 41 | segs = mb.toSegments() 42 | segsj = mb.toSegsjson() 43 | message_action_type = mb.toMessageActionType() 44 | client_generated_id = String randomid() 45 | ts = Date.now() 46 | { 47 | segs 48 | segsj 49 | conv_id 50 | client_generated_id 51 | ts 52 | image_id: undefined 53 | otr: OffTheRecordStatus.ON_THE_RECORD 54 | message_action_type 55 | delivery_medium: [delivery_medium] # requires to be used as an array 56 | } 57 | 58 | module.exports = { 59 | buildChatMessage 60 | parse 61 | } 62 | -------------------------------------------------------------------------------- /src/ui/version.coffee: -------------------------------------------------------------------------------- 1 | ipc = require('electron').ipcRenderer 2 | got = require 'got' 3 | 4 | options = 5 | headers: 6 | 'User-Agent': 'request' 7 | url: 'https://api.github.com/repos/yakyak/yakyak/releases/latest' 8 | 9 | versionToInt = (version) -> 10 | [major, minor, micro] = version.split('.') 11 | version = (micro * Math.pow(10,4)) + (minor * Math.pow(10,8)) + (major * Math.pow(10,12)) 12 | 13 | check = ()-> 14 | window.localStorage.localVersion = await ipc.invoke "app:version" 15 | got(options.url) 16 | .then (res) -> 17 | body = JSON.parse res.body 18 | tag = body.tag_name 19 | releasedVersion = tag?.substr(1) # remove first "v" char 20 | localVersion = window.localStorage.localVersion 21 | versionAdvertised = window.localStorage.versionAdvertised or null 22 | if releasedVersion? && localVersion? 23 | higherVersionAvailable = versionToInt(releasedVersion) > versionToInt(localVersion) 24 | if higherVersionAvailable and (releasedVersion isnt versionAdvertised) 25 | window.localStorage.versionAdvertised = releasedVersion 26 | notr { 27 | html: "A new YakYak version is available
Please upgrade #{localVersion} to #{releasedVersion}
(click to dismiss)", 28 | stay: 0 29 | } 30 | console.log "YakYak local version is #{localVersion}, released version is #{releasedVersion}" 31 | .catch (error) -> 32 | console.log error 33 | 34 | module.exports = {check, versionToInt} 35 | -------------------------------------------------------------------------------- /src/ui/views/about.coffee: -------------------------------------------------------------------------------- 1 | ipc = require('electron').ipcRenderer 2 | path = require 'path' 3 | i18n = require 'i18n' 4 | 5 | {check, versionToInt} = require '../version' 6 | 7 | module.exports = view (models) -> 8 | # 9 | # decide if should update 10 | localVersion = window.localStorage.localVersion 11 | releasedVersion = window.localStorage.versionAdvertised 12 | shouldUpdate = releasedVersion? && localVersion? && 13 | versionToInt(releasedVersion) > versionToInt(localVersion) 14 | # 15 | div class: 'about', -> 16 | div -> 17 | img src: path.join YAKYAK_ROOT_DIR, '..', 'icons', 'icon@8.png' 18 | div class: 'name', -> 19 | h2 -> 20 | span 'YakYak v' + localVersion 21 | span class: 'f-small f-no-bold', ' (latest)' unless shouldUpdate 22 | # TODO: if objects are undefined then it should check again on next 23 | # time about window is opened 24 | # releasedVersion = window.localStorage.versionAdvertised 25 | if shouldUpdate 26 | div class: 'update', -> 27 | span i18n.__('menu.help.about.newer:A newer version is available, please upgrade from %s to %s' 28 | , localVersion 29 | , releasedVersion) 30 | div class: 'description', -> 31 | span i18n.__('title:YakYak - Hangouts Client') 32 | div class: 'license', -> 33 | span -> 34 | em "#{i18n.__ 'menu.help.about.license:License'}: " 35 | span 'MIT' 36 | div class: 'devs', -> 37 | div -> 38 | h3 i18n.__('menu.help.about.authors:Main authors') 39 | ul -> 40 | li 'Davide Bertola' 41 | li 'Martin Algesten' 42 | li 'André Veríssimo' 43 | div -> 44 | h3 i18n.__('menu.help.about.contributors:Contributors') 45 | ul -> 46 | li 'David Banham' 47 | li 'Max Kueng' 48 | li 'Arnaud Riu' 49 | li 'Austin Guevara' 50 | li 'Mathias Tillman' 51 | 52 | div class: 'home', -> 53 | href = "https://github.com/yakyak/yakyak" 54 | a href: href 55 | , onclick: (ev) -> 56 | ev.preventDefault() 57 | address = ev.currentTarget.getAttribute 'href' 58 | require('electron').shell.openExternal address 59 | false 60 | , href 61 | 62 | #$('document').on 'click', '.link-out', (ev)-> 63 | # 64 | -------------------------------------------------------------------------------- /src/ui/views/applayout.coffee: -------------------------------------------------------------------------------- 1 | 2 | {throttle, topof} = require '../util' 3 | 4 | path = require 'path' 5 | 6 | attached = false 7 | attachListeners = -> 8 | return if attached 9 | window.addEventListener 'mousemove', onActivity 10 | window.addEventListener 'click', onActivity 11 | window.addEventListener 'keydown', onActivity 12 | window.addEventListener 'keydown', noInputKeydown 13 | 14 | onActivity = throttle 100, (ev) -> 15 | # This occasionally happens to generate error when 16 | # user clicking has generated an application event 17 | # that is being handled while we also receive the event 18 | # Current fix: defer the action generated during the update 19 | setTimeout -> 20 | action 'activity', ev.timeStamp ? Date.now() 21 | , 1 22 | 23 | noInputKeydown = (ev) -> 24 | action 'noinputkeydown', ev if ev.target.tagName != 'TEXTAREA' 25 | 26 | onScroll = throttle 20, (ev) -> 27 | el = ev.target 28 | child = el.children[0] 29 | 30 | # use the saved scroll value if we have one 31 | if el.hasAttribute('scrolltop') 32 | el.scrollTop = el.getAttribute('scrolltop') 33 | el.removeAttribute('scrolltop') 34 | 35 | # calculation to see whether we are at the bottom or top with a tolerance value 36 | if el.scrollTop <= 0 37 | atbottom = el.scrollTop >= -10 38 | attop = child.offsetHeight - el.offsetHeight + el.scrollTop <= el.offsetHeight / 2 39 | else 40 | atbottom = (el.scrollTop + el.offsetHeight) >= (child.offsetHeight - 10) 41 | attop = el.scrollTop <= (el.offsetHeight / 2) 42 | 43 | action 'atbottom', atbottom 44 | action 'attop', attop 45 | 46 | addClass = (el, cl) -> 47 | return unless el 48 | return if RegExp("\\s*#{cl}").exec el.className 49 | el.className += if el.className then " #{cl}" else cl 50 | el 51 | 52 | removeClass = (el, cl) -> 53 | return unless el 54 | el.className = el.className.replace RegExp("\\s*#{cl}"), '' 55 | el 56 | 57 | closest = (el, cl) -> 58 | return unless el 59 | cl = RegExp("\\s*#{cl}") unless cl instanceof RegExp 60 | if el.className.match(cl) then el else closest(el.parentNode, cl) 61 | 62 | drag = do -> 63 | 64 | ondragover = ondragenter = (ev) -> 65 | # this enables dragging at all 66 | ev.preventDefault() 67 | addClass closest(ev.target, 'dragtarget'), 'dragover' 68 | removeClass closest(ev.target, 'dragtarget'), 'drag-timeout' 69 | ev.dataTransfer.dropEffect = 'copy' 70 | return false 71 | 72 | ondrop = (ev) -> 73 | ev.preventDefault() 74 | removeClass closest(ev.target, 'dragtarget'), 'dragover' 75 | removeClass closest(ev.target, 'dragtarget'), 'drag-timeout' 76 | action 'uploadimage', ev.dataTransfer.files 77 | 78 | ondragleave = (ev) -> 79 | # it was firing the leave event while dragging, had to 80 | # use a timeout to check if it was a "real" event 81 | # by remaining out 82 | addClass closest(ev.target, 'dragtarget'), 'drag-timeout' 83 | setTimeout -> 84 | if closest(ev.target, 'dragtarget').classList.contains('drag-timeout') 85 | removeClass closest(ev.target, 'dragtarget'), 'dragover' 86 | removeClass closest(ev.target, 'dragtarget'), 'drag-timeout' 87 | , 200 88 | 89 | {ondragover, ondragenter, ondrop, ondragleave} 90 | 91 | 92 | resize = do -> 93 | rz = null 94 | { 95 | onmousemove: (ev) -> 96 | if rz and ev.buttons & 1 97 | rz(ev) 98 | else 99 | rz = null 100 | onmousedown: (ev) -> 101 | rz = resizers[ev.target.dataset?.resize] 102 | onmouseup: (ev) -> 103 | rz = null 104 | } 105 | 106 | resizers = 107 | leftResize: (ev) -> action 'leftresize', (Math.max 90, ev.clientX) 108 | 109 | module.exports = exp = layout -> 110 | platform = if process.platform is 'darwin' then 'osx' else '' 111 | div class:'applayout ' + platform, resize, region('last'), -> 112 | div class:'left', -> 113 | div class:'listhead', region('listhead') 114 | div class:'list', region('left') 115 | div class:'lfoot', region('lfoot') 116 | div class:'leftresize', 'data-resize':'leftResize' 117 | div class:'right dragtarget ', drag, -> 118 | div id: 'drop-overlay', -> 119 | div class: 'inner-overlay', () -> 120 | div 'Drop file here.' 121 | div class:'convhead', region('convhead') 122 | div class:'main', region('main'), onscroll: onScroll 123 | div class:'maininfo', region('maininfo') 124 | div class:'foot', region('foot') 125 | attachListeners() 126 | 127 | 128 | do -> 129 | id = ofs = null 130 | 131 | lastVisibleMessage = -> 132 | # the viewport 133 | screl = document.querySelector('.main') 134 | # the pixel offset for the bottom of the viewport 135 | bottom = screl.scrollTop + screl.offsetHeight 136 | # all messages 137 | last = null 138 | last = m for m in document.querySelectorAll('.message') when topof(m) < bottom 139 | return last 140 | 141 | exp.recordMainPos = -> 142 | ofs = document.querySelector('.main').scrollTop 143 | 144 | exp.adjustMainPos = -> 145 | return unless ofs 146 | document.querySelector('.main').scrollTop = ofs 147 | document.querySelector('.main').setAttribute('scrolltop', ofs) 148 | # reset 149 | id = ofs = null 150 | -------------------------------------------------------------------------------- /src/ui/views/conninfo.coffee: -------------------------------------------------------------------------------- 1 | module.exports = view (connection) -> 2 | div -> 3 | pass connection.infoText(), ' ', -> 4 | span class:'material-icons', 'error_outline' if connection.state == 'connect_failed' 5 | span class:'material-icons spin', 'donut_large' if connection.state == 'connecting' 6 | span class:'material-icons', 'check_circle' if connection.state == 'connected' 7 | -------------------------------------------------------------------------------- /src/ui/views/contextmenu.coffee: -------------------------------------------------------------------------------- 1 | ipc = require('electron').ipcRenderer 2 | clipboard = require('electron').clipboard 3 | # {download} = require('electron-dl') # See IMPORTANT below 4 | 5 | {isContentPasteable} = require '../util' 6 | 7 | ipc.invoke('spellcheck:availablelanguages') 8 | .then (res) -> 9 | window.availableLanguages = res 10 | .catch (error) -> 11 | console.error(error) 12 | 13 | templateContext = (params, viewstate) -> 14 | # 15 | # IMPORTANT: currently save images is disabled as there 16 | # are exceptions being thrown from the electron-dl module 17 | # 18 | canShowSaveImg = params.mediaType == 'image' && false 19 | canShowCopyImgLink = params.mediaType == 'image' && params.srcURL != '' 20 | canShowCopyLink = params.linkURL != '' && params.mediaType == 'none' 21 | # 22 | 23 | spellcheckLanguage = viewstate.spellcheckLanguage 24 | spellCheck = if spellcheckLanguage == 'none' 25 | i18n.__('menu.edit.spell_check.off:Spellcheck is off') 26 | else 27 | i18n.__('menu.edit.spell_check.title:Spellcheck') + ': ' + spellcheckLanguage 28 | 29 | langMenu = (window.availableLanguages ? []).map (el) -> 30 | { 31 | label: el, 32 | action: {name: 'setspellchecklanguage', params: [el]} 33 | } 34 | 35 | [ 36 | ...params.dictionarySuggestions.map (el) -> 37 | { 38 | label: el, 39 | action: {name: 'replacemisspelling', params: [el]} 40 | } 41 | { 42 | type: 'separator' 43 | visible: params?.dictionarySuggestions?.length > 0 44 | } 45 | { 46 | label: i18n.__('menu.edit.spell_check.title:Spellcheck') 47 | submenu: [ 48 | { 49 | label: spellCheck 50 | enabled: false 51 | checked: spellcheckLanguage != 'none' 52 | action: {name: 'setspellchecklanguage', params: ['none']} 53 | } 54 | 55 | { 56 | label: i18n.__('menu.edit.spell_check.turn_off:Turn spellcheck off') 57 | visible: spellcheckLanguage != 'none' 58 | action: {name: 'setspellchecklanguage', params: ['none']} 59 | } 60 | 61 | { 62 | label: i18n.__('menu.edit.spell_check.available:Available languages') 63 | submenu: langMenu 64 | } 65 | ] 66 | } 67 | { type: 'separator' } 68 | { 69 | label: i18n.__('menu.edit.save_image:Save Image') 70 | visible: canShowSaveImg 71 | action: {name: 'saveimage', params: [params.srcURL]} 72 | } 73 | { type: 'separator' } if canShowSaveImg 74 | { 75 | label: i18n.__('menu.edit.undo:Undo') 76 | role: 'undo' 77 | enabled: params.editFlags.canUndo 78 | visible: true 79 | } 80 | { 81 | label: i18n.__('menu.edit.redo:Redo') 82 | role: 'redo' 83 | enabled: params.editFlags.canRedo 84 | visible: true 85 | } 86 | { type: 'separator' } 87 | { 88 | label: i18n.__('menu.edit.cut:Cut') 89 | role: 'cut' 90 | enabled: params.editFlags.canCut 91 | visible: true 92 | } 93 | { 94 | label: i18n.__('menu.edit.copy:Copy') 95 | role: 'copy' 96 | enabled: params.editFlags.canCopy 97 | visible: true 98 | } 99 | { 100 | label: i18n.__('menu.edit.copy_link:Copy Link') 101 | visible: canShowCopyLink 102 | action: {name: 'copytext', params: [params.linkText]} 103 | } 104 | { 105 | label: i18n.__('menu.edit.copy_image_link:Copy Image Link') 106 | visible: canShowCopyImgLink 107 | action: {name: 'copytext', params: [params.srcURL]} 108 | } 109 | { 110 | label: i18n.__('menu.edit.paste:Paste') 111 | role: 'paste' 112 | visible: (isContentPasteable() && 113 | viewstate.state == viewstate.STATE_NORMAL) || params.isEditable 114 | }].filter (n) -> n != undefined 115 | 116 | templateAboutContext = (params, viewstate) -> 117 | [{ 118 | label: i18n.__('menu.edit.copy') 119 | role: 'copy' 120 | enabled: params.editFlags.canCopy 121 | } 122 | { 123 | label: i18n.__('menu.edit.copy_link:Copy Link') 124 | visible: params.linkURL != '' and params.mediaType == 'none' 125 | action: {name: 'copytext', params: [params.linkText]} 126 | }] 127 | module.exports = (params, viewstate) -> 128 | if viewstate.state == viewstate.STATE_ABOUT 129 | templateAboutContext(params, viewstate) 130 | else 131 | templateContext(params, viewstate) 132 | -------------------------------------------------------------------------------- /src/ui/views/controls.coffee: -------------------------------------------------------------------------------- 1 | 2 | # some unused icons/actions 3 | # {icon:'icon-user-add', action:'adduser'} 4 | # {icon:'icon-pencil', action:'renameconv'} 5 | # {icon:'icon-videocam', action:'videocall'} 6 | # {icon:'icon-phone', action:'voicecall'} 7 | 8 | onclickaction = (a) -> (ev) -> action a 9 | 10 | module.exports = view (models) -> 11 | {conv, viewstate} = models 12 | c = conv[viewstate.selectedConv] 13 | div class:'controls', -> 14 | div class:'button', title: i18n.__('conversation.add:Add new conversation'), 15 | onclick:onclickaction('addconversation'), -> span class:'material-icons', 'add' 16 | -------------------------------------------------------------------------------- /src/ui/views/convhead.coffee: -------------------------------------------------------------------------------- 1 | {nameofconv} = require '../util' 2 | 3 | ipc = require('electron').ipcRenderer 4 | moment = require('moment') 5 | 6 | onclickaction = (a) -> (ev) -> action a 7 | 8 | # Update the presence every 60 seconds 9 | setInterval () -> 10 | action 'updatepresence' 11 | , 60 * 1000 12 | 13 | updateActiveTimer = null 14 | 15 | module.exports = view (models) -> 16 | {conv, viewstate} = models 17 | 18 | if !viewstate.useSystemDateFormat 19 | moment.locale(i18n.getLocale()) 20 | else 21 | moment.locale(window.navigator.language) 22 | 23 | conv_id = viewstate?.selectedConv 24 | c = conv[conv_id] 25 | div class:'headwrap', -> 26 | return if not c # region cannot take undefined 27 | name = nameofconv c 28 | active = conv.lastActiveSec(c) 29 | 30 | if updateActiveTimer? 31 | clearInterval updateActiveTimer 32 | 33 | # Update the active label every 10 seconds 34 | if active != 0 35 | updateActiveTimer = setInterval () -> 36 | el = document.querySelector('.namewrapper > .active') 37 | if el? 38 | active = parseInt el.getAttribute('active') 39 | el.innerHTML = i18n.__('conversation.active:Active %s', moment.unix(active).fromNow()) if active != 0 40 | , 10 * 1000 41 | 42 | div class:'namewrapper', -> 43 | span class:'name', -> 44 | if conv.isQuiet(c) 45 | span class:'material-icons', 'notifications_off' 46 | if conv.isStarred(c) 47 | span class:'material-icons', "star" 48 | name 49 | span class:'active', active:active, i18n.__('conversation.active:Active %s', moment.unix(active).fromNow()) if active != 0 50 | 51 | div class:"optionwrapper", -> 52 | div class:'button' 53 | , title: i18n.__('conversation.options:Conversation Options') 54 | , onclick:convoptions, -> span class:'material-icons', 'more_vert' 55 | div class:'convoptions' 56 | , title:i18n.__('conversation.settings:Conversation settings'), -> 57 | div class:'button' 58 | , title: i18n.__('menu.view.notification.toggle:Toggle notifications') 59 | , onclick:onclickaction('togglenotif') 60 | , -> 61 | if conv.isQuiet(c) 62 | span class:'material-icons', 'notifications_off' 63 | else 64 | span class:'material-icons', 'notifications' 65 | div class:'option-label', i18n.__n('notification:Notification', 1) 66 | div class:'button' 67 | , title:i18n.__('favorite.star_it:Star / unstar') 68 | , onclick:onclickaction('togglestar') 69 | , -> 70 | if not conv.isStarred(c) 71 | span class:'material-icons', 'star_border' 72 | else 73 | span class:'material-icons', 'star' 74 | div class:'option-label', i18n.__n('favorite.title:Favorite',1) 75 | div class:'button' 76 | , title:i18n.__('settings:Settings') 77 | , onclick:onclickaction('convsettings') 78 | , -> 79 | span class:'material-icons', 'info_outline' 80 | div class:'option-label', i18n.__('details:Details') 81 | if process.platform is 'win32' 82 | div class:"win-buttons", -> 83 | div class:'button', -> 84 | button id: "win-minimize" 85 | , title:i18n.__('window.controls:Minimize') 86 | , onclick: onclickaction('minimize') 87 | div class:'button', -> 88 | button id: "win-maximize" 89 | , title:i18n.__('window.controls:Maximize') 90 | , onclick: onclickaction('resizewindow') 91 | div class:'button', -> 92 | button id: "win-restore" 93 | , title:i18n.__('window.controls:Restore') 94 | , onclick: onclickaction('resizewindow') 95 | div class:'button', -> 96 | button id: "win-close" 97 | , title:i18n.__('window.controls:Close') 98 | , onclick: onclickaction('close') 99 | 100 | ipc.on 'on-mainwindow.maximize', () -> 101 | toggleHidden document.getElementById('win-maximize'), true 102 | toggleHidden document.getElementById('win-restore'), false 103 | 104 | ipc.on 'on-mainwindow.unmaximize', () -> 105 | toggleHidden document.getElementById('win-maximize'), false 106 | toggleHidden document.getElementById('win-restore'), true 107 | 108 | toggleHidden = (element, hidden) -> 109 | return unless element 110 | if hidden 111 | element.style.display = 'none' 112 | else 113 | element.style.display = 'inline' 114 | 115 | document.querySelector('body').addEventListener 'click', (event) -> 116 | if not document.querySelector('.optionwrapper').contains(event.target) 117 | document.querySelector('.convoptions').classList.remove('open') 118 | 119 | convoptions = -> 120 | {viewstate} = models 121 | document.querySelector('.convoptions').classList.toggle('open'); 122 | if viewstate.state == viewstate.STATE_ADD_CONVERSATION 123 | action 'saveconversation' 124 | -------------------------------------------------------------------------------- /src/ui/views/dockicon.coffee: -------------------------------------------------------------------------------- 1 | ipc = require('electron').ipcRenderer 2 | # calling show multiple times makes the osx app flash 3 | # therefore we remember here if the dock is already shown 4 | # and we avoid re-calling app.dock.show() multiple times 5 | dockAlreadyVisible = true 6 | 7 | module.exports = (viewstate) -> 8 | if require('os').platform() isnt 'darwin' then return 9 | 10 | if viewstate.hidedockicon and (dockAlreadyVisible is true) 11 | console.log 'hiding dock' 12 | ipc.send 'app.dock:hide' 13 | dockAlreadyVisible = false 14 | 15 | if not viewstate.hidedockicon and (dockAlreadyVisible is false) 16 | console.log 'showing dock' 17 | ipc.send 'app.dock:show' 18 | dockAlreadyVisible = true 19 | -------------------------------------------------------------------------------- /src/ui/views/index.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | applayout: require './applayout' 3 | convlist: require './convlist' 4 | startup: require './startup' 5 | listhead: require './listhead' 6 | convhead: require './convhead' 7 | messages: require './messages' 8 | input: require './input' 9 | conninfo: require './conninfo' 10 | convadd: require './convadd' 11 | controls: require './controls' 12 | notifications: require './notifications' 13 | typinginfo: require './typinginfo' 14 | menu: require './menu' 15 | trayicon: require './trayicon' 16 | dockicon: require './dockicon' 17 | about: require './about' 18 | -------------------------------------------------------------------------------- /src/ui/views/listhead.coffee: -------------------------------------------------------------------------------- 1 | module.exports = view (models) -> 2 | div class:'listheadlabel', -> 3 | if process.platform isnt 'darwin' 4 | button title: i18n.__('menu.title:Menu'), onclick: togglemenu, -> 5 | i class:'material-icons', "menu" 6 | span i18n.__n("conversation.title:Conversations", 2) 7 | 8 | togglemenu = -> 9 | if process.platform isnt 'darwin' 10 | action 'togglemenu' 11 | -------------------------------------------------------------------------------- /src/ui/views/startup.coffee: -------------------------------------------------------------------------------- 1 | path = require 'path' 2 | 3 | {later} = require '../util' 4 | 5 | module.exports = view (models) -> 6 | {connection, viewstate} = models 7 | classList = ['connecting'] 8 | if viewstate.loadedContacts 9 | classList.push 'hide' 10 | 11 | div class: classList.join(' '), -> 12 | div -> 13 | div -> 14 | img src: path.join YAKYAK_ROOT_DIR, '..', 'icons', 'icon@32.png' 15 | div -> 16 | span class: 'text state_connecting', -> 17 | if connection.state == connection.CONNECTING 18 | i18n.__ 'connection.connecting:Connecting' 19 | else if connection.state == connection.CONNECT_FAILED 20 | i18n.__ 'connection.connecting:Not Connected (check connection)' 21 | else 22 | # connection.CONNECTED 23 | i18n.__ 'connection.connecting:Loading contacts' 24 | div class: 'spinner', -> 25 | div class: 'bounce1', '' 26 | div class: 'bounce2', '' 27 | div class: 'bounce3', '' 28 | -------------------------------------------------------------------------------- /src/ui/views/trayicon.coffee: -------------------------------------------------------------------------------- 1 | path = require 'path' 2 | os = require 'os' 3 | i18n = require 'i18n' 4 | 5 | {later} = require '../util' 6 | 7 | trayIconsFile = if os.platform() == 'darwin' 8 | "read": 'osx-icon-read-Template.png' 9 | "read-colorblind": 'osx-icon-read-Template.png' 10 | "unread": 'osx-icon-unread-Template.png' 11 | else if process.env.XDG_CURRENT_DESKTOP && process.env.XDG_CURRENT_DESKTOP.match(/KDE/) 12 | # This is to work around a bug with electron apps + KDE not showing correct icon size. 13 | "read": 'icon-read@20.png' 14 | "read-colorblind": 'icon-read@20_blue.png' 15 | "unread": 'icon-unread@20.png' 16 | else 17 | "read": 'icon-read@8x.png' 18 | "read-colorblind": 'icon-read@8x_blue.png' 19 | "unread": 'icon-unread@8x.png' 20 | 21 | trayIcons = {} 22 | trayIcons[k] = path.join __dirname, '..', '..', 'icons', v for k,v of trayIconsFile 23 | 24 | # TODO: this is all WIP 25 | quit = -> 26 | 27 | compact = (array) -> item for item in array when item 28 | 29 | create = (viewstate) -> 30 | update(0, viewstate) 31 | 32 | destroy = -> 33 | later -> action 'destroytray' 34 | 35 | update = (unreadCount, viewstate) -> 36 | # update menu 37 | templateContextMenu = compact([ 38 | { 39 | label: i18n.__ 'menu.view.tray.toggle_minimize:Toggle window show/hide' 40 | click_action: 'togglewindow' 41 | } 42 | 43 | { 44 | label: i18n.__ "menu.view.tray.start_minimize:Start minimized to tray" 45 | type: "checkbox" 46 | checked: viewstate.startminimizedtotray 47 | click_action: 'togglestartminimizedtotray' 48 | } 49 | 50 | { 51 | label: i18n.__ 'menu.view.notification.show:Show notifications' 52 | type: "checkbox" 53 | checked: viewstate.showPopUpNotifications 54 | # usage of already existing method and implements same logic 55 | # as other toggle... methods 56 | click_action: 'togglepopupnotifications' 57 | } 58 | 59 | { 60 | label: i18n.__ "menu.view.tray.close:Close to tray" 61 | type: "checkbox" 62 | checked: viewstate.closetotray 63 | click_action: 'toggleclosetotray' 64 | } 65 | 66 | { 67 | label: i18n.__ 'menu.view.hide_dock:Hide Dock icon' 68 | type: 'checkbox' 69 | checked: viewstate.hidedockicon 70 | click_action: 'togglehidedockicon' 71 | } if os.platform() == 'darwin' 72 | 73 | { 74 | label: i18n.__('menu.file.quit:Quit'), 75 | click_action: 'quit' 76 | } 77 | ]) 78 | 79 | # update icon 80 | try 81 | if unreadCount > 0 82 | later -> 83 | action 'settray', templateContextMenu, trayIcons["unread"], i18n.__('title:YakYak - Hangouts Client') 84 | else 85 | readIconName = if viewstate?.colorblind then 'read-colorblind' else 'read' 86 | later -> 87 | action 'settray', templateContextMenu, trayIcons[readIconName], i18n.__('title:YakYak - Hangouts Client') 88 | catch e 89 | console.log 'missing icons', e 90 | 91 | 92 | module.exports = ({viewstate, conv}) -> 93 | if viewstate.showtray 94 | update(conv.unreadTotal(), viewstate) 95 | else 96 | destroy() 97 | -------------------------------------------------------------------------------- /src/ui/views/typinginfo.coffee: -------------------------------------------------------------------------------- 1 | {scrollToBottom} = require './messages' 2 | {nameof} = require '../util' 3 | 4 | module.exports = view (models) -> 5 | {viewstate, conv, entity} = models 6 | 7 | conv_id = viewstate?.selectedConv 8 | c = conv[conv_id] 9 | 10 | div class:'typing '+('typingnow' if c?.typing?.length), -> 11 | return unless c 12 | span class:'material-icons', 'more_horiz' if c?.typing?.length 13 | for t, i in (c.typing ? []) 14 | name = nameof entity[t.user_id.chat_id] 15 | span class:"typing_#{t.status}", name 16 | pass ', ' if i < (c.typing.length - 1) 17 | pass " #{i18n.__ 'input.typing:is typing'}" if c?.typing?.length 18 | -------------------------------------------------------------------------------- /test/global.coffee: -------------------------------------------------------------------------------- 1 | # just so that fnuc loads before we redefine eql 2 | require 'hangupsjs' 3 | 4 | global.chai = require 'chai' 5 | 6 | chai.use require 'sinon-chai' 7 | sinon = require 'sinon' 8 | 9 | global.stub = sinon.stub 10 | global.spy = sinon.spy 11 | global.assert = chai.assert 12 | global.eql = chai.assert.deepEqual 13 | 14 | # trifl globals 15 | global.updated = -> 16 | global.action = -> 17 | 18 | # dummy localStorage 19 | global.localStorage = {} 20 | -------------------------------------------------------------------------------- /test/test-conv-init.coffee: -------------------------------------------------------------------------------- 1 | init = require './init.json' 2 | conv = require '../src/ui/models/conv' 3 | entity = require '../src/ui/models/entity' 4 | 5 | describe 'conv', -> 6 | 7 | beforeEach -> 8 | conv._reset() 9 | entity._reset() 10 | 11 | describe 'initFromConvStates', -> 12 | 13 | it 'populates lookup', -> 14 | added = conv._initFromConvStates init.conv_states 15 | eql 3, added 16 | c = conv['UzNxjbBsPhAAAQ'] 17 | assert.isNotNull c 18 | eql 'UzNxjbBsPhAAAQ', c.conversation_id.id 19 | 20 | it 'populates entities with found entities', -> 21 | conv._initFromConvStates init.conv_states 22 | eql 8, entity.count() 23 | eql entity['10964681753'], { 24 | fallback_name:'John Snow' 25 | id:'10964681753' 26 | } 27 | -------------------------------------------------------------------------------- /test/test-entity-funcs.coffee: -------------------------------------------------------------------------------- 1 | init = require './init.json' 2 | entity = require '../src/ui/models/entity' 3 | 4 | describe 'entity', -> 5 | 6 | beforeEach -> 7 | entity._reset() 8 | 9 | describe 'count', -> 10 | 11 | it 'counts total', -> 12 | added = entity._initFromEntities init.entities 13 | eql 2, added 14 | eql added, entity.count() 15 | 16 | describe 'isSelf', -> 17 | 18 | it 'does not true for undefined self', -> 19 | eql entity.self, undefined 20 | eql false, entity.isSelf 'foo' 21 | 22 | describe 'add', -> 23 | 24 | it 'adds an .id prop which is the chat_id and returns the result', -> 25 | ret = entity.add { 26 | id: 27 | chat_id:'a' 28 | gaia_id:'b' 29 | } 30 | eql entity['a'], { 31 | id:'a' 32 | } 33 | assert.strictEqual ret, entity['a'] 34 | 35 | it 'adds a gaia_id ref if not same as chat_id', -> 36 | entity.add { 37 | id: 38 | chat_id:'a' 39 | gaia_id:'b' 40 | } 41 | eql entity['b'], { 42 | id:'a' 43 | } 44 | 45 | it 'merges properties into the object', -> 46 | entity.add { 47 | id: 48 | gaia_id:'a' 49 | chat_id:'a' 50 | properties: 51 | some:'prop' 52 | } 53 | eql entity['a'], { 54 | id:'a' 55 | some:'prop' 56 | } 57 | 58 | it 'merges all other props', -> 59 | entity.add { 60 | id: 61 | gaia_id:'a' 62 | chat_id:'a' 63 | some:'outer prop' 64 | } 65 | eql entity['a'], { 66 | id:'a' 67 | some:'outer prop' 68 | } 69 | 70 | describe 'needEntity', -> 71 | 72 | afterEach -> 73 | global.action = -> 74 | 75 | it 'gathers entity id during a timeout and does an action for all', (done) -> 76 | entity.needEntity 'a', 10 77 | entity.needEntity 'b', 10 78 | entity.needEntity 'c', 10 79 | global.action = (n, ids) -> 80 | eql n, 'getentity' 81 | eql ids, ['a','b','c'] 82 | done() 83 | -------------------------------------------------------------------------------- /test/test-entity-init.coffee: -------------------------------------------------------------------------------- 1 | init = require './init.json' 2 | entity = require '../src/ui/models/entity' 3 | 4 | describe 'entity', -> 5 | 6 | beforeEach -> 7 | entity._reset() 8 | 9 | describe 'initFromEntities', -> 10 | 11 | it 'populates lookup', -> 12 | added = entity._initFromEntities init.entities 13 | eql 2, added 14 | eql 2, entity.count() 15 | ent = entity['12230235892'] 16 | assert.isNotNull ent 17 | eql 'Bo André Tenström', ent.display_name 18 | 19 | describe 'initFromSelfEntity', -> 20 | 21 | it 'adds entity to lookup', -> 22 | e = entity._initFromSelfEntity init.self_entity 23 | assert.isNotNull e 24 | assert 'Martin Algesten', e.display_name 25 | assert.strictEqual e, entity['10964681753'] 26 | 27 | it 'adds entity as .self', -> 28 | e = entity._initFromSelfEntity init.self_entity 29 | assert.isNotNull e 30 | assert 'Martin Algesten', e.display_name 31 | assert.strictEqual e, entity.self 32 | 33 | it 'ensures isSelf works', -> 34 | entity._initFromSelfEntity init.self_entity 35 | eql true, entity.isSelf '10964681753' 36 | 37 | it 'keeps the self entity even if merged', -> 38 | entity._initFromSelfEntity init.self_entity 39 | entity.add { 40 | id: {gaia_id:'10964681753', chat_id:'10964681753'} 41 | testing: 'panda' 42 | } 43 | e = entity['10964681753'] 44 | assert.isNotNull e 45 | eql 'Martin Algesten', e.display_name 46 | eql 'panda', e.testing 47 | assert.isTrue entity.isSelf '10964681753' 48 | -------------------------------------------------------------------------------- /test/test-userinput.coffee: -------------------------------------------------------------------------------- 1 | { MessageActionType } = require 'hangupsjs' 2 | viewstate = require '../src/ui/models/viewstate' 3 | userinput = require '../src/ui/models/userinput' 4 | 5 | describe 'userinput', -> 6 | 7 | describe 'buildChatMessage', -> 8 | 9 | it 'takes a text and does good', -> 10 | sender = { firstName: 'John' } 11 | viewstate.selectedConv = 'c123' 12 | msg = userinput.buildChatMessage sender, 'foo' 13 | eql msg.conv_id, 'c123' 14 | eql msg.image_id, undefined 15 | # eql msg.message_action_type, [[MessageActionType.NONE, '']] # removing assertion temporarilly 16 | eql msg.segs, [[0,'foo']] 17 | eql msg.segsj, [{text:'foo', type:'TEXT'}] 18 | assert.isNotNull msg.client_generated_id 19 | assert.isNotNull msg.ts 20 | eql msg.otr, 2 21 | 22 | it 'recognizes /me messages', -> 23 | sender = { first_name: 'John' } 24 | msg = userinput.buildChatMessage sender, '/me says hello' 25 | eql msg.message_action_type, [[MessageActionType.ME_ACTION, '']] 26 | eql msg.segs, [[0,'John says hello']] 27 | eql msg.segsj, [{text:'John says hello', type:'TEXT'}] 28 | 29 | describe 'parse', -> 30 | 31 | mb = null 32 | coll = "" 33 | beforeEach -> 34 | coll = "" 35 | mb = { 36 | text: spy -> coll += "t" 37 | link: spy -> coll += "l" 38 | linebreak: spy -> coll += "n" 39 | } 40 | 41 | it 'does text', -> 42 | userinput.parse mb, 'foo' 43 | eql mb.text.args, [['foo']] 44 | eql coll, 't' 45 | 46 | it 'does text with whitespace', -> 47 | userinput.parse mb, ' \n \n foo' 48 | eql mb.text.args, [[' '],[' '],[' foo']] 49 | eql mb.linebreak.args, [[],[]] 50 | eql coll, 'tntnt' 51 | 52 | it 'does \\n linebreaks', -> 53 | userinput.parse mb, 'foo\nbar' 54 | eql mb.linebreak.args, [[]] 55 | eql mb.text.args, [['foo'],['bar']] 56 | eql coll, 'tnt' 57 | 58 | it 'does \\r\\n linebreaks', -> 59 | userinput.parse mb, 'foo\r\nbar' 60 | eql mb.linebreak.args, [[]] 61 | eql mb.text.args, [['foo'],['bar']] 62 | eql coll, 'tnt' 63 | 64 | it 'does not ignore last linebreak', -> 65 | userinput.parse mb, 'foo\n' 66 | eql mb.text.args, [['foo']] 67 | eql mb.linebreak.args, [[]] 68 | eql coll, 'tn' 69 | 70 | it 'does not ignore many last linebreak', -> 71 | userinput.parse mb, 'foo \n\n\n' 72 | eql mb.text.args, [['foo ']] 73 | eql mb.linebreak.args, [[],[],[]] 74 | eql coll, 'tnnn' 75 | 76 | it 'identifies links only', -> 77 | userinput.parse mb, 'http://www.abc.com' 78 | eql mb.link.args, [['http://www.abc.com','http://www.abc.com']] 79 | eql coll, 'l' 80 | 81 | it 'finds links in text', -> 82 | userinput.parse mb, 'a http://www.abc.com b' 83 | eql mb.text.args, [['a '],[' b']] 84 | eql mb.link.args, [['http://www.abc.com','http://www.abc.com']] 85 | eql coll, 'tlt' 86 | 87 | it 'finds multiple links in text', -> 88 | userinput.parse mb, 'a http://www.abc.com b https://foo.bar c' 89 | eql mb.text.args, [['a '],[' b '],[' c']] 90 | eql mb.link.args, [['http://www.abc.com','http://www.abc.com'], 91 | ['https://foo.bar','https://foo.bar']] 92 | eql coll, 'tltlt' 93 | --------------------------------------------------------------------------------