new: tool to search for non-self dual spaceships

error: pathspec 'tools/check_self_duality.coffee' did not match any file(s) known to git

--- tools/check_self_duality.coffee
+++ tools/check_self_duality.coffee
@@ -0,0 +1,92 @@
+#!/usr/bin/env coffee
+fs = require "fs"
+{Cells, evaluateCellList, getDualTransform, splitPattern} = require "../scripts-src/cells"
+rules = require "../scripts-src/rules"
+stdio = require "stdio"
+{mod2} = require "../scripts-src/math_util"
+#requireemnts:
+# - stdio
+
+
+isSelfDual = (rule, record, dualTfm) ->
+  offset = [record.result.dx, record.result.dy]
+
+  #canonicalize original pattern orientation, jsut to be sure.
+  # usually, library is already canonicalized
+  [pattern, dx, dy] = Cells.canonicalize_spaceship record.result.cells, rule, record.result.dx, record.result.dy
+
+  [dual, dx1, dy1] = Cells.getDualSpaceship pattern, rule, dx, dy
+  if dx1 isnt dx or dy1 isnt dy
+    throw new Error "Dual can't be rotated"
+
+  #now evaluate original until it matches dual, for not more than result.period steps.
+
+  snap_below = (x,generation) ->
+      x - mod2(x + generation)
+
+  offsetToOrigin = (pattern, bounds, generation) ->
+      [x0,y0] = bounds
+      x0 = snap_below x0, generation
+      y0 = snap_below y0, generation
+      Cells.offset pattern, -x0, -y0
+      return [pattern, x0, y0]
+
+  #just make sure that dual is at 0
+  [dual] = offsetToOrigin dual, Cells.bounds(dual), 0
+  Cells.sortXY dual
+  [pattern] = offsetToOrigin pattern, Cells.bounds(pattern), 0
+  Cells.sortXY pattern
+
+  #console.log "#### Pattern: #{Cells.to_rle(pattern)} Dual: #{Cells.to_rle(dual)}"
+  for i in [1 .. record.result.period]
+    #phase is always 0.
+    pattern1 = evaluateCellList rule.rules[0], pattern, 0
+
+    bounds = Cells.bounds pattern1
+    [pattern, x0, y0] = offsetToOrigin pattern1, bounds, 1 #here, phase is 1
+    Cells.sortXY pattern
+    #console.log "####   #{i}\t#{Cells.to_rle(pattern)}"
+    if Cells.areEqual pattern, dual
+      #console.log "#### self-dual at step #{i} (period = #{record.result.period}): #{Cells.to_rle(pattern)}"
+      return true
+  #no self-duality found
+  return false
+
+
+
+##################
+# top-level code #
+##################
+main = ->
+  opts = stdio.getopt {
+    'rule': {key: 'r', args: 1, description: "Reversible cellular automata rule, required for normalization. Format: 16 comma-separated decimals."}
+    }, " libary.json"
+
+  unless opts.args?
+    process.stderr.write "No input files specified\n"
+    process.exit 1
+
+  if opts.rule
+    rule = rules.parse opts.rule
+  else
+    rule = rules.from_list [[0,2,8,3,1,5,6,7,4,9,10,11,12,13,14,15]]
+
+  [dualTfmName, dualTfm, dualBlockTfm] = getDualTransform rule
+  if dualTfmName
+    process.stderr.write "Rule has dual transform: #{dualTfmName}\n"
+  else
+    process.stderr.write "Rule has no dual transform\n"
+    process.exit 1
+
+  data = JSON.parse fs.readFileSync opts.args[0]
+  process.stderr.write "Read library of #{data.length} items\n"
+
+  nonSelfDualLib = []
+  for record in data
+    unless isSelfDual rule, record, dualTfm
+      nonSelfDualLib.push record
+
+  console.log JSON.stringify nonSelfDualLib
+  return
+
+main()


Update zh-HANS.coffee

Update zh-HANS.coffee

--- app/locale/zh-HANS.coffee
+++ app/locale/zh-HANS.coffee
@@ -3,7 +3,7 @@
     loading: "读取中……"
     saving: "保存中……"
     sending: "发送中……"
-    cancel: "退出"
+    cancel: "取消"
     save: "保存"
     delay_1_sec: "1 秒"
     delay_3_sec: "3 秒"
@@ -14,31 +14,31 @@

   modal:
     close: "关闭"
-    okay: "好"
+    okay: "好的"

   not_found:
     page_not_found: "找不到网页"

   nav:
     play: "玩"
-    editor: "编辑"
+    editor: "编辑器"
     blog: "博客"
     forum: "论坛"
-    admin: "超级管理员"
+    admin: "管理"
     home: "首页"
     contribute: "贡献"
     legal: "法律"
     about: "关于"
-    contact: "联系我们"
+    contact: "联系"
     twitter_follow: "关注"
     employers: "雇佣我们"

   versions:
     save_version_title: "保存新版本"
-#    new_major_version: "New Major Version"
-#    cla_prefix: "To save changes, first you must agree to our"
-#    cla_url: "CLA"
-#    cla_suffix: "."
+    new_major_version: "新的重要版本"
+    cla_prefix: "要想保存更改，您必须先同意我们的"
+    cla_url: "贡献者许可协议"
+    cla_suffix: "。"
     cla_agree: "我同意"

   login:
@@ -63,40 +63,40 @@

   home:
     slogan: "通过游戏学习 Javascript"
-    no_ie: "抱歉！Internet Explorer 9等更旧的预览器打不开此网站。"
-    no_mobile: "CodeCombat 不是针对手机设备设计的，所以可能无法达到做好的体验！"
+    no_ie: "抱歉！Internet Explorer 9 等旧式预览器无法使用本网站。"
+    no_mobile: "CodeCombat 不是针对手机设备设计的，所以可能无法达到最好的体验！"
     play: "开始游戏"

   play:
     choose_your_level: "选取难度"
-    adventurer_prefix: "你可以选择以下任意关卡，或者讨论以上的关卡。"
+    adventurer_prefix: "你可以选择以下任意关卡，或者讨论以上的关卡。到"
     adventurer_forum: "冒险者论坛"
-    adventurer_suffix: "."
+    adventurer_suffix: "。"
     campaign_beginner: "新手作战"
-    campaign_beginner_description: "……在这里可以学到编程技巧。"
+    campaign_beginner_description: "……在这里你可以学习到编程技巧。"
     campaign_dev: "随机困难关卡"
     campaign_dev_description: "……在这里你可以学到做一些复杂功能的接口。"
     campaign_multiplayer: "多人竞技场"
-    campaign_multiplayer_description: "……在这里你可以和其他玩家们进行代码肉搏战。"
-    campaign_player_created: "已创建的玩家"
+    campaign_multiplayer_description: "……在这里你可以与其他玩家进行代码肉搏战。"
+    campaign_player_created: "创建玩家"
     campaign_player_created_description: "……在这里你可以与你的小伙伴的创造力战斗 技术指导."
-    level_difficulty: "难度"
+    level_difficulty: "难度："

   contact:
     contact_us: "联系我们"
-    welcome: "很高兴收到你的信！用这个表格给我们发邮件。 "
-    contribute_prefix: "如果你想贡献代码，请看我们的 "
-    contribute_page: "贡献代码页面"
+    welcome: "我们很乐意收到你的信！用这个表单给我们发邮件。 "
+    contribute_prefix: "如果你想贡献什么，请看我们的 "
+    contribute_page: "贡献页面"
     contribute_suffix: "！"
-    forum_prefix: "对任何公共部分，放手去干吧 "
+    forum_prefix: "对于任何公开部分，请尝试用"
     forum_page: "我们的论坛"
-    forum_suffix: "代替 "
+    forum_suffix: "代替"
     send: "意见反馈"

   diplomat_suggestion:
     title: "帮我们翻译 CodeCombat"
     sub_heading: "我们需要您的语言技能"
-    pitch_body: "我们开发了 CodeCombat 的英文版，但是现在我们的玩家遍布全球。很多人想玩中文（简体）版的，却不会说英语，所以如果你中英文都会，请考虑一下参加我们的翻译工作，帮忙把 CodeCombat 网站还有所有的关卡翻译成中文（简体）。"
+    pitch_body: "我们开发了 CodeCombat 的英文版，但是现在我们的玩家遍布全球。很多人英语不熟练，但很想玩简体中文版的游戏，所以如果你中英文都很熟练，请考虑参加我们的翻译工作，帮忙把 CodeCombat 网站还有所有的关卡翻译成简体中文。"
     missing_translations: "未翻译的文本将显示为英文。"
     learn_more: "了解更多有关成为翻译人员的说明"
     subscribe_as_diplomat: "提交翻译人员申请"
@@ -130,28 +130,28 @@
     new_password_verify: "核实"
     email_subscriptions: "邮箱验证"
     email_announcements: "通知"
-    email_notifications_description: "定期接受来自你的账户的通知。"
+    email_notifications_description: "接收来自你的账户的定期通知。"
     email_announcements_description: "接收关于 CodeCombat 最近的新闻和发展的邮件。"
     contributor_emails: "贡献者通知"
-    contribute_prefix: "我们在寻找志同道合的人！请到 "
+    contribute_prefix: "我们在寻找志同道合的人！请到"
     contribute_page: "贡献页面"
-    contribute_suffix: " 查看更多信息。"
+    contribute_suffix: "查看更多信息。"
     email_toggle: "切换所有"
     error_saving: "保存时出错"
-    saved: "保存修改"
+    saved: "更改已保存"
     password_mismatch: "密码不匹配。"

   account_profile:
     edit_settings: "编辑设置"
-    profile_for_prefix: "关于TA的基本资料："
+    profile_for_prefix: "关于他的基本资料："
     profile_for_suffix: ""
     profile: "基本资料"
-    user_not_found: "没有找到用户。检查URL？"
-    gravatar_not_found_mine: "我们找不到TA的基本资料："
-    gravatar_not_found_email_suffix: "."
+    user_not_found: "没有找到用户。网址有错？"
+    gravatar_not_found_mine: "我们找不到他的基本资料："
+    gravatar_not_found_email_suffix: "。"
     gravatar_signup_prefix: "去注册 "
-    gravatar_signup_suffix: " 去设置!"
-    gravatar_not_found_other: "哎呀，没有与这个人的邮箱相关的资料。"
+    gravatar_signup_suffix: " 来设置！"
+    gravatar_not_found_other: "哎呀，没有与这个邮箱相关的资料。"
     gravatar_contact: "联系"
     gravatar_websites: "网站"
     gravatar_accounts: "显示为"
@@ -198,33 +198,33 @@
     tome_select_spell: "选择一个法术"
     tome_select_a_thang: "选择人物来 "
     tome_available_spells: "可用的法术"
-    hud_continue: "继续（按 shift-空格）"
-    spell_saved: "输入已保存"
-
-#  admin:
-#    av_title: "Admin Views"
-#    av_entities_sub_title: "Entities"
-#    av_entities_users_url: "Users"
-#    av_entities_active_instances_url: "Active Instances"
-#    av_other_sub_title: "Other"
-#    av_other_debug_base_url: "Base (for debugging base.jade)"
-#    u_title: "User List"
-#    lg_title: "Latest Games"
+    hud_continue: "继续（按 Shift-空格）"
+    spell_saved: "咒语已保存"
+
+  admin:
+    av_title: "管理员视图"
+    av_entities_sub_title: "实体"
+    av_entities_users_url: "用户"
+    av_entities_active_instances_url: "活动实例"
+    av_other_sub_title: "其他"
+    av_other_debug_base_url: "Base（用于调试 base.jade）"
+    u_title: "用户列表"
+    lg_title: "最新的游戏"

   editor:
     main_title: "CodeCombat 编辑器"
-    main_description: "建立自己的关卡, 战役, 单位 和教育内容。我们提供所有你需要的工具！"
+    main_description: "建立你自己的关卡、 战役、单元和教育内容。我们会提供所有你需要的工具！"
     article_title: "提示编辑器"
-    article_description: "编写提示，让玩家可以使用编程概念，来通过各种的关卡和战役。"
+    article_description: "编写提示，让玩家可以使用编程概念来通过各种关卡和战役。"
     thang_title: "物体编辑器"
-    thang_description: "创建单位，并定义单位的逻辑、图形和音频。目前只支持导入Flash导出的矢量图形。"
+    thang_description: "创建单元，并定义单元的逻辑、图形和音频。目前只支持导入 Flash 导出的矢量图形。"
     level_title: "关卡编辑器"
     level_description: "所有用来创造所有难度的关卡的工具，包括脚本、上传音频和构建自定义逻辑。"
-    security_notice: "编辑器的许多主要功能并不是目前默认启动的。当我们改善完这些系统的安全性的时候，他们将会变成常用的。如果你想要马上使用这些功能，"
+    security_notice: "编辑器的许多主要功能并不是目前默认启动的。当我们改善完这些系统的安全性的时候，它们就会成为通常可用的。如果你想要马上使用这些功能，"
     contact_us: "联系我们！"
     hipchat_prefix: "你也可以在这里找到我们"
-    hipchat_url: "HipChat room."
-#    level_some_options: "Some Options?"
+    hipchat_url: "HipChat 房间。"
+    level_some_options: "有些选项？"
     level_tab_thangs: "物体"
     level_tab_scripts: "脚本"
     level_tab_settings: "设定"
@@ -239,7 +239,7 @@
     level_systems_tab_title: "目前所有系统"
     level_systems_btn_new: "创建新的系统"
     level_systems_btn_add: "增加系统"
-    level_components_title: "返回到所以物体主页"
+    level_components_title: "返回到所有物体主页"
     level_components_type: "类型"
     level_component_edit_title: "编辑组件"
     level_system_edit_title: "编辑系统"
@@ -249,12 +249,12 @@

   article:
     edit_btn_preview: "预览"
-    edit_article_title: "编辑文章"
+    edit_article_title: "编辑提示"

   general:
     and: "和"
     name: "姓名"
-#    body: "Body"
+    body: "正文"
     version: "版本"
     commit_msg: "提交信息"
     version_history_for: "版本历史: "
@@ -265,11 +265,11 @@
     message: "消息"

   about:
-    who_is_codecombat: "什么是CodeCombat?"
-    why_codecombat: "为什么选择CodeCombat?"
-    who_description_prefix: "一起在2013开始编写CodeCombat。在2008年时，我们也创造了"
-    who_description_suffix: "并且发展它成为第一的学习如何写中文字和日文字的Web和IOS应用"
-    who_description_ending: "现在是时候教人们如何写代码。"
+    who_is_codecombat: "什么是 CodeCombat?"
+    why_codecombat: "为什么选择 CodeCombat?"
+    who_description_prefix: "在2013年开始一起编写 CodeCombat。在2008年时，我们还创造"
+    who_description_suffix: "并且发展出了首选的学习如何写中文和日文的Web和IOS应用"
+    who_description_ending: "现在是时候教人们如何写代码了。"
 #    why_paragraph_1: "When making Skritter, George didn't know how to program and was constantly frustrated by his inability to implement his ideas. Afterwards, he tried learning, but the lessons were too slow. His housemate, wanting to reskill and stop teaching, tried Codecademy, but \"got bored.\" Each week another friend started Codecademy, then dropped off. We realized it was the same problem we'd solved with Skritter: people learning a skill via slow, intensive lessons when what they need is fast, extensive practice. We know how to fix that."
 #    why_paragraph_2: "Need to learn to code? You don't need lessons. You need to write a lot of code and have a great time doing it."
 #    why_paragraph_3_prefix: "That's what programming is about. It's gotta be fun. Not fun like"
@@ -286,61 +286,61 @@
 #    jeremy_description: "Customer support mage, usability tester, and community organizer; you've probably already spoken with Jeremy."
 #    michael_description: "Programmer, sys-admin, and undergrad technical wunderkind, Michael is the person keeping our servers online."

-#  legal:
-#    page_title: "Legal"
-#    opensource_intro: "CodeCombat is free to play and completely open source."
-#    opensource_description_prefix: "Check out "
-#    github_url: "our GitHub"
-#    opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See "
-#    archmage_wiki_url: "our Archmage wiki"
-#    opensource_description_suffix: "for a list of the software that makes this game possible."
-#    practices_title: "Respectful Best Practices"
-#    practices_description: "These are our promises to you, the player, in slightly less legalese."
-#    privacy_title: "Privacy"
-#    privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent."
-#    security_title: "Security"
-#    security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems."
-#    email_title: "Email"
-#    email_description_prefix: "We will not inundate you with spam. Through"
-#    email_settings_url: "your email settings"
-#    email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time."
-#    cost_title: "Cost"
-#    cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:"
-#    recruitment_title: "Recruitment"
-#    recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life."
-#    url_hire_programmers: "No one can hire programmers fast enough"
-#    recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you"
-#    recruitment_description_italic: "a lot"
-#    recruitment_description_ending: "the site remains free and everybody's happy. That's the plan."
-#    copyrights_title: "Copyrights and Licenses"
-#    contributor_title: "Contributor License Agreement"
-#    contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our"
-#    cla_url: "CLA"
-#    contributor_description_suffix: "to which you should agree before contributing."
-#    code_title: "Code - MIT"
-#    code_description_prefix: "All code owned by CodeCombat or hosted on codecombat.com, both in the GitHub repository or in the codecombat.com database, is licensed under the"
-#    mit_license_url: "MIT license"
+  legal:
+    page_title: "法律"
+    opensource_intro: "CodeCombat 是一个自由发挥，完全开源的项目。"
+    opensource_description_prefix: "查看"
+    github_url: "我们的 GitHub"
+    opensource_description_center: "并做你想做的修改吧！CodeCombat 是构筑在几十个开源项目的基础之上，我们爱它们。见"
+    archmage_wiki_url: "我们的 Archmage wiki"
+    opensource_description_suffix: "了解让这个游戏成为可能的名单。"
+    practices_title: "尊重最佳实践"
+    practices_description: "这是我们对您的承诺，即玩家，尽管这在法律用语中略显不足。"
+    privacy_title: "隐私"
+    privacy_description: "我们不会出售您的任何个人信息。我们计划最终通过招聘来盈利，但请您放心，未经您的明确同意，我们不会将您的个人信息出售有兴趣的公司。"
+    security_title: "安全"
+    security_description: "我们竭力保证您的个人信息安全性。作为一个开源项目，任何人都可以检讨并改善我们自由开放的网站的安全性。"
+    email_title: "电子邮件"
+    email_description_prefix: "我们不会发给您垃圾邮件。通过"
+    email_settings_url: "您的电子邮件设置"
+    email_description_suffix: "或者我们发送的邮件中的链接，您可以随时更改您的偏好设置或者随时取消订阅。"
+    cost_title: "花费"
+    cost_description: "目前来说，CodeCombat 是完全免费的！我们的主要目标之一也是保持目前这种方式，让尽可能多的人玩得更好，不论是否是生活中。如果天空变暗，我们可能会对某些内容采取订阅收费，但我们宁愿不那么做。运气好的话，我们可以维持公司，通过："
+    recruitment_title: "招募"
+    recruitment_description_prefix: "在 CodeCombat 这里，你将得以成为一名法力强大的“巫师”，不只是在游戏中，更在生活中。"
+    url_hire_programmers: "没有人能以足够快速度招聘程序员，"
+    recruitment_description_suffix: "所以一旦你的技能成熟并且得到你的同意，我们将战士你的最佳编码成就给上万名雇主，希望他们垂涎欲滴。而他们支付给我们一点点报酬，并且付给你工资，"
+    recruitment_description_italic: "“一大笔”"
+    recruitment_description_ending: "。而这网站也就能保持免费，皆大欢喜。计划就是这样。"
+    copyrights_title: "版权与许可"
+    contributor_title: "贡献者许可协议"
+    contributor_description_prefix: "在本网站或者我们的 GitHub 版本库的所有贡献都依照我们的"
+    cla_url: "贡献者许可协议（CLA）"
+    contributor_description_suffix: "，而这在您贡献之前就应该已经同意。"
+    code_title: "代码 - MIT"
+    code_description_prefix: "所有由 CodeCombat 拥有或者托管在 codecombat.com 的代码，在 GitHub 版本库或者 codecombat.com 数据库，以上许可协议都依照"
+    mit_license_url: "MIT 许可证"
 #    code_description_suffix: "This includes all code in Systems and Components that are made available by CodeCombat for the purpose of creating levels."
 #    art_title: "Art/Music - Creative Commons "
 #    art_description_prefix: "All common content is available under the"
 #    cc_license_url: "Creative Commons Attribution 4.0 International License"
 #    art_description_suffix: "Common content is anything made generally available by CodeCombat for the purpose of creating Levels. This includes:"
-#    art_music: "Music"
-#    art_sound: "Sound"
-#    art_artwork: "Artwork"
-#    art_sprites: "Sprites"
+    art_music: "音乐"
+    art_sound: "声效"
+    art_artwork: "艺术品"
+    art_sprites: "小妖精"
 #    art_other: "Any and all other non-code creative works that are made available when creating Levels."
 #    art_access: "Currently there is no universal, easy system for fetching these assets. In general, fetch them from the URLs as used by the site, contact us for assistance, or help us in extending the site to make these assets more easily accessible."
 #    art_paragraph_1: "For attribution, please name and link to codecombat.com near where the source is used or where appropriate for the medium. For example:"
 #    use_list_1: "If used in a movie or another game, include codecombat.com in the credits."
 #    use_list_2: "If used on a website, include a link near the usage, for example underneath an image, or in a general attributions page where you might also mention other Creative Commons works and open source software being used on the site. Something that's already clearly referencing CodeCombat, such as a blog post mentioning CodeCombat, does not need some separate attribution."
 #    art_paragraph_2: "If the content being used is created not by CodeCombat but instead by a user of codecombat.com, attribute them instead, and follow attribution directions provided in that resource's description if there are any."
-#    rights_title: "Rights Reserved"
-#    rights_desc: "All rights are reserved for Levels themselves. This includes"
-#    rights_scripts: "Scripts"
-#    rights_unit: "Unit configuration"
-#    rights_description: "Description"
-#    rights_writings: "Writings"
+    rights_title: "版权所有"
+    rights_desc: "所有关卡由他们自己版权所有。这包括"
+    rights_scripts: "脚本"
+    rights_unit: "单元配置"
+    rights_description: "描述"
+    rights_writings: "作品"
 #    rights_media: "Media (sounds, music) and any other creative content made specifically for that Level and not made generally available when creating Levels."
 #    rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not."
 #    nutshell_title: "In a Nutshell"
@@ -408,16 +408,16 @@
 #    scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!"
 #    more_about_scribe: "Learn More About Becoming a Scribe"
 #    scribe_subscribe_desc: "Get emails about article writing announcements."
-    diplomat_summary: "在其他国家不讲英语，很多人对于CodeCombat有很大的兴趣。我们正在寻找译者,愿意花时间翻译网站的语料库的词语,这样CodeCombat就能尽快访问到世界各地。如果你想帮助CodeCombat国际化,那么这个类就是给你的。"
+    diplomat_summary: "在其他国家不讲英语，很多人对于CodeCombat有很大的兴趣。我们正在寻找愿意花时间翻译网站语料库的词语的译者，这样 CodeCombat 就能尽快地遍及世界各地。如果你想帮助 CodeCombat 的国际化，那么这个类就是给你的。"
     diplomat_introduction_pref: "如果有一件事情是从 "
     diplomat_launch_url: "launch in October"
     diplomat_introduction_suf: "学来的，Combat有相当大的兴趣在其他国家发展。我们正在构建一个译者兵团把单词一个一个的翻译，让CodeCombat尽可能地访问到世界各地。如果你喜欢偷偷地瞄一眼即将到来的内容,并让你的国民尽快学习到CodeCombat,那么这个类可能适合你。"
     diplomat_attribute_1: "会流利的英语和能翻译的语言。当传递复杂思想，难得的是能很好的同时掌握这两个。"
     diplomat_join_pref_github: "找到你自己的语言文件 "
     diplomat_github_url: "在GitHub网站"
-    diplomat_join_suf_github: "在线编辑它，然后提交一个合并请求。同时，检查下面这个盒子来关注最新的国际化开发！"
-    more_about_diplomat: "学习更多关于“成为一名外交官（翻译者）”"
-    diplomat_subscribe_desc: "接受邮件，关于国际化开发和翻译情况"
+    diplomat_join_suf_github: "在线编辑它，然后提交一个合并请求。同时，选中下面这个复选框来关注最新的国际化开发！"
+    more_about_diplomat: "了解更多“如何成为一名外交官（翻译者）”"
+    diplomat_subscribe_desc: "接受有关国际化开发和翻译情况的邮件"
 #    ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you."
 #    ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you."
 #    ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!"
@@ -433,26 +433,26 @@
 #    counselor_attribute_2: "A little bit of free time!"
 #    counselor_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll put you in our contact list and be in touch when we could use advice (not too often)."
 #    more_about_counselor: "Learn More About Becoming a Counselor"
-#    changes_auto_save: "Changes are saved automatically when you toggle checkboxes."
-#    diligent_scribes: "Our Diligent Scribes:"
-#    powerful_archmages: "Our Powerful Archmages:"
-#    creative_artisans: "Our Creative Artisans:"
-#    brave_adventurers: "Our Brave Adventurers:"
-#    translating_diplomats: "Our Translating Diplomats:"
-#    helpful_ambassadors: "Our Helpful Ambassadors:"
-
-#  classes:
-#    archmage_title: "Archmage"
-#    archmage_title_description: "(Coder)"
-#    artisan_title: "Artisan"
-#    artisan_title_description: "(Level Builder)"
-#    adventurer_title: "Adventurer"
-#    adventurer_title_description: "(Level Playtester)"
-#    scribe_title: "Scribe"
-#    scribe_title_description: "(Article Editor)"
-#    diplomat_title: "Diplomat"
-#    diplomat_title_description: "(Translator)"
-#    ambassador_title: "Ambassador"
-#    ambassador_title_description: "(Support)"
-#    counselor_title: "Counselor"
-#    counselor_title_description: "(Expert/Teacher)"
+    changes_auto_save: "在您切换复选框时，更改将自动保存。"
+    diligent_scribes: "我们勤奋的文书:"
+    powerful_archmages: "我们强力的大法师:"
+    creative_artisans: "我们极具创意的工匠:"
+    brave_adventurers: "我们勇敢的冒险家:"
+    translating_diplomats: "我们遍及世界的外交官:"
+    helpful_ambassadors: "我们亲切的使节:"
+
+  classes:
+    archmage_title: "大法师"
+    archmage_title_description: "(代码编写人员)"
+    artisan_title: "工匠"
+    artisan_title_description: "(关卡建立人员)"
+    adventurer_title: "冒险家"
+    adventurer_title_description: "(关卡测试人员)"
+    scribe_title: "文书"
+    scribe_title_description: "(提示编辑人员)"
+    diplomat_title: "外交官"
+    diplomat_title_description: "(翻译人员)"
+    ambassador_title: "使节"
+    ambassador_title_description: "(用户支持人员)"
+    counselor_title: "顾问"
+    counselor_title_description: "(专家/导师)"


http method for loading a single widget

http method for loading a single widget

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/leonidas/qa-dashboard.git/'


opt: less memory consumption

opt: less memory consumption

--- src/yaku.coffee
+++ src/yaku.coffee
@@ -328,16 +328,8 @@
 		flush = ->
 			i = 0
 			while i < fnQueueLen
-				pIndex = i++
-				vIndex = i++
-
-				p = fnQueue[pIndex]
-				v = fnQueue[vIndex]
-
-				release fnQueue, pIndex
-				release fnQueue, vIndex
-
-				fn p, v
+				fn fnQueue[i]
+				release fnQueue, i++

 			fnQueueLen = 0
 			fnQueue.length = initQueueSize
@@ -352,13 +344,12 @@
 		Yaku.nextTick = try
 			root.process.nextTick
 		catch
-			setTimeout
-
-		(p, v) ->
-			fnQueue[fnQueueLen++] = p
+			setTimeout.bind root
+
+		(v) ->
 			fnQueue[fnQueueLen++] = v

-			Yaku.nextTick flush if fnQueueLen == 2
+			Yaku.nextTick flush if fnQueueLen == 1

 			return

@@ -464,8 +455,8 @@
 		p1[p1._pCount++] = p2

 		# 2.2.6
-		if p1._state != $pending
-			scheduleHandler p1, p2
+		if p1._state != $pending and p1._pCount > 0
+			scheduleHandler p1

 		# 2.2.7
 		p2
@@ -476,23 +467,33 @@
 	 * @param {Yaku} p1
 	 * @param {Yaku} p2
 	###
-	scheduleHandler = genScheduler 999, (p1, p2) ->
-		handler = if p1._state then p2._onFulfilled else p2._onRejected
-
-		# 2.2.7.3
-		# 2.2.7.4
-		if handler == $nil
-			settlePromise p2, p1._state, p1._value
-			return
-
-		# 2.2.7.1
-		x = genTryCatcher(callHanler) handler, p1._value
-		if x == $tryErr
-			# 2.2.7.2
-			settlePromise p2, $rejected, x.e
-			return
-
-		settleWithX p2, x
+	scheduleHandler = genScheduler 999, (p1) ->
+		i = 0
+		len = p1._pCount
+
+		while i < len
+			p2 = p1[i++]
+
+			continue if p2._state != $pending
+
+			# 2.2.2
+			# 2.2.3
+			handler = if p1._state then p2._onFulfilled else p2._onRejected
+
+			# 2.2.7.3
+			# 2.2.7.4
+			if handler == $nil
+				settlePromise p2, p1._state, p1._value
+				continue
+
+			# 2.2.7.1
+			x = genTryCatcher(callHanler) handler, p1._value
+			if x == $tryErr
+				# 2.2.7.2
+				settlePromise p2, $rejected, x.e
+				continue
+
+			settleWithX p2, x

 		return

@@ -588,14 +589,8 @@
 		if not isLongStackTrace
 			p._pre = $nil

-		i = 0
-		len = p._pCount
-
-		# 2.2.2
-		# 2.2.3
-		while i < len
-			# 2.2.4
-			scheduleHandler p, p[i++]
+		# 2.2.4
+		scheduleHandler p if p._pCount > 0

 		p


activity: improve ensureMessage action

activity: improve ensureMessage action

--- client/activity/lib/flux/actions/message.coffee
+++ client/activity/lib/flux/actions/message.coffee
@@ -98,20 +98,38 @@
     loadComments message.id
     fetchingMap[messageId] = no

+    return { message }
+

 ensureMessage = (messageId) ->

-  message = kd.singletons.reactor.evaluate ['MessagesStore', messageId]
-
-  if message
-    return new Promise (resolve) -> resolve { message: message.toJS() }
-
-  new Promise (resolve) ->
-    loadMessage(messageId).then ({ message }) ->
+  { reactor } = kd.singletons
+
+  loadMessage(messageId)
+    .then ({ message }) ->
       channelId = message.initialChannelId
-      putLoaderMarker channelId, messageId, { position: 'before', autoload: yes }
-      putLoaderMarker channelId, messageId, { position: 'after', autoload: yes }
-      resolve { message }
+      loadMessages(channelId, { from: message.createdAt }).then ({ messages }) ->
+        [first, ...] = messages
+        return  unless first
+
+        # check to see if loaded if not put a indicator before that.
+        firstMessage = reactor.evaluate ['MessagesStore', first.id]
+        unless firstMessage
+          putLoaderMarker channelId, first.id, { position: 'before', autoload: no }
+
+        return { message }
+      .then ({ message }) ->
+        channelId = message.initialChannelId
+        loadMessages(channelId, { to: message.createdAt, sort: 'DESC' }).then ({ messages }) ->
+          [..., last] = messages
+
+          return  unless last
+
+          lastMessage = reactor.evaluate ['MessagesStore', last.id]
+          unless lastMessage
+            putLoaderMarker channelId, last.id, { position: 'after', autoload: no }
+
+          return { message }

 putLoaderMarker = (channelId, messageId, options) ->


Fix label height changing when image loads

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/nathan/lemma.git/'


update validator

update validator

error: pathspec 'validate.coffee' did not match any file(s) known to git

--- validate.coffee
+++ validate.coffee
@@ -0,0 +1,65 @@
+module.exports = {
+  type: "object"
+  required: ['host','resources','replyschema']
+  properties:
+    host:
+      type: 'string'
+    resources:
+      type: 'object'
+      properties:
+        "^[0-9a-z:\/_\*\?]+$":
+          type: 'object'
+          properties:
+            "/(get|post|del|put)/":
+              type: 'object'
+              required: ['description','function']
+              properties:
+                description:
+                  type: 'string'
+                function:
+                  type: 'string'
+                payload:
+                  type: 'object'
+                  properties:
+                    "/[a-z_]+/":
+                      type: 'object'
+                      required: ['default','type']
+                      properties:
+                        default:
+                          type: 'string'
+                        type:
+                          type: 'string'
+                          enum: ['string','integer','number','boolean','float','array','object']
+
+    replyschema:
+      type: 'object'
+      required: ['type','required','messages','payload']
+      properties:
+        type:
+          type: 'string'
+        required:
+          type: 'array'
+          items: [{ type: 'string' }]
+        messages:
+          type: 'object'
+          required: ['0','1','2','3','4']
+          properties:
+            0: { type: 'string' }
+            1: { type: 'string' }
+            2: { type: 'string' }
+            3: { type: 'string' }
+            4: { type: 'string' }
+        payload:
+          type: 'object'
+          required: ['code','message','kind','data','errors']
+          properties:
+            code: { type: 'object' }
+            message: { type: 'object' }
+            kind: { type: 'object' }
+            data: { type: 'object' }
+            errors: { type: 'object' }
+
+
+
+
+}


fix migration of secondary colors

fix migration of secondary colors

error: pathspec 'server/startup/migrations/v069.coffee' did not match any file(s) known to git

--- server/startup/migrations/v069.coffee
+++ server/startup/migrations/v069.coffee
@@ -0,0 +1,12 @@
+RocketChat.Migrations.add
+	version: 69
+	up: ->
+		RocketChat.models.Settings.update { "_id": "theme-color-custom-scrollbar-color", "value": "rgba(255, 255, 255, 0.05)" }, { $set: { "editor": "expression", "value": "@transparent-darker" } }
+		RocketChat.models.Settings.update { "_id": "theme-color-info-font-color", "value": "#aaaaaa" }, { $set: { "editor": "expression", "value": "@secondary-font-color" } }
+		RocketChat.models.Settings.update { "_id": "theme-color-link-font-color", "value": "#008ce3" }, { $set: { "editor": "expression", "value": "@primary-action-color" } }
+		RocketChat.models.Settings.update { "_id": "theme-color-status-away", "value": "#fcb316" }, { $set: { "editor": "expression", "value": "@pending-color" } }
+		RocketChat.models.Settings.update { "_id": "theme-color-status-busy", "value": "#d30230" }, { $set: { "editor": "expression", "value": "@error-color" } }
+		RocketChat.models.Settings.update { "_id": "theme-color-status-offline", "value": "rgba(150, 150, 150, 0.50)" }, { $set: { "editor": "expression", "value": "@transparent-darker" } }
+		RocketChat.models.Settings.update { "_id": "theme-color-status-online", "value": "#35ac19" }, { $set: { "editor": "expression", "value": "@success-color" } }
+		RocketChat.models.Settings.update { "_id": "theme-color-tertiary-background-color", "value": "#eaeaea" }, { $set: { "editor": "expression", "value": "@component-color" } }
+		RocketChat.models.Settings.update { "_id": "theme-color-tertiary-font-color", "value": "rgba(255, 255, 255, 0.6)" }, { $set: { "editor": "expression", "value": "@transparent-lightest" } }


fixed a lot of stuff in the infinite scrolling mixin and did some code cleanup too

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/wenchenglai/firehose-ember-shared.git/'


Renamed variables on hex plugin

Renamed variables on hex plugin

--- app/assets/javascripts/backbone/plugins/hex_color_embed.js.coffee
+++ app/assets/javascripts/backbone/plugins/hex_color_embed.js.coffee
@@ -3,14 +3,14 @@
     regex: /#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/

     template: _.template '''
-      <span class="color-preview" style="background-color:<%= rgb %>;"/>
+      <span class="color-preview" style="background-color:<%= hex %>;"/>
     '''

   @init: ()->
     Kandan.Modifiers.register @options.regex, (message, state) =>
-      rgb = message.content.match(@options.regex)[0]
-      replacement = @options.template({rgb: rgb}) + rgb
+      hex = message.content.match(@options.regex)[0]
+      replacement = @options.template({hex: hex}) + hex

-      message.content = message.content.replace(rgb, replacement)
+      message.content = message.content.replace(hex, replacement)

       return Kandan.Helpers.Activities.buildFromMessageTemplate(message)


Update documentation.

Update documentation.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/appneta/hubot-jenkins-deploy.git/'


added note about correct barcode plugin tag

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/AppGyver/kitchensink.git/'


Fix bad scrolling math

Fix bad scrolling math

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/grantovich/CSH-WebNews.git/'


Fix (?) issue with browser icon.

Fix (?) issue with browser icon.

And, following on from 7a746ba51a5124a39acb09270bf4ab470bfd6b5f:

   - 7a746ba51a5124a39acb09270bf4ab470bfd6b5f seems to work.  However,
     there's a slight unnecessary flicker when changing tab.  So, this
     tries *not* setting an initial icon, and waiting instead to hear
     back from the tab.

--- background_scripts/main.coffee
+++ background_scripts/main.coffee
@@ -366,7 +366,6 @@
   disabledIcon = "icons/browser_action_disabled.png"
   partialIcon = "icons/browser_action_partial.png"
   chrome.tabs.get tabId, (tab) ->
-    setBrowserActionIcon tabId, enabledIcon
     setBadge badge: ""
     chrome.tabs.sendMessage tabId, { name: "getActiveState" }, (response) ->
       if response


Fix close tags in coffee-react files

Fix close tags in coffee-react files

/bin/sh: 1: Syntax error: "(" unexpected


increased session expiration time

increased session expiration time


get cursor reposition working more appropriately for backspace and delete, still refactoring

--- app/assets/javascripts/angular/directives/smartNumber.js.coffee
+++ app/assets/javascripts/angular/directives/smartNumber.js.coffee
@@ -78,22 +78,22 @@
       event.preventDefault()
       event.stopPropagation()

-    findNewCharacterCursorPosition = (elem) ->
+    findNewCharacterCursorPosition = (elem, initialPosition) ->
       if elem.val().length == 4 || elem.val().length == 8 # a new comma is added
-        elem[0].selectionStart + 1
+        initialPosition + 1
       else if elem[0].selectionStart < elem.val().length # cursor is adding numbers, but is not at the end of the input field
-        elem[0].selectionStart
+        initialPosition
       else # cursor is adding numbers at the end of the input field
-        elem[0].selectionStart + 1
-
-    findNewDeleteCursorPosition = (elem) ->
-      elem[0].selectionStart
-
-    findNewBackspaceCursorPosition = (elem) ->
+        initialPosition + 1
+
+    findNewDeleteCursorPosition = (elem, initialPosition) ->
+      initialPosition
+
+    findNewBackspaceCursorPosition = (elem, initialPosition) ->
       if elem.val().length == 3 || elem.val().length == 7 # a comma has been removed
-        return elem[0].selectionStart - 1
+        return initialPosition - 1
       else # cursor is adding numbers at the end of the input field
-        return elem[0].selectionStart
+        return initialPosition

     backspaceAtComma = (elem, event) ->
       cursorPos = elem[0].selectionStart
@@ -242,20 +242,23 @@
               return if invalidInput(elem, event)
               # unless isControlKey(keycode)
                 # return if maxDigitsReached(event, 9)
+
+              initialPosition = elem[0].selectionStart

               if keycode >= 48 and keycode <= 57 # insert number character
                 insertCharacter(elem, event)
-                newCursorPosition = findNewCharacterCursorPosition(elem)
+                elem.val resetCommas(elem.val()) # reformat the number in place
+                newCursorPosition = findNewCharacterCursorPosition(elem, initialPosition)

               if keycode == 8 # backspace is pressed
                 backspacePressed(elem, event) # handle logic for backspace
                 elem.val resetCommas(elem.val()) # reformat the number in place
-                newCursorPosition = findNewBackspaceCursorPosition(elem)
+                newCursorPosition = findNewBackspaceCursorPosition(elem, initialPosition)

               if keycode == 46 # backspace is pressed
                 deletePressed(elem, event) # handle logic for backspace
                 elem.val resetCommas(elem.val()) # reformat the number in place
-                newCursorPosition = findNewDeleteCursorPosition(elem)
+                newCursorPosition = findNewDeleteCursorPosition(elem, initialPosition)

               elem[0].setSelectionRange(newCursorPosition, newCursorPosition) # set the new cursor position


Reverted adding classes to anchor

Reverted adding classes to anchor

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/lonelyplanet/rizzo.git/'


Fixed snapshot recording layout issue

Fixed snapshot recording layout issue

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/evercam/evercam-dashboard.git/'


Complete the progress bar on complete the first/last image loading.


first draft of brain class

first draft of brain class

error: pathspec 'lib/geekywalletlib.coffee' did not match any file(s) known to git

--- lib/geekywalletlib.coffee
+++ lib/geekywalletlib.coffee
@@ -0,0 +1,22 @@
+class Brain
+
+  computeFromParsed: (parsed) =>
+    @flatListOfLines = @getFlatListOfLines(parsed.group)
+    @computed = @flatListOfLines.map (line) => @computeLine(line)
+    @computed
+
+  # flatten context into each line
+  # TODO take care of abbreviations
+  getFlatListOfLines: (group) =>
+    _.map group.lines, (line) ->
+      if line.context? # line is actually a group...
+        console.log "TODO manage nested context"
+      else
+        line.context = {}
+        line.context.people = group.context.people
+      line
+
+  # compute balance, spent and given for each line
+  computeLine: (line) =>
+    line
+


fixed no search matches message

fixed no search matches message

--- src/coffeescripts/Search.coffee
+++ src/coffeescripts/Search.coffee
@@ -89,7 +89,7 @@
           else
             # Escape searchTerm to prevent XSS
             searchTerm = LoggrUtil.htmlEncode searchTerm
-            displayError L.get("no_search_matches")
+            displayError L.get("no_search_matches", searchTerm)

   # _private_ function to render the search result


Add tab-moving key bindings for mac

Add tab-moving key bindings for mac

--- keymaps/darwin.cson
+++ keymaps/darwin.cson
@@ -29,6 +29,8 @@
   'ctrl-alt-cmd-l': 'window:reload'
   'alt-cmd-i': 'window:toggle-dev-tools'
   'cmd-alt-ctrl-p': 'window:run-package-specs'
+  'ctrl-shift-left': 'pane:move-item-left'
+  'ctrl-shift-right': 'pane:move-item-right'

   # Sublime Parity
   'cmd-,': 'application:show-settings'


Colocate all preview tab tweaks

Colocate all preview tab tweaks

--- lib/tab-view.coffee
+++ lib/tab-view.coffee
@@ -6,9 +6,6 @@
 class TabView extends HTMLElement
   initialize: (@item) ->
     @isPreviewTab = atom.config.get('tabs.usePreviewTabs') and @item instanceof TextEditor
-
-    if @isPreviewTab
-      @addEventListener 'dblclick', => @clearPreview()

     @classList.add('tab', 'sortable')

@@ -30,6 +27,7 @@
     if @isPreviewTab
       @itemTitle.classList.add('temp')
       @classList.add('preview-tab')
+      @addEventListener 'dblclick', => @clearPreview()

   handleEvents: ->
     titleChangedHandler = =>


debug bucket corrected timestamp in activity feed

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/andrewjcasal/koding.git/'


chore (Auth) Provide meaningful error message for getCurrentUser when missing session

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/AppGyver/supersonic.git/'


add lastline url inspection api

add lastline url inspection api

error: pathspec 'src/scripts/lastline_url.coffee' did not match any file(s) known to git

--- src/scripts/lastline_url.coffee
+++ src/scripts/lastline_url.coffee
@@ -0,0 +1,33 @@
+# Description:
+#   Messing around with the Lastline API.
+#
+# Commands:
+#   hubot lastline url <url including http://> - searches or submits a URL to lastline for analysis.
+lastline_key = "#{process.env.HUBOT_LASTLINE_KEY}"
+lastline_api_token = "#{process.env.HUBOT_LASTLINE_TOKEN}"
+lastline_user_domain = "#{process.env.HUBOT_LASTLINE_USER_DOMAIN}"
+lastline_analysis_domain = "#{process.env.HUBOT_LASTLINE_ANALYSIS_DOMAIN}"
+
+sleep = (ms) ->
+   start = new Date().getTime()
+   continue while new Date().getTime() - start < ms
+
+module.exports = (robot) ->
+  robot.respond /(lastline) (url) ([^ ]*)?( referer [^ ]*)?( ua [^ ]*)?/i, (msg) ->
+    url = msg.match[3]
+    referer = if msg.match[4] then msg.match[4].split(" ")[2] else ''
+    user_agent = if msg.match[5] then msg.match[5].split(" ")[2] else ''
+    stringData = "key=#{encodeURIComponent lastline_key}&api_token=#{encodeURIComponent lastline_api_token}&url=#{encodeURIComponent url}#{if msg.match[4] then msg.match[4].split(' ')[1]+'=' else ''}#{if msg.match[4] then msg.match[4].split(' ')[2] else ''}#{if msg.match[5] then msg.match[5].split(' ')[1]+'=' else ''}#{if msg.match[5] then msg.match[5].split(' ')[2] else ''}"
+    robot.http("https://analysis.lastline.com/analysis/submit/url")
+      .headers('Content-Type': 'application/x-www-form-urlencoded')
+      .post(stringData) (err, res, body) ->
+        results = JSON.parse(body)
+        if (results.data.score == undefined) then msg.send "So, Lastline doesn't know about that URL yet. I'll be back to you in about 2 minutes."
+        if (results.data.score == undefined) then sleep 120000
+        robot.http("https://analysis.lastline.com/analysis/submit/url")
+          .headers('Content-Type': 'application/x-www-form-urlencoded')
+          .post(stringData) (err, res, body) ->
+            final_result = JSON.parse(body)
+            if (results.data.score == undefined) then msg.send "Are you sure that domain resolves? Also, we don't support file submissions via url; we're looking for browser exploits." else if final_result.data.score < 30 then msg.send "No worries, bro, #{url} doesn't seem to have any browser exploits." else msg.send "Man, that #{url} is badness. We found #{final_result.data.malicious_activity} in there."
+            msg.send "Check out https://#{lastline_user_domain}/malscape/#/task/#{results.data.task_uuid} for details"
+            return


implemented save queue

implemented save queue


Use proper arg name in path.exists.

Use proper arg name in path.exists.

--- src/brunch.coffee
+++ src/brunch.coffee
@@ -160,8 +160,8 @@
         return helpers.logError error if error?
         helpers.log "Generated #{testFilePath}"
         callback()
-    path.exists testDirPath, (error) ->
-      return write() unless error?
+    path.exists testDirPath, (exists) ->
+      return write() if exists
       mkdirp testDirPath, 0755, (error) ->
         return helpers.logError if error?
         write()


refactor user services out of product services

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/vtanathip/vCommerce.git/'


Readable test

Readable test

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/michalsanger/globe-geometry.git/'


add file

add file

error: pathspec 'AppStoreClient.coffee' did not match any file(s) known to git

--- AppStoreClient.coffee
+++ AppStoreClient.coffee
@@ -0,0 +1,128 @@
+###*
+get itunes data
+###
+String::replaceAll = (org, dest) ->
+  @split(org).join dest
+
+module.exports = class AppStoreClient
+
+  API = "http://itunes.apple.com/search?"
+  ANDROID_URL = "http://play.google.com/store/search?q=tsuyoshi+hyuga"
+  URL=API#"http://coecorsproxy.appspot.com/"
+
+  getParameter=(str) ->
+    dec = decodeURIComponent
+    par = {} #new Array()
+    itm = undefined
+    return par  if typeof (str) is "undefined"
+    str = str.split("?")[1]  if str.indexOf("?", 0) > -1
+    str = str.split("&")
+    i = 0
+
+    while str.length > i
+      itm = str[i].split("=")
+      #Ti.App.Const.warn? "itm0 = #{itm[0]}"
+      #Ti.App.Const.warn? "itm1 = #{itm[1]}"
+      key = (if typeof (itm[1]) is "undefined" then true else dec(itm[1]))  unless itm[0] is ""
+      #Ti.App.Const.warn? "key = #{key}"
+      par[itm[0]] = key
+      #Ti.App.Const.warn? par
+      i++
+    par
+
+  setParameter=(par) ->
+    enc = encodeURIComponent
+    str = ""
+    amp = ""
+    return ""  unless par
+    for i of par
+      str = str + amp + i + "=" + enc(par[i])
+      amp = "&"
+    str
+
+  constructor: ->
+    # body...}
+
+  COUNTRY = [
+    "GB"
+    "IT"
+    "FI"
+    "RU"
+    "AE"
+    "PK"
+    "BD"
+    "ID"
+    "PH"
+    "JP"
+    "AU"
+    "NC"
+    "NZ"
+    "TO"
+    "TO"
+    "US"
+    "US"
+    "US"
+    "US"
+    "US"
+    "CL"
+    "BR"
+    "BR"
+    "GL"
+  ]
+  ###*
+  get timezone
+  ###
+  @getTzOff : ->
+    if Ti.Locale.getCurrentCountry() isnt "" then return Ti.Locale.getCurrentCountry()
+    date = new Date()
+    COUNTRY[(date.getHours() - date.getUTCHours() + 24) % 24]
+
+  ###*
+  get data
+  @param {function} callback
+  @param {function} errorcallback
+  @param {object} callback
+  ###
+  @getItunesData:(callback,errorcallback,obj)->
+      unless ENV_PRODUCTION then Ti.API.debug "obj=#{obj}"
+
+      cur = require("AppStoreClient").getTzOff()
+      IOS_URL = unless obj? then "http://itunes.apple.com/search?term=tsuyoshi+hyuga&country=#{cur}&media=software&entity=software"
+      else
+        "http://itunes.apple.com/search?"+setParameter obj
+      url = IOS_URL#"https://itunes.apple.com/search?term=tsuyoshi+hyuga&country=#{Ti.Locale.getCurrentCountry()}&media=software&entity=software"
+      unless ENV_PRODUCTION then Ti.API.debug "url=#{url}"
+      client = Ti.Network.createHTTPClient(
+
+        # function called when the response data is available
+        onload: (e)->#getItunesData #(e) ->
+          unless ENV_PRODUCTION then Ti.API.debug "getItunesData"
+          json = JSON.parse @responseText
+          data = json.results
+
+          #TODO データのうち、無料のものを抽出 underscoreで
+          data = _.filter data,(obj)->
+            obj.price is 0
+          #TODO データを、アップデート日付順にソート underscoreで
+          data = _.sortBy(data, (item) ->
+            Number(item.releaseDate)
+          )
+          #TODO 自分のアプリIDは除外 bundleId
+          data = _.filter data,(obj)->
+            obj.bundleId isnt Ti.App.id
+          callback data
+
+        onerror: errorcallback
+        onreadystatechange :(e)->
+          unless ENV_PRODUCTION then Ti.API.debug "onreadystatechange "
+
+        onsendstream:(e)->
+          unless ENV_PRODUCTION then Ti.API.debug "onsendstream "
+        ondatastream:(e)->
+          unless ENV_PRODUCTION then Ti.API.debug "ondatastream"
+        timeout: 5000 # in milliseconds
+      )
+
+      client.open "GET", url
+
+      client.send()


turned off compression [deploy: production]

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/domjtalbot/Portfolio.git/'


JCredential: fix coffeelint issues

JCredential: fix coffeelint issues

--- workers/social/lib/social/models/computeproviders/credential.coffee
+++ workers/social/lib/social/models/computeproviders/credential.coffee
@@ -336,7 +336,7 @@
       { permission   : 'update credential', validateWith: Validators.own }
       {
         permission   : 'update credential'
-        validateWith : Validators.group.resource mode: 'w'
+        validateWith : Validators.group.resource { mode: 'w' }
       }
       { permission   : 'modify credential', superadmin: yes }
     ]
@@ -377,7 +377,7 @@
       { permission   : 'update credential', validateWith: Validators.own }
       {
         permission   : 'update credential'
-        validateWith : Validators.group.resource mode: 'w'
+        validateWith : Validators.group.resource { mode: 'w' }
       }
       { permission   : 'modify credential', superadmin: yes }
     ]
@@ -449,7 +449,7 @@
       { permission   : 'update credential', validateWith: Validators.own }
       {
         permission   : 'update credential'
-        validateWith : Validators.group.resource mode: 'w'
+        validateWith : Validators.group.resource { mode: 'w' }
       }
       { permission   : 'modify credential', superadmin: yes }
     ]


atom setting

atom setting

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/blivesta/dotfiles.git/'


Better keybinds

Better keybinds

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/demhydraz/til.git/'


Unit test stub

Unit test stub

error: pathspec 'test/tests.coffee' did not match any file(s) known to git

--- test/tests.coffee
+++ test/tests.coffee
@@ -0,0 +1,3 @@
+chai = require 'chai'
+
+describe 'checkt', ->


Make code inline instead of block.

Make code inline instead of block.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/bmedx/modulestore.git/'


Workaround for sandbox.

Workaround for sandbox.


And indicator show page integration tests

error: pathspec 'server/test/integration/indicator_show.coffee' did not match any file(s) known to git

--- server/test/integration/indicator_show.coffee
+++ server/test/integration/indicator_show.coffee
@@ -0,0 +1,24 @@
+assert = require('chai').assert
+helpers = require '../helpers'
+request = require('request')
+url = require('url')
+_ = require('underscore')
+
+suite('Indicator index')
+
+test("When given a valid indicator, I should get a 200 and see the title", (done)->
+  Indicator = require('../../models/indicator')
+
+  indicatorTitle = "Dat test indicator"
+  Indicator.create(
+    title: indicatorTitle
+  ).success((indicator)->
+    request.get {
+      url: helpers.appurl("/indicators/#{indicator.id}")
+    }, (err, res, body) ->
+      assert.equal res.statusCode, 200
+
+      assert.match body, new RegExp(".*#{indicatorTitle}.*")
+      done()
+  )
+)


Class for converting notes into frequencies.

error: pathspec 'src/scripts/FrequencyMap.coffee' did not match any file(s) known to git

--- src/scripts/FrequencyMap.coffee
+++ src/scripts/FrequencyMap.coffee
@@ -0,0 +1,28 @@
+#
+# Syntho - A Web based Synthesizer
+# by Michael Marner <michael@20papercups.net>
+#
+
+
+
+class @FrequencyMap
+
+    constructor: ->
+        @map = [ [523.251,
+                    554.365,
+                     587.33,
+                     622.254,
+                     659.255,
+                     698.456,
+                     739.989,
+                     783.991,
+                     830.609,
+                     880,
+                     932.328
+                     987.767,
+                     1046.5] ]
+
+        @octave = 0;
+
+    getFrequency: (note) ->
+        return @map[@octave][note] if note >=0 && note <= 12


Find selected transaction from checkbox model

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/xaun/transac.git/'


 Merge branch 'master.account.suggestion' into 'master'

account: remove backslash character in seed parameter of `JAccount::findSuggestions`


Sidebar: Omit owned/shared machines in collaboration section

--- workers/social/lib/social/models/sidebar.coffee
+++ workers/social/lib/social/models/sidebar.coffee
@@ -128,7 +128,11 @@

           ->

-            return filterQueue.fin()  unless workspace.channelId
+            isOwner  = isMachineOwner  user, machine
+            isShared = isMachineShared user, machine
+            skip     = workspace.channelId and (isOwner or isShared)
+
+            return filterQueue.fin()  if skip

             successFn = makeSuccessFn addCollaborationFn


Sidebar: Initial remote API implementation

error: pathspec 'workers/social/lib/social/models/sidebar.coffee' did not match any file(s) known to git

--- workers/social/lib/social/models/sidebar.coffee
+++ workers/social/lib/social/models/sidebar.coffee
@@ -0,0 +1,119 @@
+bongo = require 'bongo'
+{dash, secure, signature} = bongo
+
+JMachine   = require './computeproviders/machine'
+JWorkspace = require './workspace'
+
+SocialMessage = require './socialapi/message'
+
+KodingError = require '../error'
+
+module.exports = class Sidebar extends bongo.Base
+
+  @share()
+
+  @set
+    sharedMethods:
+      static:
+        fetchEnvironment: (signature Function)
+
+
+  @fetchEnvironment$ = secure (client, callback) ->
+
+    data = own: []
+
+    client.connection.delegate.fetchUser (err, user) ->
+
+      return callback new KodingError err  if err
+
+      query = 'users.id': user.getId()
+
+      JMachine.some query, {}, (err, machines) ->
+        return callback new KodingError err  if err
+
+        machineUIds = machines.map (machine) -> machine.uid
+        JWorkspace.some machineUId: $in: machineUIds, {}, (err, workspaces) ->
+
+          return callback new KodingError err  if err
+
+          options = {client, user, machines, workspaces, callback}
+          options.addOwnFn = makeWorkspaceAdderFn data.own
+          options.addSharedFn = makeWorkspaceAdderFn data.shared
+          options.addCollaborationFn = makeWorkspaceAdderFn data.collaboration
+          options.callback = -> callback null, data
+
+          decorateEnvironmentData options
+
+
+  makeWorkspaceAdderFn = (list) ->
+
+    findNode = (machine) ->
+
+      for node in list
+        if machine.uid is node.machine.uid
+          return node
+
+    return ({machine, workspace}) ->
+
+      if node = findNode machine
+        node.workspaces.push workspace
+      else
+        workspaces = [workspace]
+        node = {machine, workspaces}
+        list.push node
+
+
+  decorateEnvironmentData = (options) ->
+
+    {client, user} = options
+    {machines, workspaces} = options
+    {addOwnFn, addSharedFn, addCollaborationFn} = options
+    {callback} = options
+
+    machineMap = {}
+    machines.forEach (machine) -> machineMap[machine.uid] = machine
+
+    workspaceQueue = workspaces.map (workspace) ->
+
+      machine = machineMap[workspace.machineUId]
+
+      nodeValue = {machine, workspace}
+
+      return ->
+
+        failureFn = (err) ->
+
+          filterQueue.fin()
+
+          console.error \
+            new KodingError "Sidebar decorate environment data: #{err}"
+
+        makeSuccessFn = (fn) ->
+
+          return ->
+
+            fn.call null, nodeValue
+            filterQueue.fin()
+
+        filterQueue = [
+          ->
+
+            successFn = makeSuccessFn addOwnFn
+
+            options = {user, machine, successFn, failureFn}
+            filterOwnWorkspace options
+        ]
+
+        dash filterQueue, -> workspaceQueue.fin()
+
+    dash workspaceQueue, callback
+
+
+  filterOwnWorkspace = (options) ->
+
+    {user, machine, successFn, failureFn} = options
+
+    for u in machine.users when u.owner and u.id.equals user.getId()
+      return successFn()
+
+    failureFn()


Workspace: Added Workspace class.

Workspace: Added Workspace class.


Test cases for constructor

Test cases for constructor

error: pathspec 'test/spec/construct.spec.coffee' did not match any file(s) known to git

--- test/spec/construct.spec.coffee
+++ test/spec/construct.spec.coffee
@@ -0,0 +1,12 @@
+describe 'Library constructor', ->
+    overloadableFunc = null;
+
+    beforeEach ->
+        overloadableFunc = new Overloadable();
+
+    it 'Should construct a function', ->
+        expect(typeof overloadableFunc).toBe 'function'
+
+    it 'Should have overload property, also a function', ->
+        expect(typeof overloadableFunc.overload).toBe 'function'
+


fix (testApp) fixes drawer paths

fix (testApp) fixes drawer paths


Test for style block being maintained.

Test for style block being maintained.


<tests> Skeleton of Driver Controller Tests.

error: pathspec 'tests/controllers/driver.coffee' did not match any file(s) known to git

--- tests/controllers/driver.coffee
+++ tests/controllers/driver.coffee
@@ -0,0 +1,113 @@
+###
+# =================
+# Test - driver
+# =================
+# Testing the drivers Controller.
+###
+
+fixturesDriver = require '../fixtures/driver'
+fs = require 'fs'
+os = require 'os'
+copyFile = require('../../server/helpers').copyFile
+helpers = require '../helpers'
+expect = require('chai').expect
+
+store = {}
+
+describe 'Drivers Controller', ->
+
+    before helpers.clearDB
+    before helpers.startServer
+    before helpers.makeTestClient
+
+    after  helpers.killServer
+
+    describe 'When we create a Sensor Driver (POST /drivers) as a Coffee script', ->
+
+        driver = fixturesDriver.basicDriver
+
+        before (done) ->
+            # copyFile(driver.file, )
+            # @todo Prepare driver data
+            done null
+
+        it 'should allow requests', (done) ->
+            @client.post 'drivers', driver, done
+
+        it 'should reply with the created Driver', ->
+            expect(@err).to.not.exist
+            expect(@response.statusCode).to.equal 201
+            expect(@body.name).to.equal driver.name
+            expect(@body.isSensor).is.true
+            expect(@body.isActuator).is.false
+            expect(@body.id).to.exist
+            store.driverId = @body.id
+
+    describe 'When we try creating a 2nd Driver (POST /drivers) with the same name', ->
+
+        driver = fixturesDriver.basicDriver
+
+        before (done) ->
+            # copyFile(driver.file, )
+            # @todo Prepare driver data
+            done null
+
+        it 'should allow requests', (done) ->
+            @client.post 'drivers', driver, done
+
+        it 'should reply with the previously-created Driver', ->
+            expect(@err).to.not.exist
+            expect(@response.statusCode).to.equal 202
+            expect(@body.id).to.equal store.driverId
+
+    # @todo Test adding with other script formats
+    # @todo Test adding incorrect driver (no file, bad syntax, missing methods, ...)
+
+    describe 'When we get a Driver (GET /drivers/:id) which exists', ->
+
+        it 'should allow requests', (done) ->
+            @client.get "drivers/#{store.driverId}", done
+
+        it 'should reply with the corresponding driver', ->
+            expect(@err).to.not.exist
+            expect(@body.name).to.equal fixturesDriver.basicDriver
+            expect(@body.id).to.equal store.driverId
+
+
+    describe 'When we get a Driver (GET /drivers/:id) which doesn\'t exist', ->
+
+
+        it 'should allow requests', (done) ->
+            id = store.driverId + 404 # since "store.driverId" is the only correct ID in DB, "store.driverId + 404" is not.
+            @client.get "drivers/#{id}", done
+
+        it 'should return an error', ->
+            expect(@response.statusCode).to.equal 404
+            expect(@body).to.exist
+            expect(@body.error).to.equal 'Driver not found'
+
+    describe 'When we update a driver (PUT /drivers/:id)', ->
+
+        it 'should allow requests', (done) ->
+            @client.put "drivers/#{store.driverId}", fixturesdriver.validUpdateForTestdriver, done
+
+        it 'should reply with an error (not allowed)', ->
+            # @todo
+
+    describe 'When we delete a Driver (DELETE /drivers/:id)', ->
+
+        it 'should allow requests', (done) ->
+            @client.del "drivers/#{store.driverId}", done
+
+        it 'should return a "success"', ->
+            expect(@err).to.not.exist
+            expect(@response.statusCode).to.equal 200
+            expect(@body.success).to.equal true
+
+        it 'when we try getting the deleted driver (GET /drivers/:id)', (done) ->
+            @client.get "drivers/#{store.driverId}", done
+
+        it 'should return an error', ->
+            expect(@response.statusCode).to.equal 404
+            expect(@body).to.exist
+            expect(@body.error).to.equal 'Driver not found'


Updated Gruntfile to watch all files in src.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/adrianlee44/galleryrs.git/'


Deploy - select2 image added

Deploy - select2 image added

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/keboola/kbc-ui-manage-apps.git/'


social: add util

social: add util

I need a place to put helper functions; social didn't have something like that yet

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/andrewjcasal/koding.git/'


frontend/todo: add TodoModel

frontend/todo: add TodoModel

error: pathspec 'frontend/todo/TodoModel.coffee' did not match any file(s) known to git

--- frontend/todo/TodoModel.coffee
+++ frontend/todo/TodoModel.coffee
@@ -0,0 +1,8 @@
+window.jj ?= {}
+
+window.jj.TodoModel = class TodoModel extends Backbone.Model
+    defaults:
+        title: ''
+        completed: false
+    toggle: ->
+        @save { completed: !@get('completed') }


Add forum polling support

Add forum polling support

error: pathspec 'scripts/forum-poll.coffee' did not match any file(s) known to git

--- scripts/forum-poll.coffee
+++ scripts/forum-poll.coffee
@@ -0,0 +1,79 @@
+# Description:
+# 	Poll forums
+#
+# Commands:
+# 	hubot check the forums - Tests forum search results
+#
+
+PROD_MODE        = process.env.NODE_ENV is 'production'
+POLL_INTERVAL    = 1000 * 60 * 10 # 10 minute interval to start with
+POLL_INTERVAL_ID = null
+ANNOUNCE_ROOMS   = process.env.HUBOT_IRC_ROOMS.split ','
+
+class ThreadPoster
+  constructor: (@robot) ->
+    @poller = new ForumPoller(@robot)
+    @notice = new NoticeCreator()
+
+  postNewThreads: (room) ->
+    @poller.getNewThreads (threads) =>
+      if threads?
+        messages = (@notice.create(thread) for thread in threads)
+        ANNOUNCE_ROOMS.forEach (room) => @robot.messageRoom room, messages.join("\r\n")
+      else
+        @robot.messageRoom room, "No new forum threads"
+
+class ForumPoller
+  LAST_POLLED_AT = null
+  HOST_URL       = if PROD_MODE then 'http://laravel.io' else 'http://app.local'
+  API_URI        = '/api/forum'
+
+  constructor: (@robot) ->
+
+  getNewThreads: (callback) ->
+    LAST_POLLED_AT = new Date()
+    @robot.http(HOST_URL+API_URI).query(take: 3).get() (err, res, body) =>
+      try
+        threads = JSON.parse(body)
+        result = threads.filter (thread) => @isMoreRecentThanLastPoll(thread.updated_at)
+
+
+        # need to set last polled date/time
+      catch error
+        console.log "Parsing error when fetching feeds from forum", error
+        result = []
+
+      callback.call(this, result)
+
+  isMoreRecentThanLastPoll: (dateString) ->
+    # This method will break apart a mysql datetime string into a JS Date obj
+    t         = dateString.split /[- :]/
+    updatedAt = new Date(t[0], t[1]-1, t[2], t[3], t[4], t[5])
+
+    if LAST_POLLED_AT?
+      updatedAt >= LAST_POLLED_AT
+    else
+      true
+
+class NoticeCreator
+  MESSAGE_FORMAT = "Forum Activity: :subject :url"
+
+  truncate: (msg, length = 40) ->
+    msg.substring(0, length) + '...'
+
+  create: (thread) ->
+    message = MESSAGE_FORMAT
+    message = message.replace(':subject', @truncate(thread.subject))
+                     .replace(':url', thread.url)
+    message
+
+module.exports = (robot) ->
+  poster = new ThreadPoster(robot)
+
+  POLL_INTERVAL_ID = setInterval ->
+    poster.postNewThreads(msg)
+  , POLL_INTERVAL
+
+  robot.respond /check the forums/, (msg) -> poster.postNewThreads()
+  #robot.on 'poll:forums:new', (posts) -> poster.postNewThreads()
+


[Refacto] remove unused utils/model.cofffee

--- server/utils/model.coffee
+++ server/utils/model.coffee
@@ -1,192 +0,0 @@
-# replace jugglingdb-cozy-adapter
-util = require 'util'
-
-module.exports = class Model
-
-    @docType -> throw new Error "You must subclass Model"
-    @schema ->
-        _id: String
-        binary: {String: {id: String, rev: String}}
-
-    @find: (id, callback) ->
-        client.get "data/#{id}/", (error, response, body) =>
-            if error
-                callback error
-            else if response.statusCode is 404
-                callback null, null
-            else if body.docType.toLowerCase() isnt model.toLowerCase()
-                callback null, null
-            else
-                callback null, new this body
-
-    @create: (attributes, callback) ->
-        path = "data/"
-        if data.id?
-            path += "#{data.id}/"
-            delete data.id
-            return callback new Error 'cant create an object with a set id'
-
-        data.docType = model
-
-        client.post path, data, (error, response, body) =>
-            if error
-                callback error
-            else if response.statusCode is 409
-                callback new Error "This document already exists"
-            else if response.statusCode isnt 201
-                callback new Error "Server error occured."
-            else
-                body.id = body._id
-                callback null, new this body
-
-    @updateAttributes: (id, data, callback) ->
-        client.put "data/merge/#{id}/", data, (error, response, body) ->
-            if error
-                callback error
-            else if response.statusCode is 404
-                callback new Error "Document not found"
-            else if response.statusCode isnt 200
-                callback new Error "Server error occured."
-            else
-                callback null, new this, body
-
-    @destroy: (id, callback) ->
-        client.del "data/#{id}/", (error, response, body) ->
-            if error
-                callback error
-            else if response.statusCode is 404
-                callback new Error "Document not found"
-            else if response.statusCode isnt 204
-                callback new Error "Server error occured."
-            else
-                callback()
-
-
-    @attachBinary: (id, path, data, callback) ->
-        [data, callback] = [null, data] if typeof(data) is "function"
-
-        urlPath = "data/#{id}/binaries/"
-        client.sendFile urlPath, path, data, (error, response, body) =>
-            try body = JSON.parse(body)
-            checkError error, response, body, 201, callback
-
-
-    @removeBinary: (id, path, callback) ->
-        urlPath = "data/#{id}/binaries/#{path}"
-        client.del urlPath, (error, response, body) =>
-            checkError error, response, body, 204, callback
-
-    @getBinary: (id, path, callback) ->
-        urlPath = "data/#{id}/binaries/#{path}"
-        stream = client.get urlPath, (error, response, body) =>
-            checkError error, response, body, 200, callback
-        , false
-
-        return stream
-
-
-    @request: (name, params, callback) ->
-        [params, callback] = [{}, params] if typeof(params) is "function"
-        docType = @docType()
-
-        path = "request/#{docType}/#{name.toLowerCase()}/"
-        @client.post path, params, (error, response, body) =>
-            if error
-                callback error
-            else if response.statusCode isnt 200
-                callback new Error util.inspect body
-            else
-                results = []
-                for doc in body
-                    doc.value.id = doc.value._id
-                    results.push new @_models[model].model(doc.value)
-                callback null, results
-
-    @rawRequest: (name, params, callback) ->
-        [params, callback] = [{}, params] if typeof(params) is "function"
-        docType = @docType()
-
-        path = "request/#{docType}/#{name.toLowerCase()}/"
-        @client.post path, params, (error, response, body) =>
-            if error
-                callback error
-            else if response.statusCode isnt 200
-                callback new Error util.inspect body
-            else
-                callback null, body
-
-
-    constructor: (attributes) ->
-        if validate attributes, @constructor.schema
-            for own key, value of attributes
-                this[key] = value
-
-    updateAttributes: (attributes, callback) ->
-        @constructor.updateAttributes @id, attributes, (err, data) =>
-            return callback err if err
-            for key, value of data
-                this[key] = value
-            callback null, this
-
-    index: (fields, callback) ->
-        @constructor.index @id, fields, callback
-
-    destroy: (callback) ->
-        @constructor.destroy @id, callback
-
-    getAttributes: ->
-        out = {}
-        for own key, value of this
-            out[key] = value
-        return out
-
-    toJSON: -> @getAttributes()
-    toString: -> util.inspect @toJSON()
-
-
-
-_toString = (x) -> Object.prototype.toString.call x
-_isArray = Array.isArray or (x) -> '[object Array]' is _toString obj
-_isMap = (x) -> '[object Object]' is _toString obj
-
-
-validate = (value, type) ->
-
-    return true if value is null
-
-    if type is String
-        return typeof raw is 'string'
-
-    else if type is Date
-        return raw instanceof Date
-
-    else if type is Boolean
-        return !!raw is raw
-
-    else if type is Number
-        return typeof raw is 'number'
-
-    else if _isArray type
-        itemtype = type[0]
-        return value.every (item) ->
-            validate item, itemtype
-
-    else if _isMap type
-        typekeys = Object.keys(type)
-        if typekeys.length is 1 # map object {String: Number}
-            typekey = typekeys[0]
-            typevalue = type[typekey]
-            valuekeys = Object.keys(valuekeys)
-            return valuekeys.every (key) ->
-                validate(key, typekey) and
-                validate(value[key], typevalue)
-
-        else if typekeys.length is 0 # no-shema
-            return true
-
-        else # shape object
-            return typekeys.every (key) ->
-                valuetype = type[key]
-                validate value[key], valuetype
-
-    else throw "DONT UNDERSTAND type"


Cosmetic

Cosmetic


Allow a default frame to be set for flipbooks (#3498)

* Allow for default_frame property on subject

* Fix bug where loops finish on the next frame, not the current frame

* Use 1 as index, not 0

* Add proper handling for default frame subjects

* Check for edge cases for default_frame

--- app/components/subject-viewer.cjsx
+++ app/components/subject-viewer.cjsx
@@ -50,10 +50,20 @@
   getInitialState: ->
     loading: true
     playing: false
-    frame: @props.frame ? 0
+    frame: @getInitialFrame()
     frameDimensions: {}
     inFlipbookMode: @props.allowFlipbook
     promptingToSignIn: false
+
+  getInitialFrame: ->
+    {frame, allowFlipbook, subject} = @props
+    default_frame = parseInt(subject.metadata.default_frame, 10)
+    initialFrame = 0
+    if frame?
+      initialFrame = frame
+    else if allowFlipbook and typeof default_frame is 'number' and !isNaN(default_frame) and default_frame > 0 and default_frame <= subject.locations.length
+      initialFrame = default_frame - 1
+    initialFrame

   componentWillReceiveProps: (nextProps) ->
     unless nextProps.subject is @props.subject
@@ -217,7 +227,7 @@
       @nextFrame()
       @_playingInterval = setInterval @nextFrame, @props.playFrameDuration

-      autoStopDelay = @props.subject.locations.length * @props.playFrameDuration * @props.playIterations
+      autoStopDelay = (@props.subject.locations.length * @props.playFrameDuration * @props.playIterations) - @props.playFrameDuration
       unless @props.playIterations is ''
         @_autoStop = setTimeout @setPlaying.bind(this, false), autoStopDelay
     else


Testing hipchat

Testing hipchat

--- src/http/HTTPService.coffee
+++ src/http/HTTPService.coffee
@@ -5,6 +5,7 @@
   Request::promise = ->
     return new Promise (resolve, reject)=>
       Request::end.call @, (err, res)->
+
         try

           if err


Activity: fix query handling error

Activity: fix query handling error

--- client/Social/Apps/AppController.coffee
+++ client/Social/Apps/AppController.coffee
@@ -118,7 +118,8 @@

   handleQuery:(query)->
     @ready =>
-      if {q} = query
+      {q} = query
+      if q
         @emit "searchFilterChanged", q
       else
         @feedController.handleQuery query


Bugs: Take care groups routes too

Bugs: Take care groups routes too

--- client/Social/Bugs/AppController.coffee
+++ client/Social/Bugs/AppController.coffee
@@ -2,7 +2,7 @@

   KD.registerAppClass this,
     name         : "Bugs"
-    route        : "/Bugs"
+    route        : "/:name?/Bugs"
     version      : "1.0"

   constructor:(options = {}, data)->


detail (Edit) page for apps

detail (Edit) page for apps

error: pathspec 'imports/ui/pages/AppsDetailPage.cjsx' did not match any file(s) known to git

--- imports/ui/pages/AppsDetailPage.cjsx
+++ imports/ui/pages/AppsDetailPage.cjsx
@@ -0,0 +1,37 @@
+React         = require 'react'
+DetailPage    = require '../DetailPage.cjsx'
+
+{ Header, Box, Split, Sidebar, Tabs, Tab } = require 'grommet'
+AppControls = require '../menus/AppControls.cjsx'
+YamlEditor  = require '../editors/YamlEditor.cjsx'
+
+module.exports = React.createClass
+  displayName: 'AppsDetailPage'
+
+  render: ->
+    if @props.app
+      @renderWithData()
+    else @renderNoData()
+
+  renderNoData: -> <span />
+
+  onChange: (code) -> console.log 'onChange', code
+
+  renderWithData: ->
+    console.log @props.app
+    <Split flex='left' priority='left'>
+      <DetailPage title={@props.title} >
+        <Tabs style={marginBottom:0} responsive={false}>
+          <Tab title='Docker Compose'>
+            <YamlEditor code={@props.app.dockerCompose} />
+          </Tab>
+          <Tab title='BigBoat Compose'>
+            <YamlEditor code={@props.app.bigboatCompose} />
+          </Tab>
+        </Tabs>
+      </DetailPage>
+      <Sidebar size='medium' colorIndex='light-2' direction='column'>
+        <Header pad='medium' size='large' />
+        <AppControls onRemoveApp={@props.onRemoveApp} onStartApp={@props.onStartApp} name={@props.title} />
+      </Sidebar>
+    </Split>


Don't open file in multiple tabs

Don't open file in multiple tabs

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/ylansegal/dotfiles.git/'


atom: config: go-plus settings

atom: config: go-plus settings

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/suyash-new/dotfiles.git/'


[repl] Declare repl-special functions at the end

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/demhydraz/cafe.git/'


trello: implement the ability to reopen

trello: implement the ability to reopen

--- plugins/trello/index.coffee
+++ plugins/trello/index.coffee
@@ -11,9 +11,21 @@

         callback null, res.body?.find((el) -> el.name == config?.listName)?.id

-  @receiveEvent: (config, event, callback) ->
-    return if event?.trigger?.type == "reopened"
-
+  @ensureIssueOpen: (config, issueId, callback) ->
+    @request.put("https://api.trello.com/1/cards/#{issueId}/?key=#{config?.applicationKey}&token=#{config?.memberToken}")
+      .send({"closed": false})
+      .on "error", (err) ->
+        callback(err)
+      .end (res) ->
+        callback(res.error)
+
+  @addCommentToIssue: (config, issueId, comment) ->
+    @request.post("https://api.trello.com/1/cards/#{issueId}/actions/comments/?key=#{config?.applicationKey}&token=#{config?.memberToken}")
+      .send({"text": comment})
+      .on("error", console.error)
+      .end()
+
+  @openIssue: (config, event, callback) ->
     # Would be nice to save this list Id for repeated calls
     @getListId config, (err, listId) =>
       return callback(err) if err
@@ -39,4 +51,12 @@
             id: res.body.id
             url: res.body.url

+  @receiveEvent: (config, event, callback) ->
+    if event?.trigger?.type == "reopened"
+      if event?.error?.createdIssue?.id
+        @ensureIssueOpen(config, event.error.createdIssue.id, callback)
+        @addCommentToIssue(config, event.error.createdIssue.id, @markdownBody(event))
+    else
+      @openIssue(config, event, callback)
+
 module.exports = Trello


Add toc singleton

Add toc singleton

error: pathspec 'src/scripts/collections/toc.coffee' did not match any file(s) known to git

--- src/scripts/collections/toc.coffee
+++ src/scripts/collections/toc.coffee
@@ -0,0 +1,4 @@
+define (require) ->
+  Backbone = require('backbone')
+
+  return new class Toc extends Backbone.Collection


Fixed rec method

Fixed rec method

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/danseethaler/userlite-snippets.git/'


Changed sleeper nonesense

Changed sleeper nonesense

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/jencka/2001-A-SPACE-SACRIFICE.git/'


html compiler

html compiler

error: pathspec 'src/compilers/html.coffee' did not match any file(s) known to git

--- src/compilers/html.coffee
+++ src/compilers/html.coffee
@@ -0,0 +1,24 @@
+
+module.exports = class HtmlCompiler
+    brunchPlugin: true
+    type: 'template'
+    pattern: /\.(?:html?)$/
+
+    compile: (params, next)->
+        {data, path, map} = params
+
+        data = """(function() {
+            var __templateData = #{JSON.stringify(data)};
+            if (typeof define === 'function' && define.amd) {
+                define([], function() {
+                    return __templateData;
+                });
+            } else if (typeof module === 'object' && module && module.exports) {
+                module.exports = __templateData;
+            } else {
+                return __templateData;
+            }
+        })();"""
+
+        next null, {data, path, map}
+        return


grammarsにおいて、コメントの色付け範囲の不具合を修正。

grammarsにおいて、コメントの色付け範囲の不具合を修正。

不具合は、例えば下記のコメントであればhttp:の右以降がコメント色になっていて、左端の//はコメント色になっていなかった。
// http://www....

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/masaru-matsunami/language-sad.git/'


Basic test of plan model

Basic test of plan model

error: pathspec 'test/unit/model/plan.coffee' did not match any file(s) known to git

--- test/unit/model/plan.coffee
+++ test/unit/model/plan.coffee
@@ -0,0 +1,19 @@
+sinon = require 'sinon'
+should = require 'should'
+_ = require 'underscore'
+
+request = require 'request'
+
+plan = require('model/plan')
+
+describe 'Plan (Server)', ->
+  describe 'Can get maximum dataset size', ->
+    it 'gets 8Mb for the free plan', ->
+      plan.datasetMaxSize("free").should.equal 8
+
+    #  describe 'Can set quota of a dataset', ->
+    #before (done) ->
+    #  plan.setDiskQuota()
+    #
+    #xit '', ->
+    #  #


Test routes.

Test routes.

error: pathspec 'test/unit/routesSpec.coffee' did not match any file(s) known to git

--- test/unit/routesSpec.coffee
+++ test/unit/routesSpec.coffee
@@ -0,0 +1,31 @@
+describe 'Unit Testing Routes', ->
+  # The Angular router.
+  $route = null
+
+  beforeEach ->
+    # Fake the application.
+    angular.mock.module('qiprofile.routes')
+    # Enable the Angular router.
+    inject ['$route', (_$route_) ->
+      $route = _$route_
+    ]
+
+  it 'should map home to the subject list controller', () ->
+    route = $route.routes['/quip']
+    expect(route).to.exist
+    expect(route.controller).to.equal('SubjectListCtrl')
+
+  it 'should map a subject to the subject detail controller', () ->
+    route = $route.routes['/quip/:collection/subject/:subject']
+    expect(route).to.exist
+    expect(route.controller).to.equal('SubjectDetailCtrl')
+
+  it 'should map a session to the session detail controller', () ->
+    route = $route.routes['/quip/:collection/subject/:subject/session/:session']
+    expect(route).to.exist
+    expect(route.controller).to.equal('SessionDetailCtrl')
+
+  it 'should map a series to the series detail controller', () ->
+    route = $route.routes['/quip/:collection/subject/:subject/session/:session/:image_container/series/:series']
+    expect(route).to.exist
+    expect(route.controller).to.equal('SeriesDetailCtrl')


Klient.storageSetSynced: synced setter for storage waits each one to be done

We can now use the storageSet how many times we want repeadetly, synced version
will make sure that they are queued.

It's a pre requirement for [#92331726]


sidebar/container: include managed vms in owned resources

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/app/lib/sidebar/container.coffee
+++ client/app/lib/sidebar/container.coffee
@@ -37,13 +37,17 @@
     then stacks.map (stack) -> { stack, template, unreadCount: 0 }
     else [{ stack: null, template, unreadCount: 0 }]

-
   debug 'resources are calculated before flatten', resources

   # now let's flatten them to have a single array
   resources = flatten(resources).sort ({ stack }) -> if stack then -1 else 1

-  debug 'resources are calculated', resources
+  [ managedStack ] = props.stacks.filter (s) -> s.title.indexOf('Managed VMs') > -1
+
+  if managedStack
+    resources.push { stack: managedStack, template: null, unreadCount: 0 }
+
+  debug 'owned resources are calculated', resources

   return resources


Remove old test setup

Remove old test setup

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/netguru/review.git/'


dawit

dawit

error: pathspec 'scripts/dawitistic.coffee' did not match any file(s) known to git

--- scripts/dawitistic.coffee
+++ scripts/dawitistic.coffee
@@ -0,0 +1,27 @@
+# Description
+#   Display a cowboy curtis at the mention of furious or style.
+#
+# Dependencies:
+#   None.
+#
+# Configuration:
+#   None.
+#
+# Commands:
+#   furious
+#   style
+#
+# Notes:
+#   None.
+#
+# Author:
+#   jeremy-green
+
+module.exports = (robot) ->
+  robot.hear /dawit?/i, (msg) ->
+    dawitMe msg
+
+dawitMe = (msg) ->
+  msg.http('http://dawitistics.com/dawitistic.txt')
+    .get() (err, res, body) ->
+      msg.send body


[emit] don't error on empty code parts

[emit] don't error on empty code parts


start matrix module

start matrix module

--- matrixSpec.coffee
+++ matrixSpec.coffee
@@ -0,0 +1,22 @@
+require ['math/matrix'], (m) ->
+
+  describe 'Matrix module', ->
+
+    describe 'map() function', ->
+
+      it 'should be able to map a matrix in place', ->
+
+        A = [[1, 2], [3, 4], [5, 6]]
+        B = m.map ((x) -> x*x), A
+        expect(B).toBe A # in place ?
+        expect(B).toEqual [[1, 4], [9, 16], [25, 36]] # adapted correctly ?
+
+      it 'should be able to map to another matrix', ->
+
+        A = [[1, 2], [3, 4], [5, 6]]
+        B = [[], [], []]
+        C = m.map ((x) -> x-1), A, B
+        expect(C).toBe B # B returned ?
+        expect(A).toEqual [[1, 2], [3, 4], [5, 6]] # A not changed ?
+        expect(B).toEqual [[0, 1], [2, 3], [4, 5]] # B adapted correctly ?
+


upd: test

upd: test

--- test/basic.coffee
+++ test/basic.coffee
@@ -26,12 +26,10 @@
 		return if a == b
 			false
 		else
-			process?.exit(1)
 			{ a, b }

 	for k, v of a
 		if b[k] != v
-			process?.exit(1)
 			return { a, b }

 	return false
@@ -49,6 +47,7 @@
 				#{JSON.stringify res.a}
 				>>>>>>>>
 			"""
+			process?.exit(1)

 	out = fn()
 	if out and out.then
@@ -63,6 +62,13 @@
 test 'resolve', $val, ->
 	new Yaku (resolve) ->
 		resolve $val
+
+test 'resolve promise like value', $val, ->
+	new Yaku (resolve) ->
+		resolve {
+			then: (fulfil) ->
+				fulfil $val
+		}

 test 'constructor throw', $val, ->
 	new Yaku (resolve) ->


Fix for anonymous user signups.

Fix for anonymous user signups.

--- server/routes/auth.coffee
+++ server/routes/auth.coffee
@@ -203,3 +203,4 @@
   user.set 'preferredLanguage', lang if lang[...2] isnt 'en'
   user.set 'lastIP', req.connection.remoteAddress
   user.set 'chinaVersion', true if req.chinaVersion
+  user


More tweaks.

More tweaks.

--- server/routes/mail.coffee
+++ server/routes/mail.coffee
@@ -30,7 +30,7 @@
       return errors.serverError(res) if err
       if not user
         badLog("could not find user for...: #{{'mailChimp.euid':post.data.id}}")
-        return errors.notFound(res) if err
+        return errors.notFound(res)

       handleProfileUpdate(user, post) if post.type is 'profile'
       handleUnsubscribe(user) if post.type is 'unsubscribe'
@@ -38,7 +38,7 @@
       user.updatedMailChimp = true # so as not to echo back to mailchimp
       user.save (err) ->
         return errors.serverError(res) if err
-        res.end()
+        res.end('Success')

 handleProfileUpdate = (user, post) ->
@@ -47,7 +47,7 @@
   user.set 'emailSubscriptions', groups

   mailChimpInfo = user.get 'mailChimp'
-  mailChimpInfo.email = post.data.email
+  mailChimpInfo.email = post.data.merges.EMAIL
   user.set 'mailChimp', mailChimpInfo

   badLog("Updating user object to: #{JSON.stringify(user.toObject(), null, '\t')}")


Scroll past end, indent guide, spaces as tabs

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/ftan84/dotfiles.git/'


Simple ambush script

Simple ambush script

error: pathspec 'src/scripts/ambush.coffee' did not match any file(s) known to git

--- src/scripts/ambush.coffee
+++ src/scripts/ambush.coffee
@@ -0,0 +1,31 @@
+# Send messages to users the next time they speak
+#
+# ambush <user name>: <message>
+
+appendAmbush = (data, toUser, fromUser, message) ->
+  if data[toUser.name]
+    data[toUser.name].push message
+  else
+    data[toUser.name] = [[fromUser.name, message]]
+
+module.exports = (robot) ->
+  robot.brain.on 'loaded', =>
+    robot.brain.data.ambushes ||= {}
+
+  robot.respond /ambush (.*): (.*)/i, (msg) ->
+    users = robot.usersForFuzzyName(msg.match[1])
+    if users.length is 1
+      user = users[0]
+      appendAmbush(robot.brain.data.ambushes, user, msg.message.user, msg.match[2])
+      msg.send "Ambush prepared"
+    else if users.length > 1
+      msg.send "Too many users like that"
+    else
+      msg.send "#{msg.match[1]}? Never heard of 'em"
+
+  robot.hear /./i, (msg) ->
+    if (ambushes = robot.brain.data.ambushes[msg.message.user.name])
+      msg.send "Hey, " + msg.message.user.name + ", while you were out:"
+      for ambush in ambushes
+        msg.send ambush[0] + " says: " + ambush[1]
+      msg.send "That's it. You were greatly missed."


rails_apps_composer: add analytics

rails_apps_composer: add analytics

error: pathspec 'app/assets/javascripts/google_analytics.js.coffee' did not match any file(s) known to git

--- app/assets/javascripts/google_analytics.js.coffee
+++ app/assets/javascripts/google_analytics.js.coffee
@@ -0,0 +1,44 @@
+# thanks to Jonathon Wolfe at http://reed.github.io/turbolinks-compatibility/google_analytics.html
+
+class @GoogleAnalytics
+
+  @load: ->
+    # Google Analytics depends on a global _gaq array. window is the global scope.
+    window._gaq = []
+    window._gaq.push ["_setAccount", GoogleAnalytics.analyticsId()]
+
+    # Create a script element and insert it in the DOM
+    ga = document.createElement("script")
+    ga.type = "text/javascript"
+    ga.async = true
+    ga.src = ((if "https:" is document.location.protocol then "https://ssl" else "http://www")) + ".google-analytics.com/ga.js"
+    firstScript = document.getElementsByTagName("script")[0]
+    firstScript.parentNode.insertBefore ga, firstScript
+
+    # If Turbolinks is supported, set up a callback to track pageviews on page:change.
+    # If it isn't supported, just track the pageview now.
+    if typeof Turbolinks isnt 'undefined' and Turbolinks.supported
+      document.addEventListener "page:change", (->
+        GoogleAnalytics.trackPageview()
+      ), true
+    else
+      GoogleAnalytics.trackPageview()
+
+  @trackPageview: (url) ->
+    unless GoogleAnalytics.isLocalRequest()
+      if url
+        window._gaq.push ["_trackPageview", url]
+      else
+        window._gaq.push ["_trackPageview"]
+      window._gaq.push ["_trackPageLoadTime"]
+
+  @isLocalRequest: ->
+    GoogleAnalytics.documentDomainIncludes "local"
+
+  @documentDomainIncludes: (str) ->
+    document.domain.indexOf(str) isnt -1
+
+  @analyticsId: ->
+    'UA-6619509-5'
+
+GoogleAnalytics.load()


Fix ENV to env

Fix ENV to env

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/sharabash/hubot.git/'


Made atom packages list

Made atom packages list

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/msmiel/dotfiles.git/'


fix popover positioning on calendar 2

fix popover positioning on calendar 2

this: http://cl.ly/image/0J0j0p393P3N
instead of: http://cl.ly/image/2q060C2H393R

Change-Id: Ie59206a5957097bc33fa096ed96de4c7ba48375a
Reviewed-on: https://gerrit.instructure.com/16332
Reviewed-by: Mark Ericksen <776ab05fc6b68e82d2b2c63873bf56edde131809@instructure.com>
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
QA-Review: Marc LeGendre <d33c80bc45d65303e33ca83108a9952b745af9ef@instructure.com>

--- app/coffeescripts/util/Popover.coffee
+++ app/coffeescripts/util/Popover.coffee
@@ -2,6 +2,18 @@
 define [
   # 'jquery'
 ], () ->
+
+
+  # you can provide a 'using' option to jqueryUI position
+  # it will be passed the position cordinates and a feedback object which,
+  # among other things, tells you where it positioned it relative to the target. we use it to add some
+  # css classes that handle putting the pointer triangle (aka: caret) back to the trigger.
+  using = ( position, feedback ) ->
+    $( this )
+      .css( position )
+      .toggleClass('carat-bottom', feedback.vertical == 'bottom')
+
+

   idCounter = 0
   activePopovers = []
@@ -75,5 +87,6 @@
         at: 'center top',
         of: @trigger,
         offset: '0 -10px',
-        within: '#main',
-        collision: 'fit fit'
+        within: 'body',
+        collision: 'flipfit flipfit'
+        using: using


Trigger storage of latest level only after the level is guaranteed to be able to load

--- app/views/play/level_view.coffee
+++ app/views/play/level_view.coffee
@@ -92,10 +92,6 @@
     else
       @load()

-    # Save latest level played in local storage
-    if localStorage?
-      localStorage["lastLevel"] = @levelID
-
   onLevelLoadError: (e) =>
     application.router.navigate "/play?not_found=#{@levelID}", {trigger: true}

@@ -129,6 +125,10 @@
     super()

   onLevelLoaderLoaded: =>
+    # Save latest level played in local storage
+    if localStorage?
+      localStorage["lastLevel"] = @levelID
+
     @session = @levelLoader.session
     @world = @levelLoader.world
     @level = @levelLoader.level