madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

better weather module: OpenWeatherMap; #105
authorluke bonham <dada@archlinux.info>
Thu, 2 Jul 2015 10:06:07 +0000 (12:06 +0200)
committerluke bonham <dada@archlinux.info>
Thu, 2 Jul 2015 10:06:07 +0000 (12:06 +0200)
60 files changed:
asyncshell.lua
icons/openweathermap/01d.png [moved from widgets/yawn/icons/DayClear.png with 100% similarity]
icons/openweathermap/01n.png [moved from widgets/yawn/icons/NightClear.png with 100% similarity]
icons/openweathermap/02d.png [moved from widgets/yawn/icons/DayPartlyCloudy.png with 100% similarity]
icons/openweathermap/02n.png [moved from widgets/yawn/icons/NightPartlyCloudy.png with 100% similarity]
icons/openweathermap/03d.png [moved from widgets/yawn/icons/DayMostlyCloudy.png with 100% similarity]
icons/openweathermap/03n.png [moved from widgets/yawn/icons/NightMostlyCloudy.png with 100% similarity]
icons/openweathermap/04d.png [moved from widgets/yawn/icons/Cloudy.png with 100% similarity]
icons/openweathermap/04n.png [new symlink]
icons/openweathermap/09d.png [moved from widgets/yawn/icons/Rain.png with 100% similarity]
icons/openweathermap/09n.png [new symlink]
icons/openweathermap/10d.png [moved from widgets/yawn/icons/Showers.png with 100% similarity]
icons/openweathermap/10n.png [new symlink]
icons/openweathermap/11d.png [moved from widgets/yawn/icons/RainThunder.png with 100% similarity]
icons/openweathermap/11n.png [new symlink]
icons/openweathermap/13d.png [moved from widgets/yawn/icons/HeavySnow.png with 100% similarity]
icons/openweathermap/13n.png [new symlink]
icons/openweathermap/50d.png [moved from widgets/yawn/icons/Foggy.png with 100% similarity]
icons/openweathermap/50n.png [new symlink]
icons/openweathermap/README.md [new file with mode: 0644]
icons/openweathermap/na.png [moved from widgets/yawn/icons/na.png with 100% similarity]
util/dkjson.lua [new file with mode: 0644]
widgets/contrib/ccurr.lua
widgets/weather.lua [new file with mode: 0644]
widgets/yawn/icons/BlowingSnow.png [deleted file]
widgets/yawn/icons/DayFair.png [deleted symlink]
widgets/yawn/icons/DayFairWindy.png [deleted symlink]
widgets/yawn/icons/Drizzle.png [deleted symlink]
widgets/yawn/icons/Fog.png [deleted symlink]
widgets/yawn/icons/FreezingDrizzle.png [deleted file]
widgets/yawn/icons/FreezingRain.png [deleted file]
widgets/yawn/icons/Hail.png [deleted file]
widgets/yawn/icons/Haze.png [deleted symlink]
widgets/yawn/icons/HeavyRain.png [deleted symlink]
widgets/yawn/icons/LightRain.png [deleted symlink]
widgets/yawn/icons/LightSnow.png [deleted symlink]
widgets/yawn/icons/LightSnowShowers.png [deleted file]
widgets/yawn/icons/Mist.png [deleted symlink]
widgets/yawn/icons/MixedRainAndHail.png [deleted file]
widgets/yawn/icons/MixedRainAndSleet.png [deleted file]
widgets/yawn/icons/MixedRainAndSnow.png [deleted file]
widgets/yawn/icons/NightFair.png [deleted symlink]
widgets/yawn/icons/NightFairWindy.png [deleted symlink]
widgets/yawn/icons/README.md [deleted file]
widgets/yawn/icons/Sleet.png [deleted symlink]
widgets/yawn/icons/Snow.png [deleted symlink]
widgets/yawn/icons/SnowFlurries.png [deleted symlink]
widgets/yawn/icons/SnowShowers.png [deleted file]
widgets/yawn/icons/Sunny.png [deleted file]
widgets/yawn/icons/ThunderintheVicinity.png [deleted symlink]
widgets/yawn/icons/Wind.png [deleted file]
widgets/yawn/init.lua [deleted file]
widgets/yawn/localizations/de_DE [deleted file]
widgets/yawn/localizations/fr_FR [deleted file]
widgets/yawn/localizations/it_IT [deleted file]
widgets/yawn/localizations/localization_template [deleted file]
widgets/yawn/localizations/ru_RU [deleted file]
widgets/yawn/localizations/zh_CN [deleted file]
widgets/yawn/localizations/zh_TW [deleted file]
wiki

index 36ccc47a916b80feb77c06f20a8adb8479d274b2..0aafa17e7df9ffb2194c4ee76baa25575af65774 100644 (file)
 -- How to use...
 -- ...asynchronously:
 -- asyncshell.request('wscript -Kiev', function(f) wwidget.text = f:read("*l") end)
--- ...synchronously
+-- ...synchronously:
 -- widget:set_text(asyncshell.demand('wscript -Kiev', 5):read("*l") or "Error")
 
--- This is more cpu demanding, but makes things faster.
-
 local spawn = require('awful.util').spawn
 
 asyncshell               = {}
diff --git a/icons/openweathermap/04n.png b/icons/openweathermap/04n.png
new file mode 120000 (symlink)
index 0000000..b9a83df
--- /dev/null
@@ -0,0 +1 @@
+04d.png
\ No newline at end of file
diff --git a/icons/openweathermap/09n.png b/icons/openweathermap/09n.png
new file mode 120000 (symlink)
index 0000000..cca1f5d
--- /dev/null
@@ -0,0 +1 @@
+09d.png
\ No newline at end of file
diff --git a/icons/openweathermap/10n.png b/icons/openweathermap/10n.png
new file mode 120000 (symlink)
index 0000000..6e01227
--- /dev/null
@@ -0,0 +1 @@
+10d.png
\ No newline at end of file
diff --git a/icons/openweathermap/11n.png b/icons/openweathermap/11n.png
new file mode 120000 (symlink)
index 0000000..b227917
--- /dev/null
@@ -0,0 +1 @@
+11d.png
\ No newline at end of file
diff --git a/icons/openweathermap/13n.png b/icons/openweathermap/13n.png
new file mode 120000 (symlink)
index 0000000..94e5a52
--- /dev/null
@@ -0,0 +1 @@
+13d.png
\ No newline at end of file
diff --git a/icons/openweathermap/50n.png b/icons/openweathermap/50n.png
new file mode 120000 (symlink)
index 0000000..e3ba961
--- /dev/null
@@ -0,0 +1 @@
+50d.png
\ No newline at end of file
diff --git a/icons/openweathermap/README.md b/icons/openweathermap/README.md
new file mode 100644 (file)
index 0000000..f908fbd
--- /dev/null
@@ -0,0 +1,3 @@
+[Plain Weather Icons](http://merlinthered.deviantart.com/art/plain-weather-icons-157162192), created by [MerlinTheRed](http://merlinthered.deviantart.com/).
+
+<a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"><img src="http://i.creativecommons.org/l/by-nc-sa/2.5/80x15.png" align="right"></a>
diff --git a/util/dkjson.lua b/util/dkjson.lua
new file mode 100644 (file)
index 0000000..89aa2e1
--- /dev/null
@@ -0,0 +1,713 @@
+-- Module options:
+local always_try_using_lpeg = true
+local register_global_module_table = false
+local global_module_name = 'json'
+
+--[==[
+
+David Kolf's JSON module for Lua 5.1/5.2
+
+Version 2.5
+
+
+For the documentation see the corresponding readme.txt or visit
+<http://dkolf.de/src/dkjson-lua.fsl/>.
+
+You can contact the author by sending an e-mail to 'david' at the
+domain 'dkolf.de'.
+
+
+Copyright (C) 2010-2013 David Heiko Kolf
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+--]==]
+
+-- global dependencies:
+local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset =
+      pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset
+local error, require, pcall, select = error, require, pcall, select
+local floor, huge = math.floor, math.huge
+local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat =
+      string.rep, string.gsub, string.sub, string.byte, string.char,
+      string.find, string.len, string.format
+local strmatch = string.match
+local concat = table.concat
+
+local json = { version = "dkjson 2.5" }
+
+if register_global_module_table then
+  _G[global_module_name] = json
+end
+
+local _ENV = nil -- blocking globals in Lua 5.2
+
+pcall (function()
+  -- Enable access to blocked metatables.
+  -- Don't worry, this module doesn't change anything in them.
+  local debmeta = require "debug".getmetatable
+  if debmeta then getmetatable = debmeta end
+end)
+
+json.null = setmetatable ({}, {
+  __tojson = function () return "null" end
+})
+
+local function isarray (tbl)
+  local max, n, arraylen = 0, 0, 0
+  for k,v in pairs (tbl) do
+    if k == 'n' and type(v) == 'number' then
+      arraylen = v
+      if v > max then
+        max = v
+      end
+    else
+      if type(k) ~= 'number' or k < 1 or floor(k) ~= k then
+        return false
+      end
+      if k > max then
+        max = k
+      end
+      n = n + 1
+    end
+  end
+  if max > 10 and max > arraylen and max > n * 2 then
+    return false -- don't create an array with too many holes
+  end
+  return true, max
+end
+
+local escapecodes = {
+  ["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f",
+  ["\n"] = "\\n",  ["\r"] = "\\r",  ["\t"] = "\\t"
+}
+
+local function escapeutf8 (uchar)
+  local value = escapecodes[uchar]
+  if value then
+    return value
+  end
+  local a, b, c, d = strbyte (uchar, 1, 4)
+  a, b, c, d = a or 0, b or 0, c or 0, d or 0
+  if a <= 0x7f then
+    value = a
+  elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then
+    value = (a - 0xc0) * 0x40 + b - 0x80
+  elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then
+    value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80
+  elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then
+    value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80
+  else
+    return ""
+  end
+  if value <= 0xffff then
+    return strformat ("\\u%.4x", value)
+  elseif value <= 0x10ffff then
+    -- encode as UTF-16 surrogate pair
+    value = value - 0x10000
+    local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400)
+    return strformat ("\\u%.4x\\u%.4x", highsur, lowsur)
+  else
+    return ""
+  end
+end
+
+local function fsub (str, pattern, repl)
+  -- gsub always builds a new string in a buffer, even when no match
+  -- exists. First using find should be more efficient when most strings
+  -- don't contain the pattern.
+  if strfind (str, pattern) then
+    return gsub (str, pattern, repl)
+  else
+    return str
+  end
+end
+
+local function quotestring (value)
+  -- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js
+  value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8)
+  if strfind (value, "[\194\216\220\225\226\239]") then
+    value = fsub (value, "\194[\128-\159\173]", escapeutf8)
+    value = fsub (value, "\216[\128-\132]", escapeutf8)
+    value = fsub (value, "\220\143", escapeutf8)
+    value = fsub (value, "\225\158[\180\181]", escapeutf8)
+    value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8)
+    value = fsub (value, "\226\129[\160-\175]", escapeutf8)
+    value = fsub (value, "\239\187\191", escapeutf8)
+    value = fsub (value, "\239\191[\176-\191]", escapeutf8)
+  end
+  return "\"" .. value .. "\""
+end
+json.quotestring = quotestring
+
+local function replace(str, o, n)
+  local i, j = strfind (str, o, 1, true)
+  if i then
+    return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1)
+  else
+    return str
+  end
+end
+
+-- locale independent num2str and str2num functions
+local decpoint, numfilter
+
+local function updatedecpoint ()
+  decpoint = strmatch(tostring(0.5), "([^05+])")
+  -- build a filter that can be used to remove group separators
+  numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+"
+end
+
+updatedecpoint()
+
+local function num2str (num)
+  return replace(fsub(tostring(num), numfilter, ""), decpoint, ".")
+end
+
+local function str2num (str)
+  local num = tonumber(replace(str, ".", decpoint))
+  if not num then
+    updatedecpoint()
+    num = tonumber(replace(str, ".", decpoint))
+  end
+  return num
+end
+
+local function addnewline2 (level, buffer, buflen)
+  buffer[buflen+1] = "\n"
+  buffer[buflen+2] = strrep ("  ", level)
+  buflen = buflen + 2
+  return buflen
+end
+
+function json.addnewline (state)
+  if state.indent then
+    state.bufferlen = addnewline2 (state.level or 0,
+                           state.buffer, state.bufferlen or #(state.buffer))
+  end
+end
+
+local encode2 -- forward declaration
+
+local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder, state)
+  local kt = type (key)
+  if kt ~= 'string' and kt ~= 'number' then
+    return nil, "type '" .. kt .. "' is not supported as a key by JSON."
+  end
+  if prev then
+    buflen = buflen + 1
+    buffer[buflen] = ","
+  end
+  if indent then
+    buflen = addnewline2 (level, buffer, buflen)
+  end
+  buffer[buflen+1] = quotestring (key)
+  buffer[buflen+2] = ":"
+  return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state)
+end
+
+local function appendcustom(res, buffer, state)
+  local buflen = state.bufferlen
+  if type (res) == 'string' then
+    buflen = buflen + 1
+    buffer[buflen] = res
+  end
+  return buflen
+end
+
+local function exception(reason, value, state, buffer, buflen, defaultmessage)
+  defaultmessage = defaultmessage or reason
+  local handler = state.exception
+  if not handler then
+    return nil, defaultmessage
+  else
+    state.bufferlen = buflen
+    local ret, msg = handler (reason, value, state, defaultmessage)
+    if not ret then return nil, msg or defaultmessage end
+    return appendcustom(ret, buffer, state)
+  end
+end
+
+function json.encodeexception(reason, value, state, defaultmessage)
+  return quotestring("<" .. defaultmessage .. ">")
+end
+
+encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, state)
+  local valtype = type (value)
+  local valmeta = getmetatable (value)
+  valmeta = type (valmeta) == 'table' and valmeta -- only tables
+  local valtojson = valmeta and valmeta.__tojson
+  if valtojson then
+    if tables[value] then
+      return exception('reference cycle', value, state, buffer, buflen)
+    end
+    tables[value] = true
+    state.bufferlen = buflen
+    local ret, msg = valtojson (value, state)
+    if not ret then return exception('custom encoder failed', value, state, buffer, buflen, msg) end
+    tables[value] = nil
+    buflen = appendcustom(ret, buffer, state)
+  elseif value == nil then
+    buflen = buflen + 1
+    buffer[buflen] = "null"
+  elseif valtype == 'number' then
+    local s
+    if value ~= value or value >= huge or -value >= huge then
+      -- This is the behaviour of the original JSON implementation.
+      s = "null"
+    else
+      s = num2str (value)
+    end
+    buflen = buflen + 1
+    buffer[buflen] = s
+  elseif valtype == 'boolean' then
+    buflen = buflen + 1
+    buffer[buflen] = value and "true" or "false"
+  elseif valtype == 'string' then
+    buflen = buflen + 1
+    buffer[buflen] = quotestring (value)
+  elseif valtype == 'table' then
+    if tables[value] then
+      return exception('reference cycle', value, state, buffer, buflen)
+    end
+    tables[value] = true
+    level = level + 1
+    local isa, n = isarray (value)
+    if n == 0 and valmeta and valmeta.__jsontype == 'object' then
+      isa = false
+    end
+    local msg
+    if isa then -- JSON array
+      buflen = buflen + 1
+      buffer[buflen] = "["
+      for i = 1, n do
+        buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder, state)
+        if not buflen then return nil, msg end
+        if i < n then
+          buflen = buflen + 1
+          buffer[buflen] = ","
+        end
+      end
+      buflen = buflen + 1
+      buffer[buflen] = "]"
+    else -- JSON object
+      local prev = false
+      buflen = buflen + 1
+      buffer[buflen] = "{"
+      local order = valmeta and valmeta.__jsonorder or globalorder
+      if order then
+        local used = {}
+        n = #order
+        for i = 1, n do
+          local k = order[i]
+          local v = value[k]
+          if v then
+            used[k] = true
+            buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
+            prev = true -- add a seperator before the next element
+          end
+        end
+        for k,v in pairs (value) do
+          if not used[k] then
+            buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
+            if not buflen then return nil, msg end
+            prev = true -- add a seperator before the next element
+          end
+        end
+      else -- unordered
+        for k,v in pairs (value) do
+          buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
+          if not buflen then return nil, msg end
+          prev = true -- add a seperator before the next element
+        end
+      end
+      if indent then
+        buflen = addnewline2 (level - 1, buffer, buflen)
+      end
+      buflen = buflen + 1
+      buffer[buflen] = "}"
+    end
+    tables[value] = nil
+  else
+    return exception ('unsupported type', value, state, buffer, buflen,
+      "type '" .. valtype .. "' is not supported by JSON.")
+  end
+  return buflen
+end
+
+function json.encode (value, state)
+  state = state or {}
+  local oldbuffer = state.buffer
+  local buffer = oldbuffer or {}
+  state.buffer = buffer
+  updatedecpoint()
+  local ret, msg = encode2 (value, state.indent, state.level or 0,
+                   buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state)
+  if not ret then
+    error (msg, 2)
+  elseif oldbuffer == buffer then
+    state.bufferlen = ret
+    return true
+  else
+    state.bufferlen = nil
+    state.buffer = nil
+    return concat (buffer)
+  end
+end
+
+local function loc (str, where)
+  local line, pos, linepos = 1, 1, 0
+  while true do
+    pos = strfind (str, "\n", pos, true)
+    if pos and pos < where then
+      line = line + 1
+      linepos = pos
+      pos = pos + 1
+    else
+      break
+    end
+  end
+  return "line " .. line .. ", column " .. (where - linepos)
+end
+
+local function unterminated (str, what, where)
+  return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where)
+end
+
+local function scanwhite (str, pos)
+  while true do
+    pos = strfind (str, "%S", pos)
+    if not pos then return nil end
+    local sub2 = strsub (str, pos, pos + 1)
+    if sub2 == "\239\187" and strsub (str, pos + 2, pos + 2) == "\191" then
+      -- UTF-8 Byte Order Mark
+      pos = pos + 3
+    elseif sub2 == "//" then
+      pos = strfind (str, "[\n\r]", pos + 2)
+      if not pos then return nil end
+    elseif sub2 == "/*" then
+      pos = strfind (str, "*/", pos + 2)
+      if not pos then return nil end
+      pos = pos + 2
+    else
+      return pos
+    end
+  end
+end
+
+local escapechars = {
+  ["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f",
+  ["n"] = "\n", ["r"] = "\r", ["t"] = "\t"
+}
+
+local function unichar (value)
+  if value < 0 then
+    return nil
+  elseif value <= 0x007f then
+    return strchar (value)
+  elseif value <= 0x07ff then
+    return strchar (0xc0 + floor(value/0x40),
+                    0x80 + (floor(value) % 0x40))
+  elseif value <= 0xffff then
+    return strchar (0xe0 + floor(value/0x1000),
+                    0x80 + (floor(value/0x40) % 0x40),
+                    0x80 + (floor(value) % 0x40))
+  elseif value <= 0x10ffff then
+    return strchar (0xf0 + floor(value/0x40000),
+                    0x80 + (floor(value/0x1000) % 0x40),
+                    0x80 + (floor(value/0x40) % 0x40),
+                    0x80 + (floor(value) % 0x40))
+  else
+    return nil
+  end
+end
+
+local function scanstring (str, pos)
+  local lastpos = pos + 1
+  local buffer, n = {}, 0
+  while true do
+    local nextpos = strfind (str, "[\"\\]", lastpos)
+    if not nextpos then
+      return unterminated (str, "string", pos)
+    end
+    if nextpos > lastpos then
+      n = n + 1
+      buffer[n] = strsub (str, lastpos, nextpos - 1)
+    end
+    if strsub (str, nextpos, nextpos) == "\"" then
+      lastpos = nextpos + 1
+      break
+    else
+      local escchar = strsub (str, nextpos + 1, nextpos + 1)
+      local value
+      if escchar == "u" then
+        value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16)
+        if value then
+          local value2
+          if 0xD800 <= value and value <= 0xDBff then
+            -- we have the high surrogate of UTF-16. Check if there is a
+            -- low surrogate escaped nearby to combine them.
+            if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then
+              value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16)
+              if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then
+                value = (value - 0xD800)  * 0x400 + (value2 - 0xDC00) + 0x10000
+              else
+                value2 = nil -- in case it was out of range for a low surrogate
+              end
+            end
+          end
+          value = value and unichar (value)
+          if value then
+            if value2 then
+              lastpos = nextpos + 12
+            else
+              lastpos = nextpos + 6
+            end
+          end
+        end
+      end
+      if not value then
+        value = escapechars[escchar] or escchar
+        lastpos = nextpos + 2
+      end
+      n = n + 1
+      buffer[n] = value
+    end
+  end
+  if n == 1 then
+    return buffer[1], lastpos
+  elseif n > 1 then
+    return concat (buffer), lastpos
+  else
+    return "", lastpos
+  end
+end
+
+local scanvalue -- forward declaration
+
+local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta)
+  local len = strlen (str)
+  local tbl, n = {}, 0
+  local pos = startpos + 1
+  if what == 'object' then
+    setmetatable (tbl, objectmeta)
+  else
+    setmetatable (tbl, arraymeta)
+  end
+  while true do
+    pos = scanwhite (str, pos)
+    if not pos then return unterminated (str, what, startpos) end
+    local char = strsub (str, pos, pos)
+    if char == closechar then
+      return tbl, pos + 1
+    end
+    local val1, err
+    val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta)
+    if err then return nil, pos, err end
+    pos = scanwhite (str, pos)
+    if not pos then return unterminated (str, what, startpos) end
+    char = strsub (str, pos, pos)
+    if char == ":" then
+      if val1 == nil then
+        return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")"
+      end
+      pos = scanwhite (str, pos + 1)
+      if not pos then return unterminated (str, what, startpos) end
+      local val2
+      val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta)
+      if err then return nil, pos, err end
+      tbl[val1] = val2
+      pos = scanwhite (str, pos)
+      if not pos then return unterminated (str, what, startpos) end
+      char = strsub (str, pos, pos)
+    else
+      n = n + 1
+      tbl[n] = val1
+    end
+    if char == "," then
+      pos = pos + 1
+    end
+  end
+end
+
+scanvalue = function (str, pos, nullval, objectmeta, arraymeta)
+  pos = pos or 1
+  pos = scanwhite (str, pos)
+  if not pos then
+    return nil, strlen (str) + 1, "no valid JSON value (reached the end)"
+  end
+  local char = strsub (str, pos, pos)
+  if char == "{" then
+    return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta)
+  elseif char == "[" then
+    return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta)
+  elseif char == "\"" then
+    return scanstring (str, pos)
+  else
+    local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos)
+    if pstart then
+      local number = str2num (strsub (str, pstart, pend))
+      if number then
+        return number, pend + 1
+      end
+    end
+    pstart, pend = strfind (str, "^%a%w*", pos)
+    if pstart then
+      local name = strsub (str, pstart, pend)
+      if name == "true" then
+        return true, pend + 1
+      elseif name == "false" then
+        return false, pend + 1
+      elseif name == "null" then
+        return nullval, pend + 1
+      end
+    end
+    return nil, pos, "no valid JSON value at " .. loc (str, pos)
+  end
+end
+
+local function optionalmetatables(...)
+  if select("#", ...) > 0 then
+    return ...
+  else
+    return {__jsontype = 'object'}, {__jsontype = 'array'}
+  end
+end
+
+function json.decode (str, pos, nullval, ...)
+  local objectmeta, arraymeta = optionalmetatables(...)
+  return scanvalue (str, pos, nullval, objectmeta, arraymeta)
+end
+
+function json.use_lpeg ()
+  local g = require ("lpeg")
+
+  if g.version() == "0.11" then
+    error "due to a bug in LPeg 0.11, it cannot be used for JSON matching"
+  end
+
+  local pegmatch = g.match
+  local P, S, R = g.P, g.S, g.R
+
+  local function ErrorCall (str, pos, msg, state)
+    if not state.msg then
+      state.msg = msg .. " at " .. loc (str, pos)
+      state.pos = pos
+    end
+    return false
+  end
+
+  local function Err (msg)
+    return g.Cmt (g.Cc (msg) * g.Carg (2), ErrorCall)
+  end
+
+  local SingleLineComment = P"//" * (1 - S"\n\r")^0
+  local MultiLineComment = P"/*" * (1 - P"*/")^0 * P"*/"
+  local Space = (S" \n\r\t" + P"\239\187\191" + SingleLineComment + MultiLineComment)^0
+
+  local PlainChar = 1 - S"\"\\\n\r"
+  local EscapeSequence = (P"\\" * g.C (S"\"\\/bfnrt" + Err "unsupported escape sequence")) / escapechars
+  local HexDigit = R("09", "af", "AF")
+  local function UTF16Surrogate (match, pos, high, low)
+    high, low = tonumber (high, 16), tonumber (low, 16)
+    if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then
+      return true, unichar ((high - 0xD800)  * 0x400 + (low - 0xDC00) + 0x10000)
+    else
+      return false
+    end
+  end
+  local function UTF16BMP (hex)
+    return unichar (tonumber (hex, 16))
+  end
+  local U16Sequence = (P"\\u" * g.C (HexDigit * HexDigit * HexDigit * HexDigit))
+  local UnicodeEscape = g.Cmt (U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence/UTF16BMP
+  local Char = UnicodeEscape + EscapeSequence + PlainChar
+  local String = P"\"" * g.Cs (Char ^ 0) * (P"\"" + Err "unterminated string")
+  local Integer = P"-"^(-1) * (P"0" + (R"19" * R"09"^0))
+  local Fractal = P"." * R"09"^0
+  local Exponent = (S"eE") * (S"+-")^(-1) * R"09"^1
+  local Number = (Integer * Fractal^(-1) * Exponent^(-1))/str2num
+  local Constant = P"true" * g.Cc (true) + P"false" * g.Cc (false) + P"null" * g.Carg (1)
+  local SimpleValue = Number + String + Constant
+  local ArrayContent, ObjectContent
+
+  -- The functions parsearray and parseobject parse only a single value/pair
+  -- at a time and store them directly to avoid hitting the LPeg limits.
+  local function parsearray (str, pos, nullval, state)
+    local obj, cont
+    local npos
+    local t, nt = {}, 0
+    repeat
+      obj, cont, npos = pegmatch (ArrayContent, str, pos, nullval, state)
+      if not npos then break end
+      pos = npos
+      nt = nt + 1
+      t[nt] = obj
+    until cont == 'last'
+    return pos, setmetatable (t, state.arraymeta)
+  end
+
+  local function parseobject (str, pos, nullval, state)
+    local obj, key, cont
+    local npos
+    local t = {}
+    repeat
+      key, obj, cont, npos = pegmatch (ObjectContent, str, pos, nullval, state)
+      if not npos then break end
+      pos = npos
+      t[key] = obj
+    until cont == 'last'
+    return pos, setmetatable (t, state.objectmeta)
+  end
+
+  local Array = P"[" * g.Cmt (g.Carg(1) * g.Carg(2), parsearray) * Space * (P"]" + Err "']' expected")
+  local Object = P"{" * g.Cmt (g.Carg(1) * g.Carg(2), parseobject) * Space * (P"}" + Err "'}' expected")
+  local Value = Space * (Array + Object + SimpleValue)
+  local ExpectedValue = Value + Space * Err "value expected"
+  ArrayContent = Value * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp()
+  local Pair = g.Cg (Space * String * Space * (P":" + Err "colon expected") * ExpectedValue)
+  ObjectContent = Pair * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp()
+  local DecodeValue = ExpectedValue * g.Cp ()
+
+  function json.decode (str, pos, nullval, ...)
+    local state = {}
+    state.objectmeta, state.arraymeta = optionalmetatables(...)
+    local obj, retpos = pegmatch (DecodeValue, str, pos, nullval, state)
+    if state.msg then
+      return nil, state.pos, state.msg
+    else
+      return obj, retpos
+    end
+  end
+
+  -- use this function only once:
+  json.use_lpeg = function () return json end
+
+  json.using_lpeg = true
+
+  return json -- so you can get the module using json = require "dkjson".use_lpeg()
+end
+
+if always_try_using_lpeg then
+  pcall (json.use_lpeg)
+end
+
+return json
index f696a354d20beb227fd9248530e43f5f526c8114..980e19b3a99d7814350fb4848906bd62f338f74d 100644 (file)
@@ -7,9 +7,9 @@
 --]]
 
 local newtimer = require("lain.helpers").newtimer
+local json     = require("lain.util").dkjson
 
 local wibox    = require("wibox")
-local json     = require("dkjson")
 
 local string   = { format = string.format }
 local tonumber = tonumber
diff --git a/widgets/weather.lua b/widgets/weather.lua
new file mode 100644 (file)
index 0000000..d8dfd3f
--- /dev/null
@@ -0,0 +1,125 @@
+
+--[[
+                                                  
+     Licensed under GNU General Public License v2 
+      * (c) 2015, Luke Bonham                     
+                                                  
+--]]
+
+local newtimer     = require("lain.helpers").newtimer
+local async        = require("lain.asyncshell")
+local json         = require("lain.util").dkjson
+local lain_icons   = require("lain.helpers").icons_dir
+local naughty      = require("naughty")
+local wibox        = require("wibox")
+
+local math         = { floor  = math.floor }
+local string       = { format = string.format,
+                       gsub   = string.gsub }
+
+local setmetatable = setmetatable
+
+-- OpenWeatherMap
+-- current weather and X-days forecast
+-- lain.widgets.weather
+
+local function worker(args)
+    local weather               = {}
+    local args                  = args or {}
+    local timeout               = args.timeout or 900   -- 15 min
+    local timeout_forecast      = args.timeout or 86400 -- 24 hrs
+    local current_call          = "curl -s 'http://api.openweathermap.org/data/2.5/weather?id=%s&units=%s&lang=%s'"
+    local forecast_call         = "curl -s 'http://api.openweathermap.org/data/2.5/forecast/daily?id=%s&units=%s&lang=%s&cnt=%s'"
+    local city_id               = args.city_id
+    local units                 = args.units or "metric"
+    local lang                  = args.lang or "en"
+    local cnt                   = args.cnt or 7
+    local date_cmd              = args.date_cmd or "date -u -d @%d +'%%a %%d'"
+    local icons_path            = args.icons_path or lain_icons .. "openweathermap/"
+    local w_notification_preset = args.w_notification_preset or {}
+    local settings              = args.settings or function() end
+
+    weather.widget = wibox.widget.textbox('')
+    weather.icon   = wibox.widget.imagebox()
+
+    function weather.show(t_out)
+        weather.hide()
+        weather.notification = naughty.notify({
+            text    = weather.notification_text,
+            icon    = weather.icon_path,
+            timeout = t_out,
+            preset  = w_notification_preset
+        })
+    end
+
+    function weather.hide()
+        if weather.notification ~= nil then
+            naughty.destroy(weather.notification)
+            weather.notification = nil
+        end
+    end
+
+    function weather.attach(obj)
+        obj:connect_signal("mouse::enter", function()
+            weather.show(0)
+        end)
+        obj:connect_signal("mouse::leave", function()
+            weather.hide()
+        end)
+    end
+
+    function weather.forecast_update()
+        local cmd = string.format(forecast_call, city_id, units, lang, cnt)
+        async.request(cmd, function(f)
+            j = f:read("*a")
+            f:close()
+            weather_now, pos, err = json.decode(j, 1, nil)
+
+            if tonumber(weather_now["cod"]) == 200 then
+                weather.notification_text = ''
+                for i = 1, weather_now["cnt"] do
+                    local f = assert(io.popen(string.format(date_cmd, weather_now["list"][i]["dt"])))
+                    day = string.gsub(f:read("a"), "\n", "")
+                    f:close()
+
+                    tmin = math.floor(weather_now["list"][i]["temp"]["min"])
+                    tmax = math.floor(weather_now["list"][i]["temp"]["max"])
+                    desc = weather_now["list"][i]["weather"][1]["description"]
+
+                    weather.notification_text = weather.notification_text ..
+                                                string.format("<b>%s</b>: %s, %d - %d  ", day, desc, tmin, tmax)
+
+                    if i < weather_now["cnt"] then
+                        weather.notification_text = weather.notification_text .. "\n"
+                    end
+                end
+            end
+        end)
+    end
+
+    function weather.update()
+        local cmd = string.format(current_call, city_id, units, lang)
+        async.request(cmd, function(f)
+            j = f:read("*a")
+            f:close()
+            weather_now, pos, err = json.decode(j, 1, nil)
+
+            if err then
+                weather.widget.set_text("N/A")
+                weather.icon:set_image(icons_path .. "na.png")
+            elseif tonumber(weather_now["cod"]) == 200 then
+                weather.icon_path = icons_path .. weather_now["weather"][1]["icon"] .. ".png"
+                weather.icon:set_image(weather.icon_path)
+                widget = weather.widget
+                settings()
+            end
+        end)
+    end
+
+    newtimer("weather", timeout, weather.update)
+    newtimer("weather_forecast", timeout, weather.forecast_update)
+
+    return setmetatable(weather, { __index = weather.widget })
+end
+
+return setmetatable({}, { __call = function(_, ...) return worker(...) end })
diff --git a/widgets/yawn/icons/BlowingSnow.png b/widgets/yawn/icons/BlowingSnow.png
deleted file mode 100755 (executable)
index 6223f8f..0000000
Binary files a/widgets/yawn/icons/BlowingSnow.png and /dev/null differ
diff --git a/widgets/yawn/icons/DayFair.png b/widgets/yawn/icons/DayFair.png
deleted file mode 120000 (symlink)
index 8ee94d1..0000000
+++ /dev/null
@@ -1 +0,0 @@
-DayClear.png
\ No newline at end of file
diff --git a/widgets/yawn/icons/DayFairWindy.png b/widgets/yawn/icons/DayFairWindy.png
deleted file mode 120000 (symlink)
index 8ee94d1..0000000
+++ /dev/null
@@ -1 +0,0 @@
-DayClear.png
\ No newline at end of file
diff --git a/widgets/yawn/icons/Drizzle.png b/widgets/yawn/icons/Drizzle.png
deleted file mode 120000 (symlink)
index df34463..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Rain.png
\ No newline at end of file
diff --git a/widgets/yawn/icons/Fog.png b/widgets/yawn/icons/Fog.png
deleted file mode 120000 (symlink)
index b615645..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Foggy.png
\ No newline at end of file
diff --git a/widgets/yawn/icons/FreezingDrizzle.png b/widgets/yawn/icons/FreezingDrizzle.png
deleted file mode 100755 (executable)
index 6a66140..0000000
Binary files a/widgets/yawn/icons/FreezingDrizzle.png and /dev/null differ
diff --git a/widgets/yawn/icons/FreezingRain.png b/widgets/yawn/icons/FreezingRain.png
deleted file mode 100755 (executable)
index c924fac..0000000
Binary files a/widgets/yawn/icons/FreezingRain.png and /dev/null differ
diff --git a/widgets/yawn/icons/Hail.png b/widgets/yawn/icons/Hail.png
deleted file mode 100755 (executable)
index 009039f..0000000
Binary files a/widgets/yawn/icons/Hail.png and /dev/null differ
diff --git a/widgets/yawn/icons/Haze.png b/widgets/yawn/icons/Haze.png
deleted file mode 120000 (symlink)
index 0874a83..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Hail.png
\ No newline at end of file
diff --git a/widgets/yawn/icons/HeavyRain.png b/widgets/yawn/icons/HeavyRain.png
deleted file mode 120000 (symlink)
index ace2a94..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Showers.png
\ No newline at end of file
diff --git a/widgets/yawn/icons/LightRain.png b/widgets/yawn/icons/LightRain.png
deleted file mode 120000 (symlink)
index df34463..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Rain.png
\ No newline at end of file
diff --git a/widgets/yawn/icons/LightSnow.png b/widgets/yawn/icons/LightSnow.png
deleted file mode 120000 (symlink)
index aa8b28e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-LightSnowShowers.png
\ No newline at end of file
diff --git a/widgets/yawn/icons/LightSnowShowers.png b/widgets/yawn/icons/LightSnowShowers.png
deleted file mode 100755 (executable)
index d797ee9..0000000
Binary files a/widgets/yawn/icons/LightSnowShowers.png and /dev/null differ
diff --git a/widgets/yawn/icons/Mist.png b/widgets/yawn/icons/Mist.png
deleted file mode 120000 (symlink)
index b615645..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Foggy.png
\ No newline at end of file
diff --git a/widgets/yawn/icons/MixedRainAndHail.png b/widgets/yawn/icons/MixedRainAndHail.png
deleted file mode 100755 (executable)
index 758b01e..0000000
Binary files a/widgets/yawn/icons/MixedRainAndHail.png and /dev/null differ
diff --git a/widgets/yawn/icons/MixedRainAndSleet.png b/widgets/yawn/icons/MixedRainAndSleet.png
deleted file mode 100755 (executable)
index 7f0d252..0000000
Binary files a/widgets/yawn/icons/MixedRainAndSleet.png and /dev/null differ
diff --git a/widgets/yawn/icons/MixedRainAndSnow.png b/widgets/yawn/icons/MixedRainAndSnow.png
deleted file mode 100755 (executable)
index 0a07b7b..0000000
Binary files a/widgets/yawn/icons/MixedRainAndSnow.png and /dev/null differ
diff --git a/widgets/yawn/icons/NightFair.png b/widgets/yawn/icons/NightFair.png
deleted file mode 120000 (symlink)
index 23df45a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-NightClear.png
\ No newline at end of file
diff --git a/widgets/yawn/icons/NightFairWindy.png b/widgets/yawn/icons/NightFairWindy.png
deleted file mode 120000 (symlink)
index 23df45a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-NightClear.png
\ No newline at end of file
diff --git a/widgets/yawn/icons/README.md b/widgets/yawn/icons/README.md
deleted file mode 100644 (file)
index e4dc111..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-Yawn icons
-==========
-
-These are [Plain Weather Icons](http://merlinthered.deviantart.com/art/plain-weather-icons-157162192), created by [MerlinTheRed](http://merlinthered.deviantart.com/).
-
-<a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"><img src="http://i.creativecommons.org/l/by-nc-sa/2.5/80x15.png" align="right"></a>
diff --git a/widgets/yawn/icons/Sleet.png b/widgets/yawn/icons/Sleet.png
deleted file mode 120000 (symlink)
index f8f9693..0000000
+++ /dev/null
@@ -1 +0,0 @@
-SnowShowers.png
\ No newline at end of file
diff --git a/widgets/yawn/icons/Snow.png b/widgets/yawn/icons/Snow.png
deleted file mode 120000 (symlink)
index f8f9693..0000000
+++ /dev/null
@@ -1 +0,0 @@
-SnowShowers.png
\ No newline at end of file
diff --git a/widgets/yawn/icons/SnowFlurries.png b/widgets/yawn/icons/SnowFlurries.png
deleted file mode 120000 (symlink)
index 2e090cd..0000000
+++ /dev/null
@@ -1 +0,0 @@
-BlowingSnow.png
\ No newline at end of file
diff --git a/widgets/yawn/icons/SnowShowers.png b/widgets/yawn/icons/SnowShowers.png
deleted file mode 100755 (executable)
index 30534a2..0000000
Binary files a/widgets/yawn/icons/SnowShowers.png and /dev/null differ
diff --git a/widgets/yawn/icons/Sunny.png b/widgets/yawn/icons/Sunny.png
deleted file mode 100755 (executable)
index cf08c5c..0000000
Binary files a/widgets/yawn/icons/Sunny.png and /dev/null differ
diff --git a/widgets/yawn/icons/ThunderintheVicinity.png b/widgets/yawn/icons/ThunderintheVicinity.png
deleted file mode 120000 (symlink)
index 1fb3b9c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Cloudy.png
\ No newline at end of file
diff --git a/widgets/yawn/icons/Wind.png b/widgets/yawn/icons/Wind.png
deleted file mode 100755 (executable)
index 5dc1356..0000000
Binary files a/widgets/yawn/icons/Wind.png and /dev/null differ
diff --git a/widgets/yawn/init.lua b/widgets/yawn/init.lua
deleted file mode 100644 (file)
index 1f6b0f4..0000000
+++ /dev/null
@@ -1,206 +0,0 @@
-
---[[
-                                                  
-     Licensed under GNU General Public License v2 
-      * (c) 2013, Luke Bonham                     
-                                                  
---]]
-
-local newtimer     = require("lain.helpers").newtimer
-local async        = require("lain.asyncshell")
-
-local naughty      = require("naughty")
-local wibox        = require("wibox")
-
-local debug        = { getinfo = debug.getinfo }
-local io           = { lines   = io.lines,
-                       open    = io.open }
-local os           = { date    = os.date,
-                       getenv  = os.getenv }
-local string       = { find    = string.find,
-                       match   = string.match,
-                       gsub    = string.gsub,
-                       sub     = string.sub }
-local tonumber     = tonumber
-
-local setmetatable = setmetatable
-
--- YAhoo! Weather Notification
--- lain.widgets.yawn
-local yawn =
-{
-    icon   = wibox.widget.imagebox(),
-    widget = wibox.widget.textbox('')
-}
-
-local project_path        = debug.getinfo(1, 'S').source:match[[^@(.*/).*$]]
-local localizations_path  = project_path .. 'localizations/'
-local icon_path           = project_path .. 'icons/'
-local api_url             = 'http://weather.yahooapis.com/forecastrss'
-local units_set           = '?u=c&w=' -- Default is Celsius
-local language            = string.match(os.getenv("LANG"), "(%S*$*)[.]") or "en_US" -- if LANG is not set
-local weather_data        = nil
-local notification        = nil
-local city_id             = nil
-local sky                 = nil
-local settings            = function() end
-
-yawn_notification_preset  = {}
-
-function yawn.fetch_weather()
-    local url = api_url .. units_set .. city_id
-    local cmd = "curl --connect-timeout 1 -fsm 3 '" .. url .. "'"
-
-    async.request(cmd, function(f)
-        local text = f:read("*a")
-        f:close()
-
-        -- In case of no connection or invalid city ID
-        -- widgets won't display
-        if text == "" or text:match("City not found")
-        then
-            yawn.icon:set_image(icon_path .. "na.png")
-            if text == "" then
-                weather_data = "Service not available at the moment."
-                yawn.widget:set_text(" N/A ")
-            else
-                weather_data = "City not found!\n" ..
-                               "Are you sure " .. city_id ..
-                               " is your Yahoo city ID?"
-                yawn.widget:set_text(" ? ")
-            end
-            return
-        end
-
-        -- Processing raw data
-        weather_data = text:gsub("<.->", "")
-        weather_data = weather_data:match("Current Conditions:.-Full") or ""
-
-        -- may still happens in case of bad connectivity
-        if weather_data == "" then
-            yawn.icon:set_image(icon_path .. "na.png")
-            yawn.widget:set_text(" ? ")
-            return
-        end
-
-        weather_data = weather_data:gsub("Current Conditions:.-\n", "Now: ")
-        weather_data = weather_data:gsub("Forecast:.-\n", "")
-        weather_data = weather_data:gsub("\nFull", "")
-        weather_data = weather_data:gsub("[\n]$", "")
-        weather_data = weather_data:gsub(" [-] " , ": ")
-        weather_data = weather_data:gsub("[.]", ",")
-        weather_data = weather_data:gsub("High: ", "")
-        weather_data = weather_data:gsub(" Low: ", " - ")
-
-        -- Getting info for text widget
-        local now      = weather_data:sub(weather_data:find("Now:")+5,
-                         weather_data:find("\n")-1)
-        forecast       = now:sub(1, now:find(",")-1)
-        units          = now:sub(now:find(",")+2, -2)
-
-        -- Day/Night icon change
-        local hour = tonumber(os.date("%H"))
-        sky = icon_path
-
-        if string.find(forecast, "Clear")         or
-           string.find(forecast, "Fair")          or
-           string.find(forecast, "Partly Cloudy") or
-           string.find(forecast, "Mostly Cloudy")
-           then
-               if hour >= 6 and hour <= 18
-               then
-                   sky = sky .. "Day"
-               else
-                   sky = sky .. "Night"
-               end
-        end
-
-        sky = sky  .. forecast:gsub(" ", ""):gsub("/", "") .. ".png"
-
-        -- In case there's no defined icon for current forecast
-        if io.open(sky) == nil then
-            sky = icon_path .. "na.png"
-        end
-
-        -- Localization
-        local f = io.open(localizations_path .. language, "r")
-        if language:find("en_") == nil and f ~= nil
-        then
-            f:close()
-            for line in io.lines(localizations_path .. language)
-            do
-                word = string.sub(line, 1, line:find("|")-1)
-                translation = string.sub(line, line:find("|")+1)
-                weather_data = string.gsub(weather_data, word, translation)
-            end
-        end
-
-        -- Finally setting infos
-        yawn.icon:set_image(sky)
-        widget = yawn.widget
-
-        _data = weather_data:match(": %S.-,") or weather_data
-        forecast = _data:gsub(": ", ""):gsub(",", "")
-        units = units:gsub(" ", "")
-
-        settings()
-    end)
-end
-
-function yawn.hide()
-    if notification ~= nil then
-        naughty.destroy(notification)
-        notification = nil
-    end
-end
-
-function yawn.show(t_out)
-    if yawn.widget._layout.text:match("?")
-    then
-        yawn.fetch_weather()
-    end
-
-    yawn.hide()
-
-    notification = naughty.notify({
-        preset = yawn_notification_preset,
-        text = weather_data,
-        icon = sky,
-        timeout = t_out,
-    })
-end
-
-function yawn.register(id, args)
-    local args     = args or {}
-    local timeout  = args.timeout or 600
-    settings       = args.settings or function() end
-
-    if args.u == "f" then units_set = '?u=f&w=' end
-
-    city_id = id
-
-    newtimer("yawn", timeout, yawn.fetch_weather)
-
-    yawn.icon:connect_signal("mouse::enter", function()
-        yawn.show(0)
-    end)
-    yawn.icon:connect_signal("mouse::leave", function()
-        yawn.hide()
-    end)
-
-    return yawn
-end
-
-function yawn.attach(widget, id, args)
-    yawn.register(id, args)
-
-    widget:connect_signal("mouse::enter", function()
-        yawn.show(0)
-    end)
-
-    widget:connect_signal("mouse::leave", function()
-        yawn.hide()
-    end)
-end
-
-return setmetatable(yawn, { __call = function(_, ...) return yawn.register(...) end })
diff --git a/widgets/yawn/localizations/de_DE b/widgets/yawn/localizations/de_DE
deleted file mode 100644 (file)
index 82cef79..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-Now:|Jetzt:
-Sun:|So:
-Mon:|Mo:
-Tue:|Di:
-Wed:|Mi:
-Thu:|Do:
-Fri:|Fr:
-Sat:|Sa:
-Mostly Sunny|Größtenteils Sonnig
-Sunny|Sonnig
-Sun|Sonne
-Rain/Thunder|Regen/Donner
-Isolated Thunderstorms|Vereinzelte Gewitter
-Scattered Thunderstorms|Aufgelockertes Gewitter
-Thundershowers|Gewitterschauer
-Thunderstorms|Gewitter
-Thunder in the Vicinity|Donner in der Umgebung
-Thunder|Donner
-AM|Vormittags
-PM|Nachmittags
-Early|Früh
-Late|Spät
-Few|einige
-Severe|starker
-Clear|Klar
-Fair|Heiter
-Partly|teilweise
-Mostly|größtenteils
-Cloudy|Wolkig
-Clouds|Wolken
-Scattered Showers|Vereinzelte Schauer
-Light Snow Showers|Leichter Schneeregen
-Snow Showers|Schneeregen
-Heavy Snow|Starker Schneefall
-Scattered Snow Showers|Vereinzelter Schneefall
-Mixed Rain And Snow|Gemischter Regen und Schnee
-Mixed Rain And Sleet|Gemischter Regen und Graupel
-Mixed Snow And Sleet|Gemischter Schnee und Graupel
-Mixed Rain And Hail|Gemischter Regen und Hagel
-Snow Flurries|Schneegestöber
-Blowing Snow|Schneetreiben
-Blowing Rain|Treibender Regen
-Heavy Rain|Starke Regenfälle
-Freezing Rain|Eisregen
-Showers|Schauer
-Light Rain|Leichter Regen
-Heavy|Starker
-Rain|Regen
-Windy|Windig
-Wind|Wind
-Snow|Schnee
-Sleet|Graupel
-Freezing Drizzle|Gefrierender Sprühregen
-Light Drizzle|Leichter Sprühregen
-Drizzle|Sprühregen
-Hail|Hagel
-Fog|Nebel
-Foggy|Nebelig
-Haze|Dunst
-Light|leichter
-With|mit
\ No newline at end of file
diff --git a/widgets/yawn/localizations/fr_FR b/widgets/yawn/localizations/fr_FR
deleted file mode 100644 (file)
index 18a35bb..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-Now:|Auj:
-Sun:|Dim:
-Mon:|Lun:
-Tue:|Mar:
-Wed:|Mer:
-Thu:|Jeu:
-Fri:|Ven:
-Sat:|Sam:
-Mostly Sunny|Partiellement ensoleillé
-Sunny|Ensoleillé
-Sun|Soleil
-Rain/Thunder|Pluie/Orage
-Isolated Thunderstorms|Orages localisés
-Scattered Thunderstorms|Orages épars
-Thundershowers|Tempête
-Thunderstorms|Orages
-Thunder in the Vicinity|Orage aux alentours
-Thunder|Orages
-AM|Matinée
-PM|Après-midi
-Early|Tôt
-Late|Tard
-Few|Quelques
-Severe|Sévère
-Clear|Clair
-Fair|Clair
-Partly|Partiellement
-Mostly|Très
-Cloudy|Nuageux
-Clouds|Nuages
-Scattered Showers|Nuages épars
-Light Snow Showers|Légères averses de neige
-Snow Showers|Averses de neige
-Heavy Snow|Neige
-Scattered Snow Showers|Averses de neige localisées
-Mixed Rain And Snow|Alternance de neige et de pluie
-Mixed Rain And Sleet|Alternance de pluie et de neige fondue
-Mixed Snow And Sleet|Alternance de neige et de neige fondue
-Mixed Rain And Hail|Alternance de pluie et de grêle
-Snow Flurries|Averses de neige
-Blowing Snow|Neige
-Blowing Rain|Pluie
-Heavy Rain|Pluie forte
-Freezing Rain|Pluie verglaçante
-Showers|Averses
-Light Rain|Pluie légère
-Heavy|Forte
-Rain|Pluie
-Windy|Venteux
-Wind|Vent
-Snow|Neige
-Sleet|Neige fondue
-Freezing Drizzle|Bruine verglaçante
-Light Drizzle|Légère bruine
-Drizzle|Bruine
-Hail|Grêle
-Fog|Brouillard
-Foggy|Brumeux
-Haze|Brume
-Light|Clair
-With|Avec
diff --git a/widgets/yawn/localizations/it_IT b/widgets/yawn/localizations/it_IT
deleted file mode 100644 (file)
index 44d010e..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-Now:|Ora:
-Sun:|Dom:
-Mon:|Lun:
-Tue:|Mar:
-Wed:|Mer:
-Thu:|Gio:
-Fri:|Ven:
-Sat:|Sab:
-Mostly Sunny|Abbastanza Soleggiato
-Sunny|Soleggiato
-Sun|Soleggiato
-Rain/Thunder|Temporali
-Isolated Thunderstorms|Temporali Isolati
-Scattered Thunderstorms|Temporali Sparsi
-Thundershowers|Rovesci Temporaleschi
-Thunderstorms|Temporali
-Thunder in the Vicinity|Tuoni in prossimità
-Thunder|Temporale
-AM|In Mattinata
-PM|Nel Pomeriggio
-Early|In Mattinata
-Late|In Serata
-Few|Sporadiche
-Severe|Forti
-Clear|Sereno
-Fair|Sereno
-Partly|Parzialmente
-Mostly|Molto
-Cloudy|Nuvoloso
-Clouds|Nuvoloso
-Scattered Showers|Temporali Sparsi
-Light Snow Showers|Nevicate Leggere
-Snow Showers|Nevicate
-aeavy Snow|Forti Nevicate
-Scattered Snow Showers|Nevicate Sparse
-Mixed Rain And Snow|Pioggia E Neve
-Mixed Rain And Sleet|Pioggia E Nevischio
-Mixed Snow And Sleet|Neve E Nevischio
-Mixed Rain And Hail|Pioggia E Grandine
-Snow Flurries|Folate Di Neve
-Blowing Snow|Neve Battente
-Blowing Rain|Pioggia Battente
-Heavy Rain|Forti Piogge
-Freezing Rain|Pioggia Congelantesi
-Showers|Piogge
-Light Rain|Pioggia Leggera
-Heavy|Forti
-Rain|Piovoso
-Windy|Ventoso
-Wind|Ventoso
-Snow|Neve
-Sleet|Nevischio
-Light Drizzle|Pioggia Leggera
-Drizzle|Pioggia Leggera
-Freezing Drizzle|Pioggerella Congelantesi
-Hail|Grandine
-Fog|Nebbia
-Foggy|Nebbioso
-Haze|Nebbia
-Light|Leggere
-With|Con
diff --git a/widgets/yawn/localizations/localization_template b/widgets/yawn/localizations/localization_template
deleted file mode 100644 (file)
index 2fbf066..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-Now:|
-Sun:|
-Mon:|
-Tue:|
-Wed:|
-Thu:|
-Fri:|
-Sat:|
-Mostly Sunny|
-Sunny|
-Sun|
-Rain/Thunder|
-Isolated Thunderstorms|
-Scattered Thunderstorms|
-Thundershowers|
-Thunderstorms|
-Thunder in the Vicinity|
-Thunder|
-AM|
-PM|
-Early|
-Late|
-Few|
-Severe|
-Clear|
-Fair|
-Partly|
-Mostly|
-Cloudy|
-Clouds|
-Scattered Showers|
-Light Snow Showers|
-Snow Showers|
-Heavy Snow|
-Scattered Snow Showers|
-Mixed Rain And Snow|
-Mixed Rain And Sleet|
-Mixed Snow And Sleet|
-Mixed Rain And Hail|
-Snow Flurries|
-Blowing Snow|
-Blowing Rain|
-Heavy Rain|
-Freezing Rain|
-Showers|
-Light Rain|
-Heavy|
-Rain|
-Windy|
-Wind|
-Snow|
-Sleet|
-Freezing Drizzle|
-Light Drizzle|
-Drizzle|
-Hail|
-Fog|
-Foggy|
-Haze|
-Light|
-With|
diff --git a/widgets/yawn/localizations/ru_RU b/widgets/yawn/localizations/ru_RU
deleted file mode 100644 (file)
index 0b2ae23..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-Now:|Cейчас:
-Sun:|Воскресенье:
-Mon:|Понедельник:
-Tue:|Вторник:
-Wed:|Среда:
-Thu:|Четверг:
-Fri:|Пятница:
-Sat:|Суббота:
-Mostly Sunny|Преимущественно солнечно
-Sunny|Солнечно
-Sun|Солнце
-Rain/Thunder|Дождь/Гром
-Isolated Thunderstorms|Изолированные грозы
-Scattered Thunderstorms|Рассеянные грозы
-Thundershowers|Ливни
-Thunderstorms|Грозы
-Thunder in the Vicinity|Гром в окрестностях
-Thunder|Гром
-AM|Утро
-PM|Вечер
-Early|Рано
-Late|Поздно
-Few|Мало
-Severe|Тяжелый
-Clear|Ясно
-Fair|Светлый
-Partly|Частично
-Mostly|По большей части
-Cloudy|Облачно
-Clouds|Облака
-Scattered Showers|Рассеянные ливни
-Light Snow Showers|Небольшой снег
-Snow Showers|Ливневый Снег
-Heavy Snow|Сильный снегопад
-Scattered Snow Showers|Рассеянный ливневый снег
-Mixed Rain And Snow|Снег с дождём
-Mixed Rain And Sleet|Дождь и мокрый снег
-Mixed Snow And Sleet|Снег и мокрый снег
-Mixed Rain And Hail|Дождь с градом
-Snow Flurries|Снежные порывы
-Blowing Snow|Снег и ветер
-Blowing Rain|Дождь и ветер
-Heavy Rain|Сильный дождь
-Freezing Rain|Ледяной дождь
-Showers|Ливни
-Light Rain|Небольшой дождь
-Heavy|Сильный 
-Rain|Дождь
-Windy|Ветреный
-Wind|Ветер
-Snow|Снег
-Sleet|Мокрый снег
-Freezing Drizzle|Изморозь
-Light Drizzle|Лёгкая изморось
-Drizzle|Моросящий дождь
-Hail|Град
-Fog|Туман
-Foggy|Туманно
-Haze|Дымка
-Light|Лёгкий
-With|С
diff --git a/widgets/yawn/localizations/zh_CN b/widgets/yawn/localizations/zh_CN
deleted file mode 100644 (file)
index 61e98a4..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-Now:|当前:
-Sun:|周日:
-Mon:|周一:
-Tue:|周二:
-Wed:|周三:
-Thu:|周四:
-Fri:|周五:
-Sat:|周六:
-Mostly Sunny|晴时多云
-Sunny|晴朗
-Sun|太阳
-Rain/Thunder|雨/雷
-Isolated Thunderstorms|局部雷雨
-Scattered Thunderstorms|零星雷雨
-Thundershowers|雷阵雨
-Thunderstorms|雷雨
-Thunder in the Vicinity|周围有雷雨
-Thunder|雷鸣
-AM|上午
-PM|下午
-Early|早
-Late|晚
-Few|短暂
-Severe|恶劣
-Clear|晴朗
-Fair|晴
-Partly|局部
-Mostly|大部
-Cloudy|多云
-Clouds|有云
-Scattered Showers|零星阵雨
-Light Snow Showers|小阵雪
-Snow Showers|阵雪
-Heavy Snow|大雪
-Scattered Snow Showers|零星阵雪
-Mixed Rain And Snow|雨夹雪
-Mixed Rain And Sleet|雨转雨夹雪
-Mixed Snow And Sleet|雪转雨夹雪
-Mixed Rain And Hail|雨夹冰雹
-Snow Flurries|阵雪
-Blowing Snow|风吹雪
-Blowing Rain|风吹雨
-Heavy Rain|大雨
-Freezing Rain|冻雨
-Showers|阵雨
-Light Rain|小雨
-Heavy|大
-Rain|雨
-Windy|有风
-Wind|风
-Snow|雪
-Sleet|冻雨
-Freezing Drizzle|冻毛毛雨
-Light Drizzle|细雨
-Drizzle|毛毛雨
-Hail|冰雹
-Fog|雾
-Foggy|有雾
-Haze|霾
-Light|小
-With|與
diff --git a/widgets/yawn/localizations/zh_TW b/widgets/yawn/localizations/zh_TW
deleted file mode 100644 (file)
index 03644c1..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-Now:|現在:
-Sun:|週日:
-Mon:|週一:
-Tue:|週二:
-Wed:|週三:
-Thu:|週四:
-Fri:|週五:
-Sat:|週六:
-Mostly Sunny|晴時多雲
-Sunny|大太陽
-Sun|太陽
-Rain/Thunder|雨時有雷
-Isolated Thunderstorms|局部雷雨
-Scattered Thunderstorms|零星雷雨
-Thundershowers|雷陣雨
-Thunderstorms|雷雨
-Thunder in the Vicinity|局部性雷雨
-Thunder|雷嗚
-AM|上午
-PM|下午
-Early|早
-Late|晚有
-Few|短暫
-Severe|惡劣
-Clear|晴朗
-Fair|晴
-Partly|局部
-Mostly|大部
-Cloudy|多雲
-Clouds|有雲
-Scattered Showers|零星陣雨
-Light Snow Showers|小陣雪
-Snow Showers|陣雪
-Heavy Snow|大雪
-Scattered Snow Showers|零星陣雪
-Mixed Rain And Snow|雨夾雪
-Mixed Rain And Sleet|雨時雨夾雪
-Mixed Snow And Sleet|雪時雨夾雪
-Mixed Rain And Hail|雨夾冰雹
-Snow Flurries|陣雪
-Blowing Snow|風吹雪
-Blowing Rain|風吹雨
-Heavy Rain|大雨
-Freezing Rain|凍雨
-Showers|陣雨
-Light Rain|小雨
-Heavy|大
-Rain|雨
-Windy|有風
-Wind|風
-Snow|雪
-Sleet|冰珠
-Freezing Drizzle|凍毛毛雨
-Light Drizzle|細雨
-Drizzle|毛毛雨
-Hail|冰雹
-Fog|霧
-Foggy|有霧
-Haze|霾
-Light|小
-With|與
diff --git a/wiki b/wiki
index b011c0339e805ee1596d0b6778d6c497dab41109..75c796a9a7a0f0f468bd36f77c7b918a2ff9a4d5 160000 (submodule)
--- a/wiki
+++ b/wiki
@@ -1 +1 @@
-Subproject commit b011c0339e805ee1596d0b6778d6c497dab41109
+Subproject commit 75c796a9a7a0f0f468bd36f77c7b918a2ff9a4d5