aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Library/ml_webdev
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Library/ml_webdev')
-rw-r--r--Src/Plugins/Library/ml_webdev/commands.cpp328
-rw-r--r--Src/Plugins/Library/ml_webdev/commands.h29
-rw-r--r--Src/Plugins/Library/ml_webdev/common.cpp238
-rw-r--r--Src/Plugins/Library/ml_webdev/common.h68
-rw-r--r--Src/Plugins/Library/ml_webdev/config.cpp74
-rw-r--r--Src/Plugins/Library/ml_webdev/config.h18
-rw-r--r--Src/Plugins/Library/ml_webdev/external.cpp272
-rw-r--r--Src/Plugins/Library/ml_webdev/external.h49
-rw-r--r--Src/Plugins/Library/ml_webdev/forceUrl.cpp77
-rw-r--r--Src/Plugins/Library/ml_webdev/forceUrl.h15
-rw-r--r--Src/Plugins/Library/ml_webdev/import.h18
-rw-r--r--Src/Plugins/Library/ml_webdev/importFile.cpp266
-rw-r--r--Src/Plugins/Library/ml_webdev/importUrl.cpp320
-rw-r--r--Src/Plugins/Library/ml_webdev/local_menu.cpp222
-rw-r--r--Src/Plugins/Library/ml_webdev/local_menu.h17
-rw-r--r--Src/Plugins/Library/ml_webdev/main.cpp167
-rw-r--r--Src/Plugins/Library/ml_webdev/main.h25
-rw-r--r--Src/Plugins/Library/ml_webdev/ml_webdev.rc182
-rw-r--r--Src/Plugins/Library/ml_webdev/ml_webdev.sln30
-rw-r--r--Src/Plugins/Library/ml_webdev/ml_webdev.vcxproj382
-rw-r--r--Src/Plugins/Library/ml_webdev/ml_webdev.vcxproj.filters187
-rw-r--r--Src/Plugins/Library/ml_webdev/navigation.cpp1261
-rw-r--r--Src/Plugins/Library/ml_webdev/navigation.h83
-rw-r--r--Src/Plugins/Library/ml_webdev/png.rc9
-rw-r--r--Src/Plugins/Library/ml_webdev/resource.h60
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/gear.pngbin0 -> 265 bytes
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/help.pngbin0 -> 288 bytes
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/api2.js624
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/applicationApi.htm32
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/asyncDownloaderApi.htm42
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/bookmarkApi.htm17
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/configApi.htm28
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/historyApi.htm18
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/mediaCoreApi.htm37
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/playQueueApi.htm74
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/playlistApi.htm118
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/podcastApi.htm15
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/securityApi.htm17
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/serviceEditor.htm31
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/skinApi.htm40
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/transportApi.htm64
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/transportEventsApi.htm25
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/webdev.htm29
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/webdev.js97
-rw-r--r--Src/Plugins/Library/ml_webdev/resources/pages/welcome.htm22
-rw-r--r--Src/Plugins/Library/ml_webdev/serviceHelper.cpp232
-rw-r--r--Src/Plugins/Library/ml_webdev/serviceHelper.h28
-rw-r--r--Src/Plugins/Library/ml_webdev/serviceHost.cpp243
-rw-r--r--Src/Plugins/Library/ml_webdev/serviceHost.h55
-rw-r--r--Src/Plugins/Library/ml_webdev/testPages.rc22
-rw-r--r--Src/Plugins/Library/ml_webdev/version.rc239
-rw-r--r--Src/Plugins/Library/ml_webdev/wasabi.cpp87
-rw-r--r--Src/Plugins/Library/ml_webdev/wasabi.h45
53 files changed, 6478 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_webdev/commands.cpp b/Src/Plugins/Library/ml_webdev/commands.cpp
new file mode 100644
index 00000000..241017c9
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/commands.cpp
@@ -0,0 +1,328 @@
+#include "main.h"
+#include "./commands.h"
+#include "./wasabi.h"
+#include "./resource.h"
+#include "./navigation.h"
+#include "./import.h"
+#include "./serviceHelper.h"
+#include "../../General/gen_ml/ml_ipc_0313.h"
+#include <ifc_omservice.h>
+#include <browserView.h>
+#include <strsafe.h>
+
+HRESULT Command_NavigateService(ifc_omservice *service, LPCWSTR pszUrl, BOOL fActiveOnly)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ Navigation *navigation;
+ if (FAILED(Plugin_GetNavigation(&navigation)))
+ return E_UNEXPECTED;
+
+ ifc_omservice *activeService;
+ HWND hView = navigation->GetActiveView(&activeService);
+ if (NULL == hView || activeService->GetId() != service->GetId())
+ hView = NULL;
+
+ if (NULL != activeService)
+ activeService->Release();
+
+ HRESULT hr = S_OK;
+
+ if (NULL != hView)
+ {
+ if (FALSE == BrowserView_Navigate(hView, pszUrl, TRUE))
+ hr = E_FAIL;
+ }
+ else
+ {
+ hr = (FALSE == fActiveOnly) ?
+ navigation->ShowService(service->GetId(), pszUrl) : E_NOTIMPL;
+ }
+
+ navigation->Release();
+ return hr;
+}
+static void CALLBACK ThreadCallback_NavigateService(Dispatchable *object, ULONG_PTR param1, ULONG_PTR param2)
+{
+ ifc_omservice *service = (ifc_omservice*)object;
+ LPWSTR pszUrl = (LPWSTR)param1;
+
+ Command_NavigateService(service, pszUrl, (BOOL)param2);
+
+ Plugin_FreeResString(pszUrl);
+}
+
+HRESULT Command_PostNavigateSvc(ifc_omservice *service, LPCWSTR pszUrl, BOOL fActiveOnly)
+{
+ if (NULL == OMUTILITY)
+ return E_FAIL;
+
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ LPWSTR pszUrlCopy = Plugin_DuplicateResString(pszUrl);
+ HRESULT hr = OMUTILITY->PostMainThreadCallback2(ThreadCallback_NavigateService, service, (ULONG_PTR)pszUrlCopy, fActiveOnly);
+ if (FAILED(hr))
+ {
+ Plugin_FreeResString(pszUrlCopy);
+ }
+
+ return hr;
+
+}
+
+HRESULT Command_EditService(ifc_omservice *service)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ WCHAR szBuffer[2048];
+
+ HRESULT hr = Plugin_MakeResourcePath(szBuffer, ARRAYSIZE(szBuffer), RT_HTML, MAKEINTRESOURCE(IDR_HTML_EDITOR), RESPATH_TARGETIE | RESPATH_COMPACT);
+ if (FAILED(hr)) return hr;
+
+ INT cchUrl = lstrlen(szBuffer);
+ LPWSTR pszParam = szBuffer + cchUrl;
+ INT cchParamMax = ARRAYSIZE(szBuffer) - cchUrl;
+ hr = StringCchPrintf(pszParam, cchParamMax, L"?serviceId=%u", service->GetId());
+ if (FAILED(hr)) return hr;
+
+ return Command_NavigateService(service, szBuffer, FALSE);
+}
+
+HRESULT Command_ReloadService(ifc_omservice *service)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ HRESULT hr = ServiceHelper_Reload(service);
+ return hr;
+}
+
+HRESULT Command_ResetPermissions(ifc_omservice *service)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ if (NULL == AGAVE_API_JSAPI2_SECURITY)
+ return E_UNEXPECTED;
+
+ WCHAR szBuffer[64] = {0};
+ if (FAILED(StringCchPrintfW(szBuffer, ARRAYSIZE(szBuffer), L"%u", service->GetId())))
+ return E_FAIL;
+
+ AGAVE_API_JSAPI2_SECURITY->ResetAuthorization(szBuffer);
+ return S_OK;
+}
+
+HRESULT Command_LocateService(ifc_omservice *service)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ WCHAR szPath[512];
+
+ HRESULT hr = service->GetAddress(szPath, ARRAYSIZE(szPath));
+ if (FAILED(hr)) return hr;
+
+ if (L'\0' == szPath[0])
+ return E_FAIL;
+
+ Navigation *navigation;
+ if (FAILED(Plugin_GetNavigation(&navigation)))
+ return E_UNEXPECTED;
+
+ navigation->Release();
+
+ if (WASABI_API_EXPLORERFINDFILE)
+ {
+ WASABI_API_EXPLORERFINDFILE->AddFile(szPath);
+ WASABI_API_EXPLORERFINDFILE->ShowFiles();
+ }
+ return E_UNEXPECTED;
+}
+
+HRESULT Command_EditServiceExternal(ifc_omservice *service)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ WCHAR szPath[512] = {0};
+
+ HRESULT hr = service->GetAddress(szPath, ARRAYSIZE(szPath));
+ if (FAILED(hr)) return hr;
+
+ if (L'\0' == szPath[0])
+ return E_FAIL;
+
+ Navigation *navigation;
+ if (FAILED(Plugin_GetNavigation(&navigation)))
+ return E_UNEXPECTED;
+
+ HWND hOwner = navigation->GetActiveView(NULL);
+ navigation->Release();
+
+ if (NULL == hOwner)
+ hOwner = Plugin_GetLibrary();
+
+ HINSTANCE hInst = ShellExecute(hOwner, L"open", szPath, NULL, NULL, SW_SHOWNORMAL);
+ hr = ((INT_PTR)hInst > 32) ? S_OK : E_FAIL;
+ return hr;
+}
+
+HRESULT Command_DeleteItem(HNAVITEM hItem)
+{
+ Navigation *navigation;
+ HRESULT hr = Plugin_GetNavigation(&navigation);
+ if (SUCCEEDED(hr))
+ {
+ hr = navigation->DeleteItem(hItem);
+ navigation->Release();
+ }
+
+ return hr;
+}
+
+HRESULT Command_DeleteAll()
+{
+ Navigation *navigation;
+ HRESULT hr = Plugin_GetNavigation(&navigation);
+ if (SUCCEEDED(hr))
+ {
+ hr = navigation->DeleteAll();
+ navigation->Release();
+ }
+
+ return hr;
+}
+
+HRESULT Command_CreateService()
+{
+ Navigation *navigation;
+ HRESULT hr = Plugin_GetNavigation(&navigation);
+ if (FAILED(hr)) return hr;
+
+ HNAVITEM hItem;
+ hr = navigation->CreateUserService(&hItem);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omservice *service;
+ hr= navigation->GetService(hItem, &service);
+ if (SUCCEEDED(hr))
+ {
+ Command_EditService(service);
+ service->Release();
+ }
+ }
+ navigation->Release();
+ return hr;
+}
+
+HRESULT Command_NewWindow(HNAVITEM hItem)
+{
+ Navigation *navigation;
+ HRESULT hr = Plugin_GetNavigation(&navigation);
+ if (SUCCEEDED(hr))
+ {
+ HWND hWindow;
+ HRESULT hr = navigation->CreatePopup(hItem, &hWindow);
+ if (SUCCEEDED(hr))
+ {
+ ShowWindow(hWindow, SW_SHOWNORMAL);
+ }
+ navigation->Release();
+ }
+ return hr;
+}
+
+HRESULT Command_OpenView(HNAVITEM hItem)
+{
+ Navigation *navigation;
+ HRESULT hr = Plugin_GetNavigation(&navigation);
+ if (SUCCEEDED(hr))
+ {
+ hr = navigation->SelectItem(hItem, NULL);
+ navigation->Release();
+ }
+
+ return hr;
+}
+
+void Command_ImportFiles()
+{
+ HWND hOwner = Plugin_GetDialogOwner();
+ ImportService_FromFile(hOwner);
+}
+
+void Command_ImportUrl()
+{
+ HWND hOwner = Plugin_GetDialogOwner();
+ ImportService_FromUrl(hOwner);
+}
+
+static void CALLBACK BrowserOptions_Callback(HWND hOptions, UINT type, ULONG_PTR user)
+{
+ HWND hLibrary = (HWND)user;
+ switch(type)
+ {
+ case BOCALLBACK_INIT:
+ {
+ HWND hView = (HWND)SENDMLIPC(hLibrary, ML_IPC_GETCURRENTVIEW, 0);
+ if (NULL != hView)
+ {
+ RECT viewRect, optionsRect;
+ if (GetWindowRect(hView, &viewRect) && GetWindowRect(hOptions, &optionsRect))
+ {
+ INT x = viewRect.left + ((viewRect.right - viewRect.left) - (optionsRect.right - optionsRect.left))/2;
+ INT y = viewRect.top + ((viewRect.bottom - viewRect.top) - (optionsRect.bottom - optionsRect.top))/2;
+ SetWindowPos(hOptions, NULL, x, y, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
+ SendMessage(hOptions, DM_REPOSITION, 0, 0L);
+ }
+ }
+ }
+ break;
+ }
+}
+
+HRESULT Command_ShowBrowserOptions()
+{
+ HWND hWinamp = Plugin_GetWinamp();
+ if (NULL == hWinamp || NULL == OMBROWSERMNGR)
+ return E_UNEXPECTED;
+
+ HRESULT hr = OMBROWSERMNGR->Initialize(NULL, hWinamp);
+ if (SUCCEEDED(hr))
+ {
+ HWND hOwner = Plugin_GetDialogOwner();
+ hr = OMBROWSERMNGR->ShowOptions(hOwner, BOSTYLE_NORMAL | BOSTYLE_SHOWDEBUG,
+ BrowserOptions_Callback, (ULONG_PTR)hOwner);
+ }
+ return hr;
+}
+
+BOOL CommandManager_Process(HNAVITEM hItem, ifc_omservice *service, UINT commandId)
+{
+ switch(commandId)
+ {
+ case ID_VIEW_OPEN: Command_OpenView(hItem); return TRUE;
+ case ID_VIEW_NEWWINDOW: Command_NewWindow(hItem); return TRUE;
+ case ID_SERVICE_NEW: Command_CreateService(); return TRUE;
+ case ID_SERVICE_DELETE: Command_DeleteItem(hItem); return TRUE;
+ case ID_SERVICE_DELETEALL: Command_DeleteAll(); return TRUE;
+ case ID_SERVICE_EDIT: Command_EditService(service); return TRUE;
+ case ID_SERVICE_LOCATE: Command_LocateService(service); return TRUE;
+ case ID_SERVICE_EDITEXTERNAL: Command_EditServiceExternal(service); return TRUE;
+ case ID_SERVICE_RELOAD: Command_ReloadService(service); return TRUE;
+ case ID_SERVICE_RESETPERMISSIONS: Command_ResetPermissions(service); return TRUE;
+ case ID_NAVIGATION_BACK: Command_NavigateService(service, NAVIGATE_BACK, TRUE); return TRUE;
+ case ID_NAVIGATION_FORWARD: Command_NavigateService(service, NAVIGATE_FORWARD, TRUE); return TRUE;
+ case ID_NAVIGATION_HOME: Command_NavigateService(service, NAVIGATE_HOME, TRUE); return TRUE;
+ case ID_NAVIGATION_STOP: Command_NavigateService(service, NAVIGATE_STOP, TRUE); return TRUE;
+ case ID_NAVIGATION_REFRESH: Command_NavigateService(service, NAVIGATE_REFRESH, TRUE); return TRUE;
+ case ID_OMBROWSER_OPTIONS: Command_ShowBrowserOptions(); return TRUE;
+ case ID_SERVICE_IMPORT_FILE: Command_ImportFiles(); return TRUE;
+ case ID_SERVICE_IMPORT_URL: Command_ImportUrl(); return TRUE;
+ }
+ return FALSE;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/commands.h b/Src/Plugins/Library/ml_webdev/commands.h
new file mode 100644
index 00000000..e5e93ae7
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/commands.h
@@ -0,0 +1,29 @@
+#ifndef NULLSOFT_WEBDEV_PLUGIN_COMMANDS_HEADER
+#define NULLSOFT_WEBDEV_PLUGIN_COMMANDS_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+typedef LPVOID HNAVITEM;
+class ifc_omservice;
+
+HRESULT Command_NavigateService(ifc_omservice *service, LPCWSTR pszUrl, BOOL fActiveOnly);
+HRESULT Command_PostNavigateSvc(ifc_omservice *service, LPCWSTR pszUrl, BOOL fActiveOnly);
+HRESULT Command_EditService(ifc_omservice *service);
+HRESULT Command_ReloadService(ifc_omservice *service);
+HRESULT Command_ResetPermissions(ifc_omservice *service);
+HRESULT Command_LocateService(ifc_omservice *service);
+HRESULT Command_EditServiceExternal(ifc_omservice *service);
+HRESULT Command_DeleteItem(HNAVITEM hItem);
+HRESULT Command_DeleteAll();
+HRESULT Command_CreateService(void);
+HRESULT Command_OpenView(HNAVITEM hItem);
+HRESULT Command_NewWindow(HNAVITEM hItem);
+HRESULT Command_ShowBrowserOptions(void);
+
+BOOL CommandManager_Process(HNAVITEM hItem, ifc_omservice *service, UINT commandId);
+
+#endif //NULLSOFT_WEBDEV_PLUGIN_COMMANDS_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/common.cpp b/Src/Plugins/Library/ml_webdev/common.cpp
new file mode 100644
index 00000000..5d7aabf5
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/common.cpp
@@ -0,0 +1,238 @@
+#include "./common.h"
+#include "./wasabi.h"
+#include "./main.h"
+
+#include "../winamp/wa_ipc.h"
+
+#include <strsafe.h>
+
+LPWSTR Plugin_MallocString(size_t cchLen)
+{
+ return (LPWSTR)malloc(sizeof(WCHAR) * cchLen);
+}
+
+void Plugin_FreeString(LPWSTR pszString)
+{
+ if (NULL != pszString)
+ {
+ free(pszString);
+ }
+}
+
+LPWSTR Plugin_ReAllocString(LPWSTR pszString, size_t cchLen)
+{
+ return (LPWSTR)realloc(pszString, sizeof(WCHAR) * cchLen);
+}
+
+LPWSTR Plugin_CopyString(LPCWSTR pszSource)
+{
+ if (NULL == pszSource)
+ return NULL;
+
+ INT cchSource = lstrlenW(pszSource) + 1;
+
+ LPWSTR copy = Plugin_MallocString(cchSource);
+ if (NULL != copy)
+ {
+ CopyMemory(copy, pszSource, sizeof(WCHAR) * cchSource);
+ }
+ return copy;
+}
+
+LPSTR Plugin_MallocAnsiString(size_t cchLen)
+{
+ return (LPSTR)malloc(sizeof(CHAR) * cchLen);
+}
+
+LPSTR Plugin_CopyAnsiString(LPCSTR pszSource)
+{
+ if (NULL == pszSource)
+ return NULL;
+
+ INT cchSource = lstrlenA(pszSource) + 1;
+
+ LPSTR copy = Plugin_MallocAnsiString(cchSource);
+ if (NULL != copy)
+ {
+ CopyMemory(copy, pszSource, sizeof(CHAR) * cchSource);
+ }
+ return copy;
+
+}
+void Plugin_FreeAnsiString(LPSTR pszString)
+{
+ Plugin_FreeString((LPWSTR)pszString);
+}
+
+LPSTR Plugin_WideCharToMultiByte(UINT codePage, DWORD dwFlags, LPCWSTR lpWideCharStr, INT cchWideChar, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar)
+{
+ INT cchBuffer = WideCharToMultiByte(codePage, dwFlags, lpWideCharStr, cchWideChar, NULL, 0, lpDefaultChar, lpUsedDefaultChar);
+ if (0 == cchBuffer) return NULL;
+
+ LPSTR buffer = Plugin_MallocAnsiString(cchBuffer);
+ if (NULL == buffer) return NULL;
+
+ if (0 == WideCharToMultiByte(codePage, dwFlags, lpWideCharStr, cchWideChar, buffer, cchBuffer, lpDefaultChar, lpUsedDefaultChar))
+ {
+ Plugin_FreeAnsiString(buffer);
+ return NULL;
+ }
+ return buffer;
+}
+
+LPWSTR Plugin_MultiByteToWideChar(UINT codePage, DWORD dwFlags, LPCSTR lpMultiByteStr, INT cbMultiByte)
+{
+ if (NULL == lpMultiByteStr) return NULL;
+ INT cchBuffer = MultiByteToWideChar(codePage, dwFlags, lpMultiByteStr, cbMultiByte, NULL, 0);
+ if (NULL == cchBuffer) return NULL;
+
+ if (cbMultiByte > 0) cchBuffer++;
+
+ LPWSTR buffer = Plugin_MallocString(cchBuffer);
+ if (NULL == buffer) return NULL;
+
+ if (0 == MultiByteToWideChar(codePage, dwFlags, lpMultiByteStr, cbMultiByte, buffer, cchBuffer))
+ {
+ Plugin_FreeString(buffer);
+ return NULL;
+ }
+
+ if (cbMultiByte > 0)
+ {
+ buffer[cchBuffer - 1] = L'\0';
+ }
+ return buffer;
+}
+
+
+LPWSTR Plugin_DuplicateResString(LPCWSTR pszResource)
+{
+ return (IS_INTRESOURCE(pszResource)) ?
+ (LPWSTR)pszResource :
+ Plugin_CopyString(pszResource);
+}
+
+void Plugin_FreeResString(LPWSTR pszResource)
+{
+ if (!IS_INTRESOURCE(pszResource))
+ Plugin_FreeString(pszResource);
+}
+
+HRESULT Plugin_CopyResString(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszString)
+{
+ if (NULL == pszBuffer)
+ return E_INVALIDARG;
+
+ HRESULT hr = S_OK;
+
+ if (NULL == pszString)
+ {
+ pszBuffer[0] = L'\0';
+ }
+ else if (IS_INTRESOURCE(pszString))
+ {
+ if (NULL == WASABI_API_LNG)
+ hr = E_FAIL;
+ else
+ WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)pszString, pszBuffer, cchBufferMax);
+ }
+ else
+ {
+ hr = StringCchCopy(pszBuffer, cchBufferMax, pszString);
+ }
+ return hr;
+}
+
+void Plugin_SafeRelease(IUnknown *pUnk)
+{
+ if (NULL != pUnk)
+ pUnk->Release();
+}
+
+HRESULT Plugin_MakeResourcePath(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszType, LPCWSTR pszName, UINT flags)
+{
+ HINSTANCE hInstance = WASABI_API_LNG_HINST;
+ if (NULL == hInstance || NULL == FindResource(hInstance, pszName, pszType))
+ hInstance = Plugin_GetInstance();
+
+ if (NULL == OMUTILITY)
+ return E_UNEXPECTED;
+
+ return OMUTILITY->MakeResourcePath(pszBuffer, cchBufferMax, hInstance, pszType, pszName, flags);
+}
+
+HWND Plugin_GetDialogOwner(void)
+{
+ HWND hOwner= Plugin_GetLibrary();
+ if (NULL == hOwner || FALSE == IsWindowVisible(hOwner) ||
+ FALSE == IsWindowEnabled(hOwner))
+ {
+ hOwner = Plugin_GetWinamp();
+ if (NULL != hOwner)
+ {
+ HWND hDlgParent = (HWND)SENDWAIPC(hOwner, IPC_GETDIALOGBOXPARENT, 0L);
+ if (NULL != hDlgParent)
+ hOwner = hDlgParent;
+ }
+ }
+ return hOwner;
+}
+
+HRESULT Plugin_AppendFileFilter(LPTSTR pszBuffer, size_t cchBufferMax, LPCTSTR pName, LPCTSTR pFilter, LPTSTR *ppBufferOut, size_t *pRemaining, BOOL bShowFilter)
+{
+ HRESULT hr;
+
+ LPTSTR pCursor = pszBuffer;
+
+ if (NULL != ppBufferOut)
+ *ppBufferOut = pszBuffer;
+
+ if (NULL != pRemaining)
+ *pRemaining = cchBufferMax;
+
+ if (NULL == pszBuffer || NULL == pName || NULL == pFilter)
+ return E_INVALIDARG;
+
+ pszBuffer[0] = TEXT('\0');
+
+ hr = StringCchCopyEx(pCursor, cchBufferMax, pName, &pCursor, &cchBufferMax,
+ STRSAFE_IGNORE_NULLS | STRSAFE_NULL_ON_FAILURE);
+ if (bShowFilter && SUCCEEDED(hr))
+ {
+ LPTSTR p = pCursor;
+ hr = StringCchPrintfEx(pCursor, cchBufferMax, &pCursor, &cchBufferMax,
+ STRSAFE_IGNORE_NULLS | STRSAFE_NULL_ON_FAILURE, TEXT(" (%s)"), pFilter);
+ if (SUCCEEDED(hr) && p != pCursor)
+ CharLowerBuff(p, (INT)(INT_PTR)(pCursor - p));
+
+ }
+ if (SUCCEEDED(hr))
+ {
+ pCursor++;
+ cchBufferMax--;
+ hr = StringCchCopyEx(pCursor, cchBufferMax, pFilter, &pCursor, &cchBufferMax,
+ STRSAFE_IGNORE_NULLS | STRSAFE_NULL_ON_FAILURE);
+ }
+
+ if (cchBufferMax < 1)
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+
+ pCursor++;
+ cchBufferMax--;
+
+ if (SUCCEEDED(hr))
+ {
+ pCursor[0] = TEXT('\0');
+ if (NULL != ppBufferOut)
+ *ppBufferOut = pCursor;
+ if (NULL != pRemaining)
+ *pRemaining = cchBufferMax;
+ }
+ else
+ {
+ pszBuffer[0] = TEXT('\0');
+ pszBuffer[1] = TEXT('\0');
+ }
+
+ return hr;
+}
diff --git a/Src/Plugins/Library/ml_webdev/common.h b/Src/Plugins/Library/ml_webdev/common.h
new file mode 100644
index 00000000..7b52d156
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/common.h
@@ -0,0 +1,68 @@
+#ifndef NULLSOFT_WEBDEV_PLUGIN_COMMON_HEADER
+#define NULLSOFT_WEBDEV_PLUGIN_COMMON_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include "../nu/trace.h"
+
+#define CSTR_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(blah) (sizeof(blah)/sizeof(*blah))
+#endif
+
+#ifndef LONGX86
+#ifdef _WIN64
+ #define LONGX86 LONG_PTR
+#else /*_WIN64*/
+ #define LONGX86 LONG
+#endif /*_WIN64*/
+#endif // LONGX86
+
+#ifdef __cplusplus
+ #define SENDMSG(__hwnd, __msgId, __wParam, __lParam) ::SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam))
+#else
+ #define SENDMSG(__hwnd, __msgId, __wParam, __lParam) SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam))
+#endif // __cplusplus
+
+#define SENDMLIPC(__hwndML, __ipcMsgId, __param) SENDMSG((__hwndML), WM_ML_IPC, (WPARAM)(__param), (LPARAM)(__ipcMsgId))
+#define SENDWAIPC(__hwndWA, __ipcMsgId, __param) SENDMSG((__hwndWA), WM_WA_IPC, (WPARAM)(__param), (LPARAM)(__ipcMsgId))
+
+#define MSGRESULT(__hwnd, __result) { SetWindowLongPtrW((__hwnd), DWLP_MSGRESULT, ((LONGX86)(LONG_PTR)(__result))); return TRUE; }
+
+#define SENDCMD(__hwnd, __ctrlId, __eventId, __hctrl) (SENDMSG((__hwnd), WM_COMMAND, MAKEWPARAM(__ctrlId, __eventId), (LPARAM)(__hctrl)))
+
+#ifndef GetWindowStyle
+#define GetWindowStyle(__hwnd) ((UINT)GetWindowLongPtr((__hwnd), GWL_STYLE))
+#endif //GetWindowStyle
+
+#ifndef GetWindowStyleEx
+#define GetWindowStyleEx(__hwnd) ((UINT)GetWindowLongPtr((__hwnd), GWL_EXSTYLE))
+#endif // GetWindowStyleEx
+
+LPWSTR Plugin_MallocString(size_t cchLen);
+LPWSTR Plugin_ReAllocString(LPWSTR pszString, size_t cchLen);
+void Plugin_FreeString(LPWSTR pszString);
+LPWSTR Plugin_CopyString(LPCWSTR pszSource);
+
+LPSTR Plugin_MallocAnsiString(size_t cchLen);
+LPSTR Plugin_CopyAnsiString(LPCSTR pszSource);
+void Plugin_FreeAnsiString(LPSTR pszString);
+
+LPWSTR Plugin_DuplicateResString(LPCWSTR pszResource);
+void Plugin_FreeResString(LPWSTR pszResource);
+HRESULT Plugin_CopyResString(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszString);
+
+LPSTR Plugin_WideCharToMultiByte(UINT codePage, DWORD dwFlags, LPCWSTR lpWideCharStr, INT cchWideChar, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar);
+LPWSTR Plugin_MultiByteToWideChar(UINT codePage, DWORD dwFlags, LPCSTR lpMultiByteStr, INT cbMultiByte);
+
+void Plugin_SafeRelease(IUnknown *pUnk);
+HRESULT Plugin_MakeResourcePath(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszType, LPCWSTR pszName, UINT uFlags);
+
+HWND Plugin_GetDialogOwner(void);
+HRESULT Plugin_AppendFileFilter(LPTSTR pszBuffer, size_t cchBufferMax, LPCTSTR pName, LPCTSTR pFilter, LPTSTR *ppBufferOut, size_t *pRemaining, BOOL bShowFilter);
+
+#endif //NULLSOFT_WEBDEV_PLUGIN_COMMON_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/config.cpp b/Src/Plugins/Library/ml_webdev/config.cpp
new file mode 100644
index 00000000..6a81bb6c
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/config.cpp
@@ -0,0 +1,74 @@
+#include "main.h"
+#include "./config.h"
+#include "./wasabi.h"
+
+#include <shlwapi.h>
+#include <strsafe.h>
+
+#define CONFIG_SUFFIX L"Plugins\\webDev"
+
+static LPCSTR Config_GetPath()
+{
+ static LPSTR configPath = NULL;
+ if (NULL == configPath)
+ {
+ LPCWSTR p = (NULL != WASABI_API_APP) ? WASABI_API_APP->path_getUserSettingsPath() : NULL;
+ if (NULL != p)
+ {
+ WCHAR szBuffer[MAX_PATH * 2] = {0};
+ if (0 != PathCombine(szBuffer, p, CONFIG_SUFFIX))
+ {
+ OMUTILITY->EnsurePathExist(szBuffer);
+ PathAppend(szBuffer, L"config.ini");
+ configPath = Plugin_WideCharToMultiByte(CP_UTF8, 0, szBuffer, -1, NULL, NULL);
+ }
+ }
+ }
+
+ return configPath;
+}
+
+DWORD Config_ReadStr(LPCSTR lpSectionName, LPCSTR lpKeyName, LPCSTR lpDefault, LPSTR lpReturnedString, DWORD nSize)
+{
+ return GetPrivateProfileStringA(lpSectionName, lpKeyName, lpDefault, lpReturnedString, nSize, Config_GetPath());
+}
+
+UINT Config_ReadInt(LPCSTR lpSectionName, LPCSTR lpKeyName, INT nDefault)
+{
+ return GetPrivateProfileIntA(lpSectionName, lpKeyName, nDefault, Config_GetPath());
+}
+
+HRESULT Config_WriteStr(LPCSTR lpSectionName, LPCSTR lpKeyName, LPCSTR lpString)
+{
+ LPCSTR configPath = Config_GetPath();
+ if (NULL == configPath || '\0' == *configPath)
+ return E_UNEXPECTED;
+
+ if (0 != WritePrivateProfileStringA(lpSectionName, lpKeyName, lpString, configPath))
+ return S_OK;
+
+ DWORD errorCode = GetLastError();
+ return HRESULT_FROM_WIN32(errorCode);
+}
+
+HRESULT Config_WriteInt(LPCSTR lpSectionName, LPCSTR lpKeyName, INT nValue)
+{
+ char szBuffer[32];
+ HRESULT hr = StringCchPrintfA(szBuffer, ARRAYSIZE(szBuffer), "%d", nValue);
+ if (FAILED(hr)) return hr;
+
+ return Config_WriteStr(lpSectionName, lpKeyName, szBuffer);
+}
+
+HRESULT Config_WriteSection(LPCSTR lpSectionName, LPCSTR lpData)
+{
+ LPCSTR configPath = Config_GetPath();
+ if (NULL == configPath || '\0' == *configPath)
+ return E_UNEXPECTED;
+
+ if (0 == WritePrivateProfileSectionA(lpSectionName, lpData, configPath))
+ return S_OK;
+
+ DWORD errorCode = GetLastError();
+ return HRESULT_FROM_WIN32(errorCode);
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/config.h b/Src/Plugins/Library/ml_webdev/config.h
new file mode 100644
index 00000000..24f5816c
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/config.h
@@ -0,0 +1,18 @@
+#ifndef NULLSOFT_WEBDEV_PLUGIN_CONFIG_HEADER
+#define NULLSOFT_WEBDEV_PLUGIN_CONFIG_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+DWORD Config_ReadStr(LPCSTR lpSectionName, LPCSTR lpKeyName, LPCSTR lpDefault, LPSTR lpReturnedString, DWORD nSize);
+UINT Config_ReadInt(LPCSTR lpSectionName, LPCSTR lpKeyName, INT nDefault);
+HRESULT Config_WriteStr(LPCSTR lpSectionName, LPCSTR lpKeyName, LPCSTR lpString);
+HRESULT Config_WriteInt(LPCSTR lpSectionName, LPCSTR lpKeyName, INT nValue);
+HRESULT Config_WriteSection(LPCSTR lpSectionName, LPCSTR lpData);
+
+
+
+#endif //NULLSOFT_WEBDEV_PLUGIN_CONFIG_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/external.cpp b/Src/Plugins/Library/ml_webdev/external.cpp
new file mode 100644
index 00000000..30837747
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/external.cpp
@@ -0,0 +1,272 @@
+#include "main.h"
+#include "./resource.h"
+#include "./external.h"
+#include "./navigation.h"
+#include "./commands.h"
+
+#include "../winamp/jsapi.h"
+#include "../winamp/jsapi_CallbackParameters.h"
+
+#include "./serviceHelper.h"
+
+#include <browserView.h>
+#include <ifc_omservice.h>
+#include <ifc_omserviceeditor.h>
+
+#define DISPTABLE_CLASS ExternalDispatch
+
+DISPTABLE_BEGIN()
+ DISPENTRY_ADD(DISPATCH_SERVICE_OPEN, L"serviceOpen", OnServiceOpen)
+ DISPENTRY_ADD(DISPATCH_SERVICE_CREATE, L"serviceCreate", OnServiceCreate)
+ DISPENTRY_ADD(DISPATCH_SERVICE_GETINFO, L"serviceGetInfo", OnServiceGetInfo)
+ DISPENTRY_ADD(DISPATCH_SERVICE_SETINFO, L"serviceSetInfo", OnServiceSetInfo)
+DISPTABLE_END
+
+#undef DISPTABLE_CLASS
+
+
+static BOOL DispParam_GetStringOpt(LPCWSTR *str, DISPPARAMS *paramInfo, UINT paramNumber, UINT *argErr)
+{
+ if (paramInfo->cArgs < paramNumber ||
+ VT_NULL == paramInfo->rgvarg[paramInfo->cArgs - paramNumber].vt)
+ {
+ *str = NULL;
+ return FALSE;
+ }
+
+ JSAPI_GETSTRING((*str), paramInfo, paramNumber, argErr);
+ return TRUE;
+}
+
+ExternalDispatch::ExternalDispatch()
+ : ref(1)
+{
+}
+
+ExternalDispatch::~ExternalDispatch()
+{
+}
+
+HRESULT ExternalDispatch::CreateInstance(ExternalDispatch **instance)
+{
+ if (NULL == instance) return E_POINTER;
+
+ *instance = new ExternalDispatch();
+ if (NULL == *instance) return E_OUTOFMEMORY;
+
+ return S_OK;
+}
+
+LPCWSTR ExternalDispatch::GetName()
+{
+ return L"WebDev";
+}
+
+ULONG ExternalDispatch::AddRef(void)
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+
+ULONG ExternalDispatch::Release(void)
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+STDMETHODIMP ExternalDispatch::QueryInterface(REFIID riid, void **ppvObject)
+{
+ if (NULL == ppvObject) return E_POINTER;
+
+ if (IsEqualIID(riid, IID_IDispatch))
+ *ppvObject = static_cast<IDispatch*>(this);
+ else if (IsEqualIID(riid, IID_IUnknown))
+ *ppvObject = static_cast<IUnknown*>(this);
+ else
+ {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+}
+
+HRESULT ExternalDispatch::OnServiceOpen(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr)
+{
+ JSAPI_VERIFY_METHOD(wFlags);
+ JSAPI_VERIFY_PARAMCOUNT_OPTIONAL(pdispparams, 1, 2);
+
+ JSAPI_INIT_RESULT(pvarResult, VT_BOOL);
+
+ UINT serviceId;
+ JSAPI_GETUNSIGNED_AS_NUMBER(serviceId, pdispparams, 1, puArgErr);
+
+ LPCWSTR forceUrl = NULL;
+ if (pdispparams->cArgs > 1)
+ {
+ switch(pdispparams->rgvarg[0].vt)
+ {
+ case VT_BSTR: forceUrl = pdispparams->rgvarg[0].bstrVal; break;
+ case VT_I4: forceUrl = MAKEINTRESOURCE(pdispparams->rgvarg[0].lVal); break;
+ }
+ }
+
+ if (FALSE == DispParam_GetStringOpt(&forceUrl, pdispparams,2, puArgErr))
+ forceUrl = NULL;
+
+ HRESULT hr;
+
+ Navigation *navigation;
+ hr = Plugin_GetNavigation(&navigation);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omservice *service;
+ if (NULL != navigation->FindService(serviceId, &service))
+ {
+ hr = Command_NavigateService(service, forceUrl, FALSE);
+ service->Release();
+ }
+ else
+ {
+ hr = E_FAIL;
+ }
+ navigation->Release();
+ }
+
+ JSAPI_SET_RESULT(pvarResult, boolVal, (SUCCEEDED(hr) ? VARIANT_TRUE : VARIANT_FALSE));
+
+ return S_OK;
+}
+
+HRESULT ExternalDispatch::OnServiceCreate(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr)
+{
+ JSAPI_VERIFY_METHOD(wFlags);
+ JSAPI_VERIFY_PARAMCOUNT(pdispparams, 0);
+
+ HRESULT hr = Command_CreateService();
+
+ JSAPI_INIT_RESULT(pvarResult, VT_BOOL);
+ JSAPI_SET_RESULT(pvarResult, boolVal, (SUCCEEDED(hr) ? VARIANT_TRUE : VARIANT_FALSE));
+
+ return S_OK;
+}
+
+HRESULT ExternalDispatch::OnServiceGetInfo(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr)
+{
+ JSAPI_VERIFY_METHOD(wFlags);
+ JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1);
+
+ JSAPI_INIT_RESULT(pvarResult, VT_DISPATCH);
+
+ UINT serviceId;
+ JSAPI_GETUNSIGNED_AS_NUMBER(serviceId, pdispparams, 1, puArgErr);
+
+ Navigation *navigation;
+ HRESULT hr = Plugin_GetNavigation(&navigation);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omservice *service;
+ if (NULL != navigation->FindService(serviceId, &service))
+ {
+ WCHAR szBuffer[2048];
+ JSAPI::CallbackParameters *params = new JSAPI::CallbackParameters;
+ params->AddLong(L"id", service->GetId());
+
+ if (FAILED(service->GetName(szBuffer, ARRAYSIZE(szBuffer)))) szBuffer[0] = L'\0';
+ params->AddString(L"name", szBuffer);
+
+ if (FAILED(service->GetUrl(szBuffer, ARRAYSIZE(szBuffer)))) szBuffer[0] = L'\0';
+ params->AddString(L"url", szBuffer);
+
+ if (FAILED(service->GetIcon(szBuffer, ARRAYSIZE(szBuffer)))) szBuffer[0] = L'\0';
+ params->AddString(L"icon", szBuffer);
+
+ params->AddBoolean(L"preauthorized", (S_OK == ServiceHelper_IsPreAuthorized(service)));
+
+ service->Release();
+ V_DISPATCH(pvarResult) = params;
+ }
+ else
+ {
+ hr = E_FAIL;
+ }
+ navigation->Release();
+ }
+
+ if (FAILED(hr))
+ {
+ V_DISPATCH(pvarResult) = 0;
+ }
+
+ return S_OK;
+}
+
+HRESULT ExternalDispatch::OnServiceSetInfo(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr)
+{
+ JSAPI_VERIFY_METHOD(wFlags);
+ JSAPI_VERIFY_PARAMCOUNT_OPTIONAL(pdispparams, 2, 5);
+
+ UINT serviceId;
+ JSAPI_GETUNSIGNED_AS_NUMBER(serviceId, pdispparams, 1, puArgErr);
+
+ Navigation *navigation;
+ HRESULT hr = Plugin_GetNavigation(&navigation);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omservice *service;
+ HNAVITEM hItem = navigation->FindService(serviceId, &service);
+ if (NULL != hItem)
+ {
+ ifc_omserviceeditor *editor;
+ hr = service->QueryInterface(IFC_OmServiceEditor, (void**)&editor);
+ if (SUCCEEDED(hr))
+ {
+ LPCWSTR value = NULL;
+
+ editor->BeginUpdate();
+
+ if (FALSE != DispParam_GetStringOpt(&value, pdispparams, 2, puArgErr) && FAILED(editor->SetName(value, FALSE)))
+ hr = E_FAIL;
+
+ if (FALSE != DispParam_GetStringOpt(&value, pdispparams, 3, puArgErr) && FAILED(ServiceHelper_UpdateIcon(editor, value)))
+ hr = E_FAIL;
+
+ if (FALSE != DispParam_GetStringOpt(&value, pdispparams, 4, puArgErr) && FAILED(editor->SetUrl(value, FALSE)))
+ hr = E_FAIL;
+
+ VARIANT_BOOL authorized = JSAPI_PARAM_OPTIONAL(pdispparams, 5, boolVal, VARIANT_FALSE);
+ if (S_OK == editor->SetFlags((VARIANT_TRUE == authorized) ? WDSVCF_PREAUTHORIZED : 0, WDSVCF_PREAUTHORIZED))
+ {
+ if (VARIANT_TRUE == authorized)
+ Command_ResetPermissions(service);
+ }
+
+ editor->EndUpdate();
+ editor->Release();
+ }
+
+ if(SUCCEEDED(hr))
+ hr = ServiceHelper_Save(service);
+
+ service->Release();
+ }
+ else
+ {
+ hr = E_FAIL;
+ }
+
+ navigation->Release();
+ }
+
+ JSAPI_INIT_RESULT(pvarResult, VT_BOOL);
+ JSAPI_SET_RESULT(pvarResult, boolVal, (SUCCEEDED(hr) ? VARIANT_TRUE : VARIANT_FALSE));
+
+ return S_OK;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/external.h b/Src/Plugins/Library/ml_webdev/external.h
new file mode 100644
index 00000000..e3dfe6ca
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/external.h
@@ -0,0 +1,49 @@
+#ifndef NULLSOFT_WEBDEV_PLUGIN_EXTERNAL_HEADER
+#define NULLSOFT_WEBDEV_PLUGIN_EXTERNAL_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include "../nu/dispatchTable.h"
+
+class ExternalDispatch : public IDispatch
+{
+
+public:
+ typedef enum
+ {
+ DISPATCH_SERVICE_OPEN = 700,
+ DISPATCH_SERVICE_CREATE = 701,
+ DISPATCH_SERVICE_GETINFO = 702,
+ DISPATCH_SERVICE_SETINFO = 703,
+ } DispatchCodes;
+
+protected:
+ ExternalDispatch();
+ ~ExternalDispatch();
+
+public:
+ static HRESULT CreateInstance(ExternalDispatch **instance);
+ static LPCWSTR GetName();
+
+public:
+ STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject);
+ STDMETHOD_(ULONG, AddRef)(void);
+ STDMETHOD_(ULONG, Release)(void);
+
+protected:
+ DISPTABLE_INCLUDE();
+ DISPHANDLER_REGISTER(OnServiceOpen);
+ DISPHANDLER_REGISTER(OnServiceCreate);
+ DISPHANDLER_REGISTER(OnServiceGetInfo);
+ DISPHANDLER_REGISTER(OnServiceSetInfo);
+
+protected:
+ ULONG ref;
+
+};
+
+
+#endif //NULLSOFT_WEBDEV_PLUGIN_EXTERNAL_HEADER
diff --git a/Src/Plugins/Library/ml_webdev/forceUrl.cpp b/Src/Plugins/Library/ml_webdev/forceUrl.cpp
new file mode 100644
index 00000000..90b55742
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/forceUrl.cpp
@@ -0,0 +1,77 @@
+#include "main.h"
+#include "./forceUrl.h"
+
+typedef struct __FORCEURLDATA
+{
+ UINT serviceId;
+ LPWSTR url;
+} FORCEURLDATA;
+
+#define FORCEURLPROP L"MLWEBDEV_FORCEURL"
+
+void ForceUrl_Remove()
+{
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary) return;
+
+ FORCEURLDATA *data = (FORCEURLDATA*)GetProp(hLibrary, FORCEURLPROP);
+ RemoveProp(hLibrary, FORCEURLPROP);
+ if (NULL != data)
+ {
+ Plugin_FreeString(data->url);
+ free(data);
+ }
+}
+
+HRESULT ForceUrl_Set(UINT serviceId, LPCWSTR pszUrl)
+{
+ if (NULL == pszUrl) return E_INVALIDARG;
+
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary) return E_FAIL;
+
+ FORCEURLDATA *data = (FORCEURLDATA*)GetProp(hLibrary, FORCEURLPROP);
+
+ if (NULL != data)
+ {
+ Plugin_FreeString(data->url);
+ if (data->serviceId != serviceId)
+ {
+ free(data);
+ data = NULL;
+ }
+ }
+
+ if (NULL == data)
+ {
+ data = (FORCEURLDATA*)malloc(sizeof(FORCEURLDATA));
+ if (NULL == data) return E_OUTOFMEMORY;
+ data->serviceId = serviceId;
+ }
+
+ data->url = Plugin_CopyString(pszUrl);
+ if (NULL == data->url || FALSE == SetProp(hLibrary, FORCEURLPROP, data))
+ {
+ ForceUrl_Remove();
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+HRESULT ForceUrl_Get(UINT serviceId, const wchar_t **ppszUrl)
+{
+ if (NULL == ppszUrl) return E_POINTER;
+ *ppszUrl = NULL;
+
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary) return E_FAIL;
+
+ FORCEURLDATA *data = (FORCEURLDATA*)GetProp(hLibrary, FORCEURLPROP);
+
+ if (NULL == data || data->serviceId != serviceId)
+ return E_NOINTERFACE;
+
+ *ppszUrl = data->url;
+ return S_OK;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/forceUrl.h b/Src/Plugins/Library/ml_webdev/forceUrl.h
new file mode 100644
index 00000000..75b25aaf
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/forceUrl.h
@@ -0,0 +1,15 @@
+#ifndef NULLSOFT_WEBDEV_PLUGIN_FORCE_URL_HEADER
+#define NULLSOFT_WEBDEV_PLUGIN_FORCE_URL_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+
+#include <wtypes.h>
+
+HRESULT ForceUrl_Set(UINT serviceId, LPCWSTR pszUrl);
+HRESULT ForceUrl_Get(UINT serviceId, const wchar_t **ppszUrl);
+void ForceUrl_Remove();
+
+#endif // NULLSOFT_WEBDEV_PLUGIN_FORCE_URL_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/import.h b/Src/Plugins/Library/ml_webdev/import.h
new file mode 100644
index 00000000..b1c079c7
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/import.h
@@ -0,0 +1,18 @@
+#ifndef NULLSOFT_WEBDEV_PLUGIN_IMPORT_HEADER
+#define NULLSOFT_WEBDEV_PLUGIN_IMPORT_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+HRESULT ImportService_GetFileSupported();
+HRESULT ImportService_GetUrlSupported();
+
+HRESULT ImportService_FromFile(HWND hOwner);
+HRESULT ImportService_FromUrl(HWND hOwner);
+
+void ImportService_SaveRecentUrl();
+
+#endif //NULLSOFT_WEBDEV_PLUGIN_IMPORT_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/importFile.cpp b/Src/Plugins/Library/ml_webdev/importFile.cpp
new file mode 100644
index 00000000..a6e1d654
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/importFile.cpp
@@ -0,0 +1,266 @@
+#include "main.h"
+#include "./import.h"
+#include "./wasabi.h"
+#include "./resource.h"
+#include "./serviceHost.h"
+#include "./serviceHelper.h"
+#include "./navigation.h"
+
+#include <ifc_omstorage.h>
+#include <ifc_omfilestorage.h>
+#include <ifc_omstorageenum.h>
+#include <ifc_omservice.h>
+#include <ifc_omserviceenum.h>
+
+#include <strsafe.h>
+
+static HRESULT ImportFile_GetEnumerator(ifc_omstorageenumerator **enumerator)
+{
+ if (NULL == OMSERVICEMNGR) return E_UNEXPECTED;
+ return OMSERVICEMNGR->EnumStorage(&STID_OmFileStorage, ifc_omstorage::capPublic | ifc_omstorage::capLoad, enumerator);
+}
+
+HRESULT ImportService_GetFileSupported()
+{
+ if (NULL == OMSERVICEMNGR)
+ return E_UNEXPECTED;
+
+ ifc_omstorageenumerator *enumerator;
+ HRESULT hr = ImportFile_GetEnumerator(&enumerator);
+
+ if (SUCCEEDED(hr))
+ {
+ ifc_omstorage *storage;
+ if(S_OK == enumerator->Next(1, &storage, NULL))
+ {
+ storage->Release();
+ hr = S_OK;
+ }
+ else
+ {
+ hr = S_FALSE;
+ }
+
+ enumerator->Release();
+ }
+ return hr;
+}
+
+static HRESULT ImportFile_GetFilter(LPWSTR pszBuffer, UINT cchBufferMax, DWORD *defaultIndex)
+{
+ if (NULL != defaultIndex)
+ *defaultIndex = 0;
+
+ if (NULL == pszBuffer)
+ return E_POINTER;
+
+ HRESULT hr;
+ WCHAR szName[128] = {0}, szList[512] = {0};
+
+ LPWSTR cursor = pszBuffer;
+ size_t remaining = cchBufferMax;
+
+ LPWSTR listC = szList;
+ size_t listR = ARRAYSIZE(szList);
+
+ DWORD counter = 0;
+
+ szList[0] = L'\0';
+ pszBuffer[0] = L'\0';
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_FILEFILTER_ALL, szName, ARRAYSIZE(szName));
+ hr = Plugin_AppendFileFilter(cursor, remaining, szName, L"*.*", &cursor, &remaining, TRUE);
+ if (FAILED(hr)) return hr;
+ counter++;
+
+
+ ifc_omstorageenumerator *enumerator;
+ hr = ImportFile_GetEnumerator(&enumerator);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omstorage *storage;
+ ifc_omfilestorage *fileStorage;
+ while(S_OK == enumerator->Next(1, &storage, NULL))
+ {
+ if (SUCCEEDED(storage->QueryInterface(IFC_OmFileStorage, (void**)&fileStorage)))
+ {
+ WCHAR szFilter[64] = {0};
+ if (SUCCEEDED(fileStorage->GetFilter(szFilter, ARRAYSIZE(szFilter))) &&
+ L'\0' != szFilter[0] &&
+ SUCCEEDED(storage->GetDescription(szName, ARRAYSIZE(szName))))
+ {
+ hr = Plugin_AppendFileFilter(cursor, remaining, szName, szFilter, &cursor, &remaining, TRUE);
+ if (FAILED(hr)) break;
+
+ counter++;
+
+ if (listC == szList || SUCCEEDED(StringCchCopyEx(listC, listR, L";", &listC, &listR, 0)))
+ StringCchCopyEx(listC, listR, szFilter, &listC, &listR, 0);
+ }
+ fileStorage->Release();
+ }
+ storage->Release();
+ }
+ enumerator->Release();
+ }
+
+ if (SUCCEEDED(hr) && L'\0' != szList[0])
+ {
+ WASABI_API_LNGSTRINGW_BUF(IDS_FILEFILTER_ALLKNOWN, szName, ARRAYSIZE(szName));
+ hr = Plugin_AppendFileFilter(cursor, remaining, szName, szList, &cursor, &remaining, TRUE);
+ if (FAILED(hr)) return hr;
+
+ counter++;
+
+ if (NULL != defaultIndex)
+ *defaultIndex = counter;
+ }
+
+ return hr;
+}
+static HRESULT ImportFile_ProcessFile(HWND hOwner, ifc_omstorageenumerator *enumerator,
+ ifc_omservicehost *serviceHost, ifc_omstorage *serviceStorage, LPCWSTR pszFile, ULONG *converted)
+{
+ ifc_omstorage *storage;
+ enumerator->Reset();
+
+ Navigation *navigation;
+ if (FAILED(Plugin_GetNavigation(&navigation)))
+ return E_FAIL;
+
+ ULONG loaded(0), saved(0);
+ while(S_OK == enumerator->Next(1, &storage, NULL))
+ {
+ ifc_omserviceenum *serviceEnum;
+ HRESULT hr = storage->Load(pszFile, serviceHost, &serviceEnum);
+ if(SUCCEEDED(hr))
+ {
+ ifc_omservice *service;
+ while(S_OK == serviceEnum->Next(1, &service, NULL))
+ {
+ loaded++;
+ if (SUCCEEDED(service->SetAddress(NULL)))
+ {
+ ULONG savedOk;
+ if (SUCCEEDED(serviceStorage->Save(&service, 1, ifc_omstorage::saveClearModified, &savedOk)))
+ {
+ navigation->CreateItem(service);
+ saved += savedOk;
+ }
+ }
+ service->Release();
+ }
+ serviceEnum->Release();
+ break;
+ }
+ else if (OMSTORAGE_E_UNKNOWN_FORMAT != hr)
+ {
+ break;
+ }
+
+ storage->Release();
+ }
+
+ if (NULL != converted)
+ *converted = saved;
+
+ navigation->Release();
+
+ return S_OK;
+}
+
+
+static HRESULT ImportFile_ProcessList(HWND hOwner, LPCWSTR pszList)
+{
+ if (NULL == pszList)
+ return E_INVALIDARG;
+
+ LPCWSTR base, block, c;
+ base = pszList;
+ c = base;
+ block = NULL;
+ ULONG converted;
+
+ HRESULT hr;
+ ifc_omstorageenumerator *enumerator;
+ hr = ImportFile_GetEnumerator(&enumerator);
+ if (FAILED(hr)) return hr;
+
+ WebDevServiceHost *serviceHost;
+ hr = WebDevServiceHost::GetCachedInstance(&serviceHost);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omstorage *serviceStorage;
+ hr = ServiceHelper_QueryStorage(&serviceStorage);
+ if (SUCCEEDED(hr))
+ {
+ ULONG scanned(0);
+ while(L'\0' != *c)
+ {
+ block = c;
+ while (L'\0' != *c) c++;
+ if (c != block && block != base)
+ {
+ WCHAR szBuffer[MAX_PATH * 2] = {0};
+ scanned++;
+ if (SUCCEEDED(StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), L"%s\\%s", base, block)) &&
+ SUCCEEDED(ImportFile_ProcessFile(hOwner, enumerator, serviceHost, serviceStorage, szBuffer, &converted)))
+ {
+ }
+ }
+ c++;
+ }
+
+ if (pszList == block && c != pszList &&
+ SUCCEEDED(ImportFile_ProcessFile(hOwner, enumerator, serviceHost, serviceStorage, pszList, &converted)))
+ {
+ }
+ serviceStorage->Release();
+ }
+ serviceHost->Release();
+ }
+
+ enumerator->Release();
+ return hr;
+}
+
+HRESULT ImportService_FromFile(HWND hOwner)
+{
+ if (NULL == OMSERVICEMNGR)
+ return E_UNEXPECTED;
+
+ HRESULT hr;
+ OPENFILENAME of;
+ ZeroMemory(&of, sizeof(of));
+ of.lStructSize = sizeof(of);
+
+ WCHAR szFilter[1024];
+ hr = ImportFile_GetFilter(szFilter, ARRAYSIZE(szFilter), &of.nFilterIndex);
+ if (FAILED(hr)) return hr;
+
+ UINT cchResultMax = 16384;
+ LPWSTR pszResult = Plugin_MallocString(cchResultMax);
+ if (NULL == pszResult) return E_OUTOFMEMORY;
+ *pszResult = L'\0';
+
+ of.hwndOwner = hOwner;
+ of.lpstrFilter = szFilter;
+ of.lpstrFile = pszResult;
+ of.nMaxFile = cchResultMax;
+ of.lpstrInitialDir = WASABI_API_APP->path_getUserSettingsPath();
+ of.lpstrTitle = WASABI_API_LNGSTRINGW(IDS_IMPORT_FILES);
+ of.Flags = OFN_ENABLESIZING | OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_ALLOWMULTISELECT;
+
+ if (0 == GetOpenFileName(&of))
+ {
+ INT err = CommDlgExtendedError();
+ hr = (0 == err) ? S_FALSE : E_FAIL;
+ }
+ else
+ {
+ hr = ImportFile_ProcessList(hOwner, pszResult);
+ }
+
+ Plugin_FreeString(pszResult);
+ return hr;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/importUrl.cpp b/Src/Plugins/Library/ml_webdev/importUrl.cpp
new file mode 100644
index 00000000..2ab584e8
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/importUrl.cpp
@@ -0,0 +1,320 @@
+#include "main.h"
+#include "./import.h"
+#include "./wasabi.h"
+#include "./serviceHost.h"
+#include "./serviceHelper.h"
+#include "./navigation.h"
+#include "./resource.h"
+#include "./config.h"
+
+#include <ifc_omstorage.h>
+#include <ifc_omwebstorage.h>
+#include <ifc_omstorageenum.h>
+#include <ifc_omservice.h>
+#include <ifc_omserviceenum.h>
+
+#include <strsafe.h>
+
+
+typedef std::vector<WCHAR*> StringList;
+
+static StringList recentList;
+static BOOL recentListModified = FALSE;
+
+typedef struct __OPENURLDLG
+{
+ HWND hOwner;
+ LPCWSTR pszAddress;
+ LPWSTR pszBuffer;
+ UINT cchBufferMax;
+} OPENURLDLG;
+
+static INT_PTR ImportUrlDlg_Show(OPENURLDLG *poud);
+
+static HRESULT ImportUrl_GetEnumerator(ifc_omstorageenumerator **enumerator)
+{
+ if (NULL == OMSERVICEMNGR) return E_UNEXPECTED;
+ return OMSERVICEMNGR->EnumStorage(&STID_OmWebStorage, ifc_omstorage::capPublic | ifc_omstorage::capLoad, enumerator);
+}
+
+HRESULT ImportService_GetUrlSupported()
+{
+ if (NULL == OMSERVICEMNGR)
+ return E_UNEXPECTED;
+
+ ifc_omstorageenumerator *enumerator;
+ HRESULT hr = ImportUrl_GetEnumerator(&enumerator);
+
+ if (SUCCEEDED(hr))
+ {
+ ifc_omstorage *storage;
+ if(S_OK == enumerator->Next(1, &storage, NULL))
+ {
+ storage->Release();
+ hr = S_OK;
+ }
+ else
+ {
+ hr = S_FALSE;
+ }
+
+ enumerator->Release();
+ }
+ return hr;
+}
+
+HRESULT ImportUrl_LoadAddress(HWND hOwner, LPCWSTR pszAddress, ifc_omstorageenumerator *enumerator,
+ WebDevServiceHost *serviceHost, ifc_omstorage *serviceStorage, Navigation *navigation)
+{
+ HRESULT hr = S_OK;
+ ULONG loaded(0);
+
+ ifc_omstorage *storage;
+ enumerator->Reset();
+
+ while(S_OK == enumerator->Next(1, &storage, NULL))
+ {
+ ifc_omstorageasync *async;
+ hr = storage->BeginLoad(pszAddress, serviceHost, NULL, NULL, &async);
+ if(SUCCEEDED(hr))
+ {
+ ifc_omserviceenum *serviceEnum;
+ hr = storage->EndLoad(async, &serviceEnum);
+ async->Release();
+
+ if (SUCCEEDED(hr))
+ {
+ ifc_omservice *service;
+ while(S_OK == serviceEnum->Next(1, &service, NULL))
+ {
+ loaded++;
+ if (SUCCEEDED(service->SetAddress(NULL)))
+ {
+ ULONG savedOk;
+ if (SUCCEEDED(serviceStorage->Save(&service, 1, ifc_omstorage::saveClearModified, &savedOk)))
+ {
+ navigation->CreateItem(service);
+ }
+ }
+ service->Release();
+ }
+ serviceEnum->Release();
+ }
+ break;
+ }
+ else if (OMSTORAGE_E_UNKNOWN_FORMAT != hr)
+ {
+ break;
+ }
+ storage->Release();
+ }
+
+ return hr;
+}
+
+HRESULT ImportService_FromUrl(HWND hOwner)
+{
+ OPENURLDLG dlg = {0};
+ WCHAR szBuffer[4096] = {0};
+
+ dlg.hOwner = hOwner;
+ dlg.pszAddress = NULL;
+ dlg.pszBuffer = szBuffer;
+ dlg.cchBufferMax = ARRAYSIZE(szBuffer);
+ if (IDOK != ImportUrlDlg_Show(&dlg))
+ return S_FALSE;
+
+ ifc_omstorageenumerator *enumerator;
+ HRESULT hr = ImportUrl_GetEnumerator(&enumerator);
+ if (SUCCEEDED(hr))
+ {
+ Navigation *navigation;
+ hr = Plugin_GetNavigation(&navigation);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omstorage *serviceStorage;
+ hr = ServiceHelper_QueryStorage(&serviceStorage);
+ if (SUCCEEDED(hr))
+ {
+ WebDevServiceHost *serviceHost;
+ hr = WebDevServiceHost::GetCachedInstance(&serviceHost);
+ if (SUCCEEDED(hr))
+ {
+ hr = ImportUrl_LoadAddress(hOwner, szBuffer, enumerator, serviceHost, serviceStorage, navigation);
+ serviceHost->Release();
+ }
+ serviceStorage->Release();
+ }
+ navigation->Release();
+ }
+ enumerator->Release();
+ }
+ return hr;
+}
+
+static INT_PTR ImportUrlDlg_OnInit(HWND hwnd, HWND hFocus, LPARAM param)
+{
+ OPENURLDLG *poud = (OPENURLDLG*)param;
+ if (NULL != poud)
+ {
+ SetProp(hwnd, L"OPENURLDLG", poud);
+
+ HWND hAddress = GetDlgItem(hwnd, IDC_ADDRESS);
+ if (NULL != hAddress)
+ {
+ LPWSTR p;
+
+ size_t count = recentList.size();
+ if (0 == count)
+ {
+ char szKey[32] = {0}, szBuffer[4096] = {0};
+ for(int i = 1; i < 101; i++)
+ {
+ if (FAILED(StringCchPrintfA(szKey, ARRAYSIZE(szKey), "entry%d", i)) ||
+ 0 == Config_ReadStr("RecentUrl", szKey, NULL, szBuffer, ARRAYSIZE(szBuffer)) ||
+ '\0' == szBuffer[0])
+ {
+ break;
+ }
+
+ p = Plugin_MultiByteToWideChar(CP_UTF8, 0, szBuffer, -1);
+ if (NULL != p) recentList.push_back(p);
+ }
+ count = recentList.size();
+ }
+
+ for (size_t i = 0; i < count; i ++)
+ {
+ p = recentList[i];
+ if(NULL != p && L'\0' != *p)
+ SendMessage(hAddress, CB_ADDSTRING, 0, (LPARAM)p);
+ }
+
+ if (NULL != poud->pszAddress)
+ SetWindowText(hAddress, poud->pszAddress);
+ }
+
+ RECT ownerRect;
+ if (NULL != poud->hOwner && IsWindowVisible(poud->hOwner) &&
+ GetWindowRect(poud->hOwner, &ownerRect))
+ {
+ RECT myRect;
+ GetWindowRect(hwnd, &myRect);
+ LONG x = ownerRect.left + ((ownerRect.right - ownerRect.left) - (myRect.right - myRect.left))/2;
+ LONG y = ownerRect.top + ((ownerRect.bottom - ownerRect.top) - (myRect.bottom - myRect.top))/2;
+ SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
+ }
+ }
+
+ return 0;
+}
+
+static void ImportUrlDlg_OnDestroy(HWND hwnd)
+{
+ RemoveProp(hwnd, L"OPENURLDLG");
+
+}
+static INT_PTR ImportUrlDlg_ReturnAddress(HWND hwnd)
+{
+ OPENURLDLG *poud = (OPENURLDLG*)GetProp(hwnd, L"OPENURLDLG");
+ if (NULL == poud) return -1;
+
+ HWND hAddress = GetDlgItem(hwnd, IDC_ADDRESS);
+ if (NULL == hAddress) return -2;
+
+ if (0 == GetWindowText(hAddress, poud->pszBuffer, poud->cchBufferMax))
+ return -3;
+
+ if (NULL != poud->pszBuffer && L'\0' != *poud->pszBuffer)
+ {
+ LPWSTR p;
+ size_t index = recentList.size();
+ while(index--)
+ {
+ p = recentList[index];
+ if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, poud->pszBuffer, -1, p, -1))
+ {
+ Plugin_FreeString(p);
+ recentList.erase(recentList.begin() + index);
+ }
+ }
+
+ p = Plugin_CopyString(poud->pszBuffer);
+ if (NULL != p)
+ {
+ //recentList.push_front(p);
+ recentList.insert(recentList.begin(), p);
+ recentListModified = TRUE;
+ }
+ }
+ return IDOK;
+}
+
+static void ImportUrlDlg_OnCommand(HWND hwnd, INT commandId, INT eventId, HWND hControl)
+{
+ switch(commandId)
+ {
+ case IDOK:
+ EndDialog(hwnd, ImportUrlDlg_ReturnAddress(hwnd));
+ break;
+ case IDCANCEL:
+ EndDialog(hwnd, IDCANCEL);
+ break;
+ }
+
+}
+static INT_PTR WINAPI ImportUrlDlg_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG: return ImportUrlDlg_OnInit(hwnd, (HWND)wParam, lParam);
+ case WM_DESTROY: ImportUrlDlg_OnDestroy(hwnd); break;
+ case WM_COMMAND: ImportUrlDlg_OnCommand(hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); break;
+ }
+
+ return 0;
+
+}
+
+
+static INT_PTR ImportUrlDlg_Show(OPENURLDLG *poud)
+{
+ if (NULL == poud || NULL == poud->pszBuffer)
+ return -1;
+
+ HWND hParent = poud->hOwner;
+ if (NULL == poud->hOwner)
+ hParent = Plugin_GetLibrary();
+
+ return WASABI_API_DIALOGBOXPARAMW(IDD_OPENURL, hParent, ImportUrlDlg_DialogProc, (LPARAM)poud);
+
+}
+
+void ImportService_SaveRecentUrl()
+{
+ if (FALSE == recentListModified)
+ return;
+
+ Config_WriteSection("RecentUrl", NULL);
+
+ size_t count = recentList.size();
+ if (count > 100) count = 100;
+
+ char szKey[32], szBuffer[4096];
+ UINT entry = 1;
+
+ for (size_t i = 0; i < count; i++)
+ {
+ LPCWSTR p = recentList[i];
+ if (NULL != p && L'\0' != *p &&
+ 0 != WideCharToMultiByte(CP_UTF8, 0, p, -1, szBuffer, ARRAYSIZE(szBuffer), NULL, NULL) &&
+ SUCCEEDED(StringCchPrintfA(szKey, ARRAYSIZE(szKey), "entry%d", entry)) &&
+ SUCCEEDED(Config_WriteStr("RecentUrl", szKey, szBuffer)))
+ {
+ entry++;
+ }
+ }
+
+ recentListModified = FALSE;
+
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/local_menu.cpp b/Src/Plugins/Library/ml_webdev/local_menu.cpp
new file mode 100644
index 00000000..dd2eb09c
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/local_menu.cpp
@@ -0,0 +1,222 @@
+#include "main.h"
+#include "./local_menu.h"
+#include "./wasabi.h"
+#include "./resource.h"
+#include "./navigation.h"
+#include "./import.h"
+
+#include "../../General/gen_ml/ml_ipc_0313.h"
+#include "../nu/menuHelpers.h"
+#include "./ombrowser/browserView.h"
+#include "./ombrowser/toolbar.h"
+
+#include "./serviceHelper.h"
+#include <ifc_omservice.h>
+
+#define SUBMENU_VIEW 0
+#define SUBMENU_SERVICEMNGR 1
+#define SUBMENU_NAVIGATION 2
+#define SUBMENU_PLUGIN 3
+
+static INT Menu_AddViewContext(HMENU destMenu, HMENU baseMenu, BOOL viewActive)
+{
+ if (NULL == destMenu) return 0;
+
+ HMENU sorceMenu = GetSubMenu(baseMenu, SUBMENU_VIEW);
+ if (NULL == sorceMenu) return 0;
+
+ INT c = GetMenuItemCount(destMenu);
+ c = MenuHelper_CopyMenu(destMenu, c, sorceMenu);
+
+
+ if (FALSE != viewActive)
+ {
+ EnableMenuItem(destMenu, ID_VIEW_OPEN, MF_BYCOMMAND | MF_GRAYED | MF_DISABLED);
+ }
+ else
+ {
+ EnableMenuItem(destMenu, ID_VIEW_OPEN, MF_BYCOMMAND | MF_ENABLED);
+ SetMenuDefaultItem(destMenu, ID_VIEW_OPEN, FALSE);
+ }
+ return c;
+}
+
+typedef struct __NAVITEMPAIR
+{
+ INT wId;
+ LPCSTR toolItem;
+} NAVITEMPAIR;
+
+static INT Menu_AddNavigationContext(HMENU destMenu, HMENU baseMenu)
+{
+ if (NULL == destMenu) return 0;
+
+ HMENU sorceMenu = GetSubMenu(baseMenu, SUBMENU_NAVIGATION);
+ if (NULL == sorceMenu) return 0;
+
+ INT c = GetMenuItemCount(destMenu);
+ c = MenuHelper_CopyMenu(destMenu, c, sorceMenu);
+ if (0 == c) return c;
+
+ HWND hView = NULL;
+
+ Navigation *navigation;
+ if (SUCCEEDED(Plugin_GetNavigation(&navigation)))
+ {
+ hView = navigation->GetActiveView(NULL);
+ navigation->Release();
+ }
+
+ HWND hToolbar = (NULL != hView) ? BrowserView_GetToolbar(hView) : NULL;
+ if (NULL != hToolbar)
+ {
+ NAVITEMPAIR szItems[] =
+ {
+ {ID_NAVIGATION_BACK, TOOLITEM_BUTTON_BACK},
+ {ID_NAVIGATION_FORWARD, TOOLITEM_BUTTON_FORWARD},
+ {ID_NAVIGATION_HOME, TOOLITEM_BUTTON_HOME},
+ {ID_NAVIGATION_STOP, TOOLITEM_BUTTON_STOP},
+ {ID_NAVIGATION_REFRESH, TOOLITEM_BUTTON_REFRESH},
+ };
+
+ for (INT i = 0; i < ARRAYSIZE(szItems); i++)
+ {
+ INT index = Toolbar_FindItem(hToolbar, szItems[i].toolItem);
+ if (-1 != index)
+ {
+ BOOL fDisabled = Toolbar_GetItemStyle(hToolbar, MAKEINTRESOURCE(index), TBIS_DISABLED);
+ EnableMenuItem(destMenu, szItems[i].wId, MF_BYCOMMAND | (fDisabled) ? (MF_DISABLED | MF_GRAYED) : (MF_ENABLED));
+ }
+ }
+ }
+
+ return c;
+}
+
+static INT Menu_AddServiceContext(HMENU destMenu, HMENU baseMenu, ifc_omservice *service)
+{
+ if (NULL == destMenu || NULL == service) return 0;
+
+ HMENU sorceMenu = GetSubMenu(baseMenu, SUBMENU_SERVICEMNGR);
+ if (NULL == sorceMenu) return 0;
+
+ INT origCount = GetMenuItemCount(destMenu);
+ INT c = MenuHelper_CopyMenu(destMenu, origCount, sorceMenu);
+ UINT serviceFlags;
+ if (FAILED(service->GetFlags(&serviceFlags))) serviceFlags = 0;
+
+ if (0 != ((WDSVCF_ROOT | WDSVCF_SPECIAL) & serviceFlags))
+ {
+ UINT szDisabled[] = {ID_SERVICE_EDIT, ID_SERVICE_DELETE, ID_SERVICE_RELOAD,
+ ID_SERVICE_LOCATE, ID_SERVICE_EDITEXTERNAL };
+
+ for (INT i = 0; i < ARRAYSIZE(szDisabled); i++)
+ EnableMenuItem(destMenu, szDisabled[i], MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ }
+
+ UINT uEnable;
+
+ uEnable = (S_OK == ImportService_GetFileSupported()) ? MF_ENABLED : (MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(destMenu, ID_SERVICE_IMPORT_FILE, MF_BYCOMMAND | uEnable);
+
+ uEnable = (S_OK == ImportService_GetUrlSupported()) ? MF_ENABLED : (MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(destMenu, ID_SERVICE_IMPORT_URL, MF_BYCOMMAND | uEnable);
+
+ return c;
+}
+
+static INT Menu_AddPluginContext(HMENU destMenu, HMENU baseMenu, ifc_omservice *service)
+{
+ UINT serviceFlags;
+ if (NULL == service || FAILED(service->GetFlags(&serviceFlags)))
+ serviceFlags = 0;
+
+ if (NULL == destMenu || NULL == service ||
+ 0 == (WDSVCF_ROOT & serviceFlags))
+ {
+ return 0;
+ }
+
+ HMENU sorceMenu = GetSubMenu(baseMenu, SUBMENU_PLUGIN);
+ if (NULL == sorceMenu) return 0;
+
+ INT origCount = GetMenuItemCount(destMenu);
+ INT c = MenuHelper_CopyMenu(destMenu, origCount, sorceMenu);
+
+ return c;
+}
+
+static HMENU Menu_GetServiceContext(HMENU baseMenu, ifc_omservice *service)
+{
+ HMENU hMenu = CreatePopupMenu();
+ if(NULL == hMenu) return NULL;
+
+ ifc_omservice *activeService = NULL;
+ Navigation *navigation;
+ if (SUCCEEDED(Plugin_GetNavigation(&navigation)))
+ {
+ navigation->GetActive(&activeService);
+ navigation->Release();
+ }
+
+ BOOL fActive = (NULL != service && activeService == service);
+
+ INT c, total;
+ total = Menu_AddViewContext(hMenu, baseMenu, fActive);
+ if (FALSE != fActive)
+ {
+ c = Menu_AddNavigationContext(hMenu, baseMenu);
+ if (0 != c && total > 0 && 0 != MenuHelper_InsertSeparator(hMenu, total))
+ total++;
+
+ total += c;
+ }
+
+ if (NULL != service)
+ {
+ c = Menu_AddServiceContext(hMenu, baseMenu, service);
+ if (0 != c && total > 0 && 0 != MenuHelper_InsertSeparator(hMenu, total))
+ total++;
+
+ total += c;
+
+ c = Menu_AddPluginContext(hMenu, baseMenu, service);
+ if (0 != c && total > 0 && 0 != MenuHelper_InsertSeparator(hMenu, total))
+ total++;
+
+ total += c;
+ }
+
+
+ if (NULL != activeService)
+ activeService->Release();
+
+ return hMenu;
+}
+
+HMENU Menu_GetMenu(UINT menuKind, ifc_omservice *service)
+{
+ HMENU baseMenu = WASABI_API_LOADMENUW(IDR_CONTEXTMENU);
+ if (NULL == baseMenu)
+ return NULL;
+
+ switch(menuKind)
+ {
+ case MENU_SERVICECONTEXT:
+ return Menu_GetServiceContext(baseMenu, service);
+ }
+
+ return NULL;
+}
+
+BOOL Menu_ReleaseMenu(HMENU hMenu, UINT menuKind)
+{
+ if (NULL == hMenu) return FALSE;
+
+ switch(menuKind)
+ {
+ case MENU_SERVICECONTEXT:
+ return DestroyMenu(hMenu);
+ }
+ return FALSE;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/local_menu.h b/Src/Plugins/Library/ml_webdev/local_menu.h
new file mode 100644
index 00000000..50df5ca3
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/local_menu.h
@@ -0,0 +1,17 @@
+#ifndef NULLSOFT_WEBDEV_PLUGIN_MENU_HEADER
+#define NULLSOFT_WEBDEV_PLUGIN_MENU_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+class ifc_omservice;
+
+#define MENU_SERVICECONTEXT 0
+
+HMENU Menu_GetMenu(UINT menuKind, ifc_omservice *service);
+BOOL Menu_ReleaseMenu(HMENU hMenu, UINT menuKind);
+
+#endif //NULLSOFT_WEBDEV_PLUGIN_MENU_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/main.cpp b/Src/Plugins/Library/ml_webdev/main.cpp
new file mode 100644
index 00000000..344a49aa
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/main.cpp
@@ -0,0 +1,167 @@
+#include "main.h"
+#include "./navigation.h"
+#include "./wasabi.h"
+#include "./resource.h"
+#include "./external.h"
+#include "./serviceHost.h"
+#include "./import.h"
+#include "../winamp/wa_ipc.h"
+
+
+#include <initguid.h>
+#include <strsafe.h>
+
+static DWORD externalCookie = 0;
+static Navigation *navigation = NULL;
+
+// {BF4F80A7-7470-4b08-8A4C-34382C146202}
+DEFINE_GUID(WebDevLangUid, 0xbf4f80a7, 0x7470, 0x4b08, 0x8a, 0x4c, 0x34, 0x38, 0x2c, 0x14, 0x62, 0x2);
+
+static int Plugin_Init();
+static void Plugin_Quit();
+static INT_PTR Plugin_MessageProc(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3);
+
+extern "C" winampMediaLibraryPlugin plugin =
+{
+ MLHDR_VER,
+ "nullsoft(ml_webdev.dll)",
+ Plugin_Init,
+ Plugin_Quit,
+ Plugin_MessageProc,
+ 0,
+ 0,
+ 0,
+};
+
+HINSTANCE Plugin_GetInstance(void)
+{
+ return plugin.hDllInstance;
+}
+
+HWND Plugin_GetWinamp(void)
+{
+ return plugin.hwndWinampParent;
+}
+
+HWND Plugin_GetLibrary(void)
+{
+ return plugin.hwndLibraryParent;
+}
+
+HRESULT Plugin_GetExternal(ExternalDispatch **ppExternal)
+{
+ if (NULL == ppExternal) return E_POINTER;
+ HRESULT hr = ExternalDispatch::CreateInstance(ppExternal);
+ return hr;
+}
+
+static int Plugin_Init()
+{
+ if (!WasabiApi_Initialize(Plugin_GetInstance()))
+ return 1;
+
+ if (NULL == OMBROWSERMNGR ||
+ NULL == OMSERVICEMNGR ||
+ NULL == OMUTILITY )
+ {
+ return 2;
+ }
+
+ if (NULL != WASABI_API_LNG)
+ {
+ static wchar_t szDescription[256];
+ StringCchPrintf(szDescription, ARRAYSIZE(szDescription),
+ WASABI_API_LNGSTRINGW(IDS_PLUGIN_NAME),
+ PLUGIN_VERSION_MAJOR, PLUGIN_VERSION_MINOR);
+ plugin.description = (char*)szDescription;
+ }
+
+ ExternalDispatch *externalDispatch;
+ if (SUCCEEDED(ExternalDispatch::CreateInstance(&externalDispatch)))
+ {
+ DispatchInfo dispatchInfo;
+ dispatchInfo.id = 0;
+ dispatchInfo.name =(LPWSTR)externalDispatch->GetName();
+ dispatchInfo.dispatch = externalDispatch;
+
+ if (0 == SENDWAIPC(Plugin_GetWinamp(), IPC_ADD_DISPATCH_OBJECT, (WPARAM)&dispatchInfo))
+ externalCookie = dispatchInfo.id;
+
+ externalDispatch->Release();
+ }
+
+ if (NULL == navigation)
+ {
+ if (FAILED(Navigation::CreateInstance(&navigation)))
+ {
+ navigation = NULL;
+
+ if (0 != externalCookie)
+ {
+ HWND hWinamp = Plugin_GetWinamp();
+ SENDWAIPC(hWinamp, IPC_REMOVE_DISPATCH_OBJECT, (WPARAM)externalCookie);
+ externalCookie = 0;
+ }
+
+ return 2;
+ }
+ }
+
+ return 0;
+}
+
+static void Plugin_Quit()
+{
+ if (0 != externalCookie)
+ {
+ HWND hWinamp = Plugin_GetWinamp();
+ SENDWAIPC(hWinamp, IPC_REMOVE_DISPATCH_OBJECT, (WPARAM)externalCookie);
+ externalCookie = 0;
+ }
+
+ WebDevServiceHost::ReleseCache();
+
+ if (NULL != navigation)
+ {
+ navigation->Finish();
+ navigation->Release();
+ navigation = NULL;
+ }
+
+ ImportService_SaveRecentUrl();
+
+ WasabiApi_Release();
+}
+
+static INT_PTR Plugin_MessageProc(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3)
+{
+ INT_PTR result = 0;
+ if (NULL != navigation &&
+ FALSE != navigation->ProcessMessage(msg, param1, param2, param3, &result))
+ {
+ return result;
+ }
+
+ return FALSE;
+}
+
+HRESULT Plugin_GetNavigation(Navigation **instance)
+{
+ if(NULL == instance) return E_POINTER;
+
+ if (NULL == navigation)
+ {
+ *instance = NULL;
+ return E_UNEXPECTED;
+ }
+
+ *instance = navigation;
+ navigation->AddRef();
+
+ return S_OK;
+}
+
+EXTERN_C __declspec(dllexport) winampMediaLibraryPlugin *winampGetMediaLibraryPlugin()
+{
+ return &plugin;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/main.h b/Src/Plugins/Library/ml_webdev/main.h
new file mode 100644
index 00000000..7762ec9c
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/main.h
@@ -0,0 +1,25 @@
+#ifndef NULLSOFT_WEBDEV_PLUGIN_MAIN_HEADER
+#define NULLSOFT_WEBDEV_PLUGIN_MAIN_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include "../../General/gen_ml/ml.h"
+#include "./common.h"
+
+#define PLUGIN_VERSION_MAJOR 2
+#define PLUGIN_VERSION_MINOR 34
+
+// {BF4F80A7-7470-4b08-8A4C-34382C146202}
+DEFINE_GUID(WebDevLangUid, 0xbf4f80a7, 0x7470, 0x4b08, 0x8a, 0x4c, 0x34, 0x38, 0x2c, 0x14, 0x62, 0x2);
+
+HINSTANCE Plugin_GetInstance(void);
+HWND Plugin_GetWinamp(void);
+HWND Plugin_GetLibrary(void);
+
+class Navigation;
+HRESULT Plugin_GetNavigation(Navigation **instance);
+
+#endif //NULLSOFT_WEBDEV_PLUGIN_MAIN_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/ml_webdev.rc b/Src/Plugins/Library/ml_webdev/ml_webdev.rc
new file mode 100644
index 00000000..37589a2c
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/ml_webdev.rc
@@ -0,0 +1,182 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "#include ""version.rc2""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_CONTEXTMENU MENU
+BEGIN
+ POPUP "ViewContext"
+ BEGIN
+ MENUITEM "&Open", ID_VIEW_OPEN
+ MENUITEM "Open in &New Window", ID_VIEW_NEWWINDOW
+ END
+ POPUP "ServiceMngrContext"
+ BEGIN
+ POPUP "&Service"
+ BEGIN
+ MENUITEM "&New", ID_SERVICE_NEW
+ POPUP "&Import"
+ BEGIN
+ MENUITEM "&File...", ID_SERVICE_IMPORT_FILE
+ MENUITEM "&Url..", ID_SERVICE_IMPORT_URL
+ END
+ MENUITEM SEPARATOR
+ MENUITEM "&Edit", ID_SERVICE_EDIT
+ MENUITEM "&Reload", ID_SERVICE_RELOAD
+ MENUITEM "Reset &Permissions", ID_SERVICE_RESETPERMISSIONS
+ MENUITEM SEPARATOR
+ MENUITEM "&Delete", ID_SERVICE_DELETE
+ MENUITEM "Delete &All", ID_SERVICE_DELETEALL
+ MENUITEM SEPARATOR
+ MENUITEM "Open Containing &Folder", ID_SERVICE_LOCATE
+ MENUITEM "Open in E&xternal Editor", ID_SERVICE_EDITEXTERNAL
+ END
+ END
+ POPUP "NavigationContext"
+ BEGIN
+ MENUITEM "&Back", ID_NAVIGATION_BACK
+ MENUITEM "&Forward", ID_NAVIGATION_FORWARD
+ MENUITEM "&Home", ID_NAVIGATION_HOME
+ MENUITEM "&Refresh", ID_NAVIGATION_REFRESH
+ MENUITEM "Sto&p", ID_NAVIGATION_STOP
+ END
+ POPUP "PluginContext"
+ BEGIN
+ MENUITEM "omBrowser Options...", ID_OMBROWSER_OPTIONS
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// HTML
+//
+
+IDR_HTML_WELCOME HTML ".\\resources\\pages\\welcome.htm"
+IDR_HTML_EDITOR HTML ".\\resources\\pages\\serviceEditor.htm"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_OPENURL DIALOGEX 0, 0, 220, 54
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Open Url"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ LTEXT "Enter service list address:",IDC_STATIC,5,4,210,10
+ COMBOBOX IDC_ADDRESS,5,18,210,30,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
+ DEFPUSHBUTTON "OK",IDOK,111,36,50,13
+ PUSHBUTTON "Cancel",IDCANCEL,165,36,50,13
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_OPENURL, DIALOG
+ BEGIN
+ LEFTMARGIN, 5
+ RIGHTMARGIN, 215
+ TOPMARGIN, 4
+ BOTTOMMARGIN, 49
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_PLUGIN_NAME "Nullsoft WebDev Test Platform v%d.%d"
+ 65535 "{BF4F80A7-7470-4b08-8A4C-34382C146202}"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_ROOTSERVICE_NAME "WebDev Platform"
+ IDS_DOCSERVICE_NAME "Online Documentation"
+ IDS_TESTSERVICE_NAME "JSAPI2 Test"
+ IDS_USERSERVICE_NAME "My Service"
+ IDS_FILEFILTER_ALL "All Files"
+ IDS_FILEFILTER_ALLKNOWN "All Supported Files"
+ IDS_LOADSERVICE "Load WebDev Service"
+ IDS_UNKNOWNNAME "<Unknown>"
+ IDS_IMPORT_FILES "Import Service..."
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc2"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/Plugins/Library/ml_webdev/ml_webdev.sln b/Src/Plugins/Library/ml_webdev/ml_webdev.sln
new file mode 100644
index 00000000..5702953f
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/ml_webdev.sln
@@ -0,0 +1,30 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29613.14
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ml_webdev", "ml_webdev.vcxproj", "{2DA51094-B28D-4CAC-8C92-8460A6E63504}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2DA51094-B28D-4CAC-8C92-8460A6E63504}.Debug|Win32.ActiveCfg = Debug|Win32
+ {2DA51094-B28D-4CAC-8C92-8460A6E63504}.Debug|Win32.Build.0 = Debug|Win32
+ {2DA51094-B28D-4CAC-8C92-8460A6E63504}.Debug|x64.ActiveCfg = Debug|x64
+ {2DA51094-B28D-4CAC-8C92-8460A6E63504}.Debug|x64.Build.0 = Debug|x64
+ {2DA51094-B28D-4CAC-8C92-8460A6E63504}.Release|Win32.ActiveCfg = Release|Win32
+ {2DA51094-B28D-4CAC-8C92-8460A6E63504}.Release|Win32.Build.0 = Release|Win32
+ {2DA51094-B28D-4CAC-8C92-8460A6E63504}.Release|x64.ActiveCfg = Release|x64
+ {2DA51094-B28D-4CAC-8C92-8460A6E63504}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {B9FEFFB9-5976-4591-B204-3323609B831B}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/Plugins/Library/ml_webdev/ml_webdev.vcxproj b/Src/Plugins/Library/ml_webdev/ml_webdev.vcxproj
new file mode 100644
index 00000000..0d7093e1
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/ml_webdev.vcxproj
@@ -0,0 +1,382 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{2DA51094-B28D-4CAC-8C92-8460A6E63504}</ProjectGuid>
+ <RootNamespace>ml_webdev</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnableManifest>false</VcpkgEnableManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;..\..\..\;..\..\..\wasabi;..\..\..\agave;..\..\..\ombrowser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_WINDOWS;_USRDLL;TEST_EXPORTS;_DEBUG;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4302;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Manifest>
+ <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile>
+ </Manifest>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..;..\..\..\;..\..\..\wasabi;..\..\..\agave;..\..\..\ombrowser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_WINDOWS;_USRDLL;TEST_EXPORTS;_DEBUG;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4302;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Manifest>
+ <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile>
+ </Manifest>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..;..\..\..\;..\..\..\wasabi;..\..\..\agave;..\..\..\ombrowser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_WINDOWS;NDEBUG;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4302;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Manifest>
+ <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile>
+ </Manifest>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..;..\..\..\;..\..\..\wasabi;..\..\..\agave;..\..\..\ombrowser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_WINDOWS;NDEBUG;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4302;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Manifest>
+ <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile>
+ </Manifest>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\General\gen_ml\menu.cpp" />
+ <ClCompile Include="..\..\..\nu\menuHelpers.cpp" />
+ <ClCompile Include="..\..\..\nu\sort.cpp" />
+ <ClCompile Include="..\..\..\nu\trace.cpp" />
+ <ClCompile Include="..\..\..\Winamp\JSAPI_CallbackParameters.cpp" />
+ <ClCompile Include="commands.cpp" />
+ <ClCompile Include="common.cpp" />
+ <ClCompile Include="config.cpp" />
+ <ClCompile Include="external.cpp" />
+ <ClCompile Include="forceUrl.cpp" />
+ <ClCompile Include="importFile.cpp" />
+ <ClCompile Include="importUrl.cpp" />
+ <ClCompile Include="local_menu.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="navigation.cpp" />
+ <ClCompile Include="serviceHelper.cpp" />
+ <ClCompile Include="serviceHost.cpp" />
+ <ClCompile Include="wasabi.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="ml_webdev.rc" />
+ <ResourceCompile Include="png.rc" />
+ <ResourceCompile Include="testPages.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="resource.hi" />
+ <None Include="resources\pages\api2.js" />
+ <None Include="resources\pages\applicationApi.htm">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
+ <None Include="resources\pages\asyncDownloaderApi.htm">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
+ <None Include="resources\pages\bookmarkApi.htm">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
+ <None Include="resources\pages\configApi.htm">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
+ <None Include="resources\pages\historyApi.htm">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
+ <None Include="resources\pages\mediaCoreApi.htm">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
+ <None Include="resources\pages\playlistApi.htm">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
+ <None Include="resources\pages\playQueueApi.htm">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
+ <None Include="resources\pages\podcastApi.htm">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
+ <None Include="resources\pages\securityApi.htm">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
+ <None Include="resources\pages\serviceEditor.htm">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
+ <None Include="resources\pages\skinApi.htm">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
+ <None Include="resources\pages\transportApi.htm">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
+ <None Include="resources\pages\transportEventsApi.htm">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
+ <None Include="resources\pages\webdev.htm">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
+ <None Include="resources\pages\webdev.js" />
+ <None Include="resources\pages\welcome.htm">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\General\gen_ml\menu.h" />
+ <ClInclude Include="commands.h" />
+ <ClInclude Include="common.h" />
+ <ClInclude Include="config.h" />
+ <ClInclude Include="external.h" />
+ <ClInclude Include="forceUrl.h" />
+ <ClInclude Include="import.h" />
+ <ClInclude Include="local_menu.h" />
+ <ClInclude Include="main.h" />
+ <ClInclude Include="navigation.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="serviceHelper.h" />
+ <ClInclude Include="serviceHost.h" />
+ <ClInclude Include="wasabi.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/ml_webdev.vcxproj.filters b/Src/Plugins/Library/ml_webdev/ml_webdev.vcxproj.filters
new file mode 100644
index 00000000..f880b5ce
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/ml_webdev.vcxproj.filters
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="commands.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="common.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="config.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="external.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="forceUrl.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="importFile.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="importUrl.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="local_menu.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="navigation.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wasabi.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="serviceHost.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="serviceHelper.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Winamp\JSAPI_CallbackParameters.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\General\gen_ml\menu.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\menuHelpers.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\sort.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\trace.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="commands.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="common.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="config.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="external.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="forceUrl.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="import.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="local_menu.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="navigation.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="serviceHelper.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="serviceHost.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="wasabi.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\General\gen_ml\menu.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="png.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ <ResourceCompile Include="testPages.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ <ResourceCompile Include="ml_webdev.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="resource.hi">
+ <Filter>Ressource Files</Filter>
+ </None>
+ <None Include="resources\pages\api2.js">
+ <Filter>HTM Files</Filter>
+ </None>
+ <None Include="resources\pages\applicationApi.htm">
+ <Filter>HTM Files</Filter>
+ </None>
+ <None Include="resources\pages\asyncDownloaderApi.htm">
+ <Filter>HTM Files</Filter>
+ </None>
+ <None Include="resources\pages\bookmarkApi.htm">
+ <Filter>HTM Files</Filter>
+ </None>
+ <None Include="resources\pages\configApi.htm">
+ <Filter>HTM Files</Filter>
+ </None>
+ <None Include="resources\pages\historyApi.htm">
+ <Filter>HTM Files</Filter>
+ </None>
+ <None Include="resources\pages\mediaCoreApi.htm">
+ <Filter>HTM Files</Filter>
+ </None>
+ <None Include="resources\pages\playlistApi.htm">
+ <Filter>HTM Files</Filter>
+ </None>
+ <None Include="resources\pages\playQueueApi.htm">
+ <Filter>HTM Files</Filter>
+ </None>
+ <None Include="resources\pages\podcastApi.htm">
+ <Filter>HTM Files</Filter>
+ </None>
+ <None Include="resources\pages\securityApi.htm">
+ <Filter>HTM Files</Filter>
+ </None>
+ <None Include="resources\pages\serviceEditor.htm">
+ <Filter>HTM Files</Filter>
+ </None>
+ <None Include="resources\pages\skinApi.htm">
+ <Filter>HTM Files</Filter>
+ </None>
+ <None Include="resources\pages\transportApi.htm">
+ <Filter>HTM Files</Filter>
+ </None>
+ <None Include="resources\pages\transportEventsApi.htm">
+ <Filter>HTM Files</Filter>
+ </None>
+ <None Include="resources\pages\webdev.htm">
+ <Filter>HTM Files</Filter>
+ </None>
+ <None Include="resources\pages\webdev.js">
+ <Filter>HTM Files</Filter>
+ </None>
+ <None Include="resources\pages\welcome.htm">
+ <Filter>HTM Files</Filter>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{ae54a1c4-5018-4345-b9e9-0721b28bca7e}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{70f7d7d1-2117-46a3-9271-35a7b3817d6b}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{36a28036-6df6-46f8-a5fc-6846e411aa7a}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="HTM Files">
+ <UniqueIdentifier>{db057e45-2170-41cd-85d0-b8250030d0ac}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/navigation.cpp b/Src/Plugins/Library/ml_webdev/navigation.cpp
new file mode 100644
index 00000000..ebbe555f
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/navigation.cpp
@@ -0,0 +1,1261 @@
+#include "main.h"
+#include "./navigation.h"
+#include "./resource.h"
+#include "./wasabi.h"
+#include "./local_menu.h"
+#include "./commands.h"
+#include "./forceUrl.h"
+#include "./config.h"
+
+#include "../omBrowser/browserView.h"
+#include "../winamp/wa_ipc.h"
+
+#include "./serviceHost.h"
+#include "./serviceHelper.h"
+#include <ifc_omservice.h>
+#include <ifc_omserviceeditor.h>
+#include <storageIni.h>
+#include <ifc_omserviceenum.h>
+#include <ifc_mlnavigationhelper.h>
+#include <ifc_omserviceeventmngr.h>
+#include <ifc_ombrowserwndmngr.h>
+#include <ifc_ombrowserwndenum.h>
+
+#define _ML_HEADER_IMPMLEMENT
+#include "../../General/gen_ml/ml_ipc_0313.h"
+#undef _ML_HEADER_IMPMLEMENT
+#include "../../General/gen_ml/menu.h"
+
+#include <vector>
+#include "../nu/sort.h"
+
+#include <shlwapi.h>
+#include <strsafe.h>
+#include <algorithm>
+
+#define NAVITEM_PREFIX L"webdev_svc_"
+#define E_NAVITEM_UNKNOWN E_NOINTERFACE
+
+
+static BOOL Navigation_CheckInvariantName(LPCWSTR pszInvarian)
+{
+ INT cchInvariant = (NULL != pszInvarian) ? lstrlen(pszInvarian) : 0;
+ INT cchPrefix = ARRAYSIZE(NAVITEM_PREFIX) - 1;
+ return (cchInvariant > cchPrefix &&
+ CSTR_EQUAL == CompareString(CSTR_INVARIANT, 0, NAVITEM_PREFIX, cchPrefix, pszInvarian, cchPrefix));
+}
+
+
+Navigation::Navigation()
+ : ref(1), cookie(0), hRoot(NULL), hLibrary(NULL)
+{
+}
+
+Navigation::~Navigation()
+{
+}
+
+HRESULT Navigation::CreateInstance(Navigation **instance)
+{
+ if (NULL == instance) return E_POINTER;
+
+ HRESULT hr;
+
+ Navigation *navigation = new Navigation();
+ if (NULL != navigation)
+ {
+ hr = navigation->Initialize();
+ if (FAILED(hr))
+ {
+ navigation->Release();
+ navigation = NULL;
+ }
+ }
+ else
+ {
+ hr = E_OUTOFMEMORY;
+ }
+
+ *instance = navigation;
+ return hr;
+}
+
+size_t Navigation::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+size_t Navigation::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+int Navigation::QueryInterface(GUID interface_guid, void **object)
+{
+ if (NULL == object) return E_POINTER;
+
+ if (IsEqualIID(interface_guid, IFC_MlNavigationCallback))
+ *object = static_cast<ifc_mlnavigationcallback*>(this);
+ else
+ {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+ if (NULL == *object)
+ return E_UNEXPECTED;
+
+ AddRef();
+ return S_OK;
+}
+
+HRESULT Navigation::Initialize()
+{
+ hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ if (0 == cookie)
+ {
+ ifc_mlnavigationhelper *navHelper;
+ if (NULL != OMUTILITY && SUCCEEDED(OMUTILITY->GetMlNavigationHelper(Plugin_GetLibrary(), &navHelper)))
+ {
+ navHelper->RegisterCallback(this, &cookie);
+ navHelper->Release();
+ }
+ }
+
+ ifc_omservice *service;
+
+ MLNavCtrl_BeginUpdate(hLibrary, NUF_LOCK_TOP);
+
+ if (SUCCEEDED(ServiceHelper_Create(700, MAKEINTRESOURCE(IDS_ROOTSERVICE_NAME),
+ NULL, MAKEINTRESOURCE(IDR_HTML_WELCOME),
+ WDSVCF_ROOT, FALSE, &service)))
+ {
+ hRoot = CreateItemInt(NULL, service);
+ service->Release();
+ }
+
+ if (NULL == hRoot)
+ {
+ MLNavCtrl_EndUpdate(hLibrary);
+ return E_FAIL;
+ }
+
+
+ if (SUCCEEDED(ServiceHelper_Create(701, MAKEINTRESOURCE(IDS_DOCSERVICE_NAME),
+ MAKEINTRESOURCE(IDR_HELP_ICON), L"http://dev.winamp.com/online-service-developers",
+ WDSVCF_SPECIAL, FALSE, &service)))
+ {
+ CreateItemInt(hRoot, service);
+ service->Release();
+ }
+
+ if (SUCCEEDED(ServiceHelper_Create(702, MAKEINTRESOURCE(IDS_TESTSERVICE_NAME),
+ MAKEINTRESOURCE(IDR_GEAR_ICON), MAKEINTRESOURCE(IDR_HTML_TEST),
+ WDSVCF_SPECIAL, FALSE, &service)))
+ {
+ CreateItemInt(hRoot, service);
+ service->Release();
+ }
+
+ ifc_omstorage *storage;
+ if (NULL != OMSERVICEMNGR && SUCCEEDED(OMSERVICEMNGR->QueryStorage(&SUID_OmStorageIni, &storage)))
+ {
+ ifc_omserviceenum *enumerator;
+ WebDevServiceHost *serviceHost;
+ if (FAILED(WebDevServiceHost::GetCachedInstance(&serviceHost)))
+ serviceHost = NULL;
+
+ if (SUCCEEDED(storage->Load(L"*.ini", serviceHost, &enumerator)))
+ {
+ ifc_omservice *service;
+ std::vector<ifc_omservice*> serviceList;
+ while (S_OK == enumerator->Next(1, &service, NULL))
+ {
+ serviceList.push_back(service);
+ }
+ enumerator->Release();
+
+ size_t count = serviceList.size();
+ Order(serviceList);
+ for(size_t i =0; i < count; i++)
+ {
+ service = serviceList[i];
+ CreateItemInt(hRoot, service);
+ service->Release();
+ }
+ }
+ storage->Release();
+
+ if (NULL != serviceHost)
+ serviceHost->Release();
+ }
+
+ MLNavCtrl_EndUpdate(hLibrary);
+
+ return S_OK;
+}
+
+HRESULT Navigation::Finish()
+{
+ ForceUrl_Remove();
+
+ if (0 != cookie)
+ {
+ ifc_mlnavigationhelper *navHelper;
+ if (NULL != OMUTILITY && SUCCEEDED(OMUTILITY->GetMlNavigationHelper(Plugin_GetLibrary(), &navHelper)))
+ {
+ navHelper->UnregisterCallback(cookie);
+ navHelper->Release();
+ }
+ }
+
+ if (NULL != OMBROWSERMNGR)
+ {
+ OMBROWSERMNGR->Finish();
+ }
+
+ return S_OK;
+}
+
+HRESULT Navigation::SaveOrder()
+{
+ if (NULL == hRoot || NULL == hLibrary)
+ return E_UNEXPECTED;
+
+ LPSTR buffer = NULL;
+ INT count = MLNavItem_GetChildrenCount(hLibrary, hRoot);
+ if (count > 0)
+ {
+ size_t bufferMax = 11 * count;
+ buffer = Plugin_MallocAnsiString(bufferMax);
+ if (NULL == buffer) return E_OUTOFMEMORY;
+ *buffer = '\0';
+
+ LPSTR cursor = buffer;
+ size_t remaining = bufferMax;
+
+ NAVITEM item;
+ item.cbSize = sizeof(item);
+ item.mask = NIMF_PARAM;
+ item.hItem = MLNavItem_GetChild(hLibrary, hRoot);
+ while (NULL != item.hItem)
+ {
+ if (FALSE != MLNavItem_GetInfo(hLibrary, &item))
+ {
+ ifc_omservice *service = (ifc_omservice*)item.lParam;
+ if (NULL != service)
+ {
+ UINT serviceFlags;
+ if (SUCCEEDED(service->GetFlags(&serviceFlags)) &&
+ 0 == ((WDSVCF_ROOT | WDSVCF_SPECIAL) & serviceFlags))
+ {
+ if (cursor == buffer ||
+ SUCCEEDED(StringCchCopyExA(cursor, remaining, ";", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE)))
+ {
+ StringCchPrintfExA(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, "%d", service->GetId());
+ }
+ }
+ }
+ }
+ item.hItem = MLNavItem_GetNext(hLibrary, item.hItem);
+ }
+
+ }
+
+ Config_WriteStr("Navigation", "order", buffer);
+ Plugin_FreeAnsiString(buffer);
+
+ return S_OK;
+}
+
+//static int __fastcall Navigation_OrderComparer(const void *elem1, const void *elem2, const void *context)
+//{
+// std::vector<UINT> *orderList = (std::vector<UINT>*)context;
+//
+// UINT serviceId;
+// size_t index1, index2;
+// size_t count = orderList->size();
+//
+// serviceId = (*(ifc_omservice**)elem1)->GetId();
+// for (index1 = 0; index1 < count && serviceId != orderList->at(index1); index1++);
+//
+// serviceId = (*(ifc_omservice**)elem2)->GetId();
+// for (index2 = 0; index2 < count && serviceId != orderList->at(index2); index2++);
+//
+// return (INT)(index1 - index2);
+//}
+class Navigation_OrderComparer
+{
+public:
+ Navigation_OrderComparer(const void* ctx)
+ : context(ctx)
+ {
+ }
+
+ bool operator()(const void* elem1, const void* elem2)
+ {
+ std::vector<UINT>* orderList = (std::vector<UINT>*)context;
+
+ UINT serviceId;
+ size_t index1, index2;
+ size_t count = orderList->size();
+
+ serviceId = ((ifc_omservice*)elem1)->GetId();
+ for (index1 = 0; index1 < count && serviceId != orderList->at(index1); index1++);
+
+ serviceId = ((ifc_omservice*)elem2)->GetId();
+ for (index2 = 0; index2 < count && serviceId != orderList->at(index2); index2++);
+
+ return (INT)(index1 - index2) < 0;
+ }
+
+
+private:
+ const void* context;
+};
+HRESULT Navigation::Order(std::vector<ifc_omservice*> &list)
+{
+ size_t listSize = list.size();
+
+ if (listSize < 2)
+ return S_FALSE;
+
+ //if (NULL == list) return E_INVALIDARG;
+
+ size_t bufferMax = 16384;
+ LPSTR buffer = Plugin_MallocAnsiString(bufferMax);
+ if (NULL == buffer) return E_OUTOFMEMORY;
+
+ UINT len = Config_ReadStr("Navigation", "order", NULL, buffer, (UINT)bufferMax);
+ std::vector<UINT> orderList;
+
+ LPCSTR end = buffer + len;
+ LPCSTR block = buffer;
+ LPCSTR cursor = block;
+ for(;;)
+ {
+ if (cursor == end || ';' == *cursor)
+ {
+ if (block != cursor)
+ {
+ INT serviceId;
+ if (FALSE != StrToIntExA(block, STIF_DEFAULT, &serviceId))
+ orderList.push_back(serviceId);
+ }
+
+ if (cursor == end) break;
+ cursor++;
+ block = cursor;
+ }
+ cursor++;
+ }
+
+
+ if (0 != orderList.size())
+ {
+ //nu::qsort(list, listSize, sizeof(ifc_omservice*), &orderList, Navigation_OrderComparer);
+ std::sort(list.begin(), list.end(), Navigation_OrderComparer(&orderList));
+ }
+
+ Plugin_FreeAnsiString(buffer);
+ return S_OK;
+}
+
+void Navigation::ImageChanged(LPCWSTR pszName, INT index)
+{
+ if (NULL == hRoot || NULL == hLibrary || NULL == pszName)
+ return;
+
+ WCHAR szBuffer[2048] = {0};
+ NAVITEM item = {0};
+ item.cbSize = sizeof(item);
+ item.mask = NIMF_TEXTINVARIANT | NIMF_PARAM;
+ item.pszInvariant = szBuffer;
+ item.cchInvariantMax = ARRAYSIZE(szBuffer);
+
+ item.hItem = hRoot;
+
+ while (NULL != item.hItem)
+ {
+ if (FALSE != MLNavItem_GetInfo(hLibrary, &item) &&
+ FALSE != Navigation_CheckInvariantName(item.pszInvariant))
+ {
+ ifc_omservice *service = (ifc_omservice*)item.lParam;
+ if (NULL != service &&
+ SUCCEEDED(service->GetIcon(szBuffer, ARRAYSIZE(szBuffer))) &&
+ CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, szBuffer, -1, pszName, -1))
+ {
+
+ item.iImage = index;
+ item.iSelectedImage = index;
+ item.mask = NIMF_IMAGE | NIMF_IMAGESEL;
+ MLNavItem_SetInfo(hLibrary, &item);
+ return;
+ }
+ }
+
+ item.hItem = (HNAVITEM)SENDMLIPC(hLibrary,
+ (item.hItem == hRoot) ? ML_IPC_NAVITEM_GETCHILD : ML_IPC_NAVITEM_GETNEXT,
+ (WPARAM)item.hItem);
+
+ }
+}
+
+
+
+BOOL Navigation::ProcessMessage(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3, INT_PTR *result)
+{
+ if (msg == ML_MSG_NO_CONFIG)
+ {
+ *result = TRUE;
+ return TRUE;
+ }
+
+ if (msg < ML_MSG_TREE_BEGIN || msg > ML_MSG_TREE_END)
+ return FALSE;
+
+ HRESULT hr;
+
+ switch(msg)
+ {
+ case ML_MSG_TREE_ONCREATEVIEW:
+ {
+ HWND hView;
+ hr = OnCreateView(GetMessageItem(msg, param1), (HWND)param2, &hView);
+ *result = (SUCCEEDED(hr)) ? (INT_PTR)hView : NULL;
+ }
+ return TRUE;
+
+ case ML_MSG_NAVIGATION_CONTEXTMENU:
+ hr = OnContextMenu(GetMessageItem(msg, param1), (HWND)param2, MAKEPOINTS(param3));
+ *result = SUCCEEDED(hr);
+ return TRUE;
+
+ case ML_MSG_NAVIGATION_ONDELETE:
+ hr = OnDeleteItem(GetMessageItem(msg, param1));
+ *result = SUCCEEDED(hr);
+ return TRUE;
+
+ case ML_MSG_NAVIGATION_ONENDTITLEEDIT:
+ hr = OnEndTitleEdit(GetMessageItem(msg, param1), (LPCWSTR)param2);
+ *result = SUCCEEDED(hr);
+ return TRUE;
+
+ case ML_MSG_TREE_ONKEYDOWN:
+ hr = OnKeyDown(GetMessageItem(msg, param1), (NMTVKEYDOWN*)param2);
+ *result = SUCCEEDED(hr);
+ return TRUE;
+
+ case ML_MSG_NAVIGATION_ONDESTROY:
+ OnControlDestroy();
+ *result = 0;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+HNAVITEM Navigation::GetActive(ifc_omservice **serviceOut)
+{
+ ifc_omservice *service;
+ HNAVITEM hActive = (NULL != hLibrary) ? MLNavCtrl_GetSelection(hLibrary) : NULL;
+ if (NULL == hActive || FAILED(GetService(hActive, &service)))
+ {
+ hActive = NULL;
+ service = NULL;
+ }
+
+ if (NULL != serviceOut)
+ *serviceOut = service;
+ else if (NULL != service)
+ service->Release();
+
+ return hActive;
+}
+
+HWND Navigation::GetActiveView(ifc_omservice **serviceOut)
+{
+ HWND hView = (NULL != hLibrary) ? ((HWND)SENDMLIPC(hLibrary, ML_IPC_GETCURRENTVIEW, 0)) : NULL;
+ if (NULL != hView)
+ {
+ WCHAR szBuffer[128] = {0};
+ if (!GetClassName(hView, szBuffer, ARRAYSIZE(szBuffer)) ||
+ CSTR_EQUAL != CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, szBuffer, -1,
+ L"Nullsoft_omBrowserView", -1))
+ {
+ hView = NULL;
+ }
+ }
+
+ ifc_omservice *service;
+ if (NULL == hView || FALSE == BrowserView_GetService(hView, &service))
+ {
+ hView = NULL;
+ service = NULL;
+ }
+
+ if (NULL != serviceOut)
+ *serviceOut = service;
+ else if (NULL != service)
+ service->Release();
+
+ return hView;
+}
+
+HRESULT Navigation::SelectItem(HNAVITEM hItem, LPCWSTR pszUrl)
+{
+ if (NULL == hItem) return E_INVALIDARG;
+
+ ifc_omservice *service;
+ HRESULT hr = GetService(hItem, &service);
+ if (FAILED(hr)) return hr;
+
+ hr = SelectItemInt(hItem, service->GetId(), pszUrl);
+ service->Release();
+
+ return hr;
+}
+
+HRESULT Navigation::DeleteItem(HNAVITEM hItem)
+{
+ if (NULL == hItem) return E_INVALIDARG;
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ ifc_omservice *service;
+ if (FAILED(GetService(hItem, &service)))
+ return E_FAIL;
+
+ UINT serviceFlags;
+ if (FAILED(service->GetFlags(&serviceFlags))) serviceFlags = 0;
+ if (0 != ((WDSVCF_ROOT | WDSVCF_SPECIAL) & serviceFlags))
+ return E_NOTIMPL;
+
+ MLNavCtrl_BeginUpdate(hLibrary, 0);
+ HNAVITEM hSelection = MLNavCtrl_GetSelection(hLibrary);
+ if (hSelection == hItem)
+ {
+ HNAVITEM hNext = MLNavItem_GetNext(hLibrary, hItem);
+ if (NULL == hNext)
+ hNext = MLNavItem_GetPrevious(hLibrary, hItem);
+
+ if (NULL != hNext)
+ {
+ MLNavItem_Select(hLibrary, hNext);
+ }
+ }
+
+ BOOL result = MLNavCtrl_DeleteItem(hLibrary, hItem);
+ HRESULT hr = (FALSE != result) ? S_OK : E_FAIL;
+
+ MLNavCtrl_EndUpdate(hLibrary);
+
+ if (SUCCEEDED(hr))
+ ServiceHelper_Delete(service);
+
+ service->Release();
+
+ return hr;
+}
+
+HRESULT Navigation::DeleteAll()
+{
+ if (NULL == hRoot || NULL == hLibrary) return E_UNEXPECTED;
+
+ std::vector<HNAVITEM> itemList;
+ HNAVITEM hItem = MLNavItem_GetChild(hLibrary, hRoot);
+ while (NULL != hItem)
+ {
+ itemList.push_back(hItem);
+ hItem = MLNavItem_GetNext(hLibrary, hItem);
+ }
+
+ MLNavCtrl_BeginUpdate(hLibrary, 0);
+
+ NAVITEM item;
+ item.cbSize = sizeof(item);
+ item.mask = NIMF_PARAM;
+
+ size_t index = itemList.size();
+ while(index--)
+ {
+ item.hItem = itemList[index];
+ if (FALSE != MLNavItem_GetInfo(hLibrary, &item))
+ {
+ ifc_omservice *service = (ifc_omservice*)item.lParam;
+ if (NULL != service)
+ {
+ service->AddRef();
+
+ UINT serviceFlags;
+ if (SUCCEEDED(service->GetFlags(&serviceFlags)) &&
+ 0 == ((WDSVCF_ROOT | WDSVCF_SPECIAL) & serviceFlags) &&
+ FALSE != MLNavCtrl_DeleteItem(hLibrary, item.hItem))
+ {
+ ServiceHelper_Delete(service);
+ }
+
+ service->Release();
+ }
+ }
+ }
+
+ MLNavCtrl_EndUpdate(hLibrary);
+ return S_OK;
+}
+
+HRESULT Navigation::CreatePopup(HNAVITEM hItem, HWND *hwnd)
+{
+ if (NULL == hwnd) return E_POINTER;
+ *hwnd = NULL;
+
+ if (NULL == hLibrary) return E_UNEXPECTED;
+ if (NULL == hItem) return E_INVALIDARG;
+
+ HRESULT hr;
+
+ ifc_omservice *service;
+ hr = GetService(hItem, &service);
+ if (SUCCEEDED(hr))
+ {
+ HWND hWinamp = Plugin_GetWinamp();
+
+ if (NULL == OMBROWSERMNGR)
+ hr = E_UNEXPECTED;
+
+ if (SUCCEEDED(hr))
+ {
+ hr = OMBROWSERMNGR->Initialize(NULL, hWinamp);
+ if (SUCCEEDED(hr))
+ {
+ RECT rect;
+ HWND hFrame = (HWND)SENDMLIPC(hLibrary, ML_IPC_GETCURRENTVIEW, 0);
+ if (NULL == hFrame) hFrame = hLibrary;
+ if (NULL == hFrame || FALSE == GetWindowRect(hFrame, &rect))
+ {
+ hr = E_FAIL;
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ rect.left += 16;
+ rect.top += 16;
+
+ hr = OMBROWSERMNGR->CreatePopup(service, rect.left, rect.top,
+ rect.right - rect.left, rect.bottom - rect.top, hWinamp, NULL, 0, hwnd);
+ }
+ }
+ }
+ service->Release();
+ }
+ return hr;
+}
+
+HRESULT Navigation::GetService(HNAVITEM hItem, ifc_omservice **service)
+{
+ WCHAR szBuffer[64] = {0};
+
+ if (NULL == service) return E_POINTER;
+ *service = NULL;
+
+ if (NULL == hLibrary || NULL == hItem)
+ return E_INVALIDARG;
+
+ NAVITEM itemInfo;
+ itemInfo.cbSize = sizeof(NAVITEM);
+ itemInfo.hItem = hItem;
+ itemInfo.pszInvariant = szBuffer;
+ itemInfo.cchInvariantMax = ARRAYSIZE(szBuffer);
+ itemInfo.mask = NIMF_PARAM | NIMF_TEXTINVARIANT;
+
+ if (FALSE == MLNavItem_GetInfo(hLibrary, &itemInfo))
+ return E_FAIL;
+
+ if (FALSE == Navigation_CheckInvariantName(szBuffer))
+ return E_NAVITEM_UNKNOWN;
+
+ *service = (ifc_omservice*)itemInfo.lParam;
+ (*service)->AddRef();
+ return S_OK;
+}
+
+static void CALLBACK Navigtaion_UpdateServiceApc(Dispatchable *object, ULONG_PTR param1, ULONG_PTR param2)
+{
+ Navigation *navigation = (Navigation*)object;
+ if (NULL != navigation)
+ {
+ ifc_omservice *service = (ifc_omservice*)param1;
+ navigation->UpdateService(service, (UINT)param2);
+ if (NULL != service) service->Release();
+ }
+}
+
+HRESULT Navigation::UpdateService(ifc_omservice *service, UINT modifiedFlags)
+{
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ DWORD libraryTID = GetWindowThreadProcessId(hLibrary, NULL);
+ DWORD currentTID = GetCurrentThreadId();
+ if (libraryTID != currentTID)
+ {
+ if (NULL != OMUTILITY)
+ {
+ service->AddRef();
+ if (FAILED(OMUTILITY->PostMainThreadCallback2(Navigtaion_UpdateServiceApc, this, (ULONG_PTR)service, (ULONG_PTR)modifiedFlags)))
+ service->Release();
+ }
+ return E_PENDING;
+ }
+
+ HNAVITEM hItem = FindService(service->GetId(), NULL);
+ if (NULL == hItem)
+ return E_FAIL;
+
+ NAVITEM itemInfo;
+ itemInfo.cbSize = sizeof(NAVITEM);
+ itemInfo.hItem = hItem;
+
+ itemInfo.mask = NIMF_IMAGE;
+ if (FALSE == MLNavItem_GetInfo(hLibrary, &itemInfo))
+ itemInfo.iImage= -1;
+
+ itemInfo.mask = 0;
+
+ WCHAR szName[512] = {0};
+ if (0 != (ifc_omserviceeditor::modifiedName & modifiedFlags) &&
+ SUCCEEDED(service->GetName(szName, ARRAYSIZE(szName))))
+ {
+ itemInfo.mask |= NIMF_TEXT;
+ itemInfo.pszText = szName;
+ }
+
+
+ if (0 != (ifc_omserviceeditor::modifiedIcon & modifiedFlags))
+ {
+ ifc_mlnavigationhelper *navHelper;
+ if (SUCCEEDED(OMUTILITY->GetMlNavigationHelper(Plugin_GetLibrary(), &navHelper)))
+ {
+ INT iImage;
+ WCHAR szIcon[1024] = {0};
+ if (FAILED(service->GetIcon(szIcon, ARRAYSIZE(szIcon))) ||
+ FAILED(navHelper->QueryIndex(szIcon, &iImage, NULL)))
+ {
+ iImage = -1;
+ }
+
+ if (itemInfo.iImage != iImage)
+ {
+ itemInfo.mask |= NIMF_IMAGE | NIMF_IMAGESEL;
+ itemInfo.iImage = iImage;
+ itemInfo.iSelectedImage = iImage;
+ }
+
+ navHelper->Release();
+ }
+ }
+
+ if (0 != itemInfo.mask)
+ {
+ if (FALSE == MLNavItem_SetInfo(hLibrary, &itemInfo))
+ return E_FAIL;
+ }
+
+ NAVITEMINAVLIDATE invalidate;
+ invalidate.hItem = hItem;
+ invalidate.fErase = FALSE;
+ invalidate.prc = NULL;
+ MLNavItem_Invalidate(hLibrary, &invalidate);
+
+ return S_OK;
+}
+
+HNAVITEM Navigation::FindService(UINT serviceId, ifc_omservice **serviceOut)
+{
+ if (NULL == hRoot || NULL == hLibrary)
+ {
+ if (NULL != serviceOut) *serviceOut = NULL;
+ return NULL;
+ }
+
+ WCHAR szBuffer[128] = {0};
+ NAVITEM item = {0};
+ item.cbSize = sizeof(item);
+ item.mask = NIMF_TEXTINVARIANT | NIMF_PARAM;
+ item.pszInvariant = szBuffer;
+ item.cchInvariantMax = ARRAYSIZE(szBuffer);
+
+ item.hItem = hRoot;
+
+ while (NULL != item.hItem)
+ {
+ if (FALSE != MLNavItem_GetInfo(hLibrary, &item) &&
+ FALSE != Navigation_CheckInvariantName(item.pszInvariant))
+ {
+ ifc_omservice *service = (ifc_omservice*)item.lParam;
+ if (NULL != service && serviceId == service->GetId())
+ {
+ if (NULL != serviceOut)
+ {
+ *serviceOut = service;
+ service->AddRef();
+ }
+ return item.hItem;
+ }
+ }
+
+ item.hItem = (HNAVITEM)SENDMLIPC(hLibrary,
+ (item.hItem == hRoot) ? ML_IPC_NAVITEM_GETCHILD : ML_IPC_NAVITEM_GETNEXT,
+ (WPARAM)item.hItem);
+
+ }
+
+ if (NULL != serviceOut) *serviceOut = NULL;
+ return NULL;
+}
+
+HRESULT Navigation::ShowService(UINT serviceId, LPCWSTR pszUrl)
+{
+ ifc_omservice *service;
+ HNAVITEM hItem = FindService(serviceId, &service);
+ if (NULL == hItem) return E_FAIL;
+
+ HRESULT hr = SelectItemInt(hItem, serviceId, pszUrl);
+ service->Release();
+
+ return hr;
+}
+
+HNAVITEM Navigation::CreateItem(ifc_omservice *service)
+{
+ if (NULL == hLibrary || NULL == hRoot) return NULL;
+ return CreateItemInt(hRoot, service);
+}
+
+HRESULT Navigation::GenerateServiceName(LPWSTR pszBuffer, INT cchBufferMax)
+{
+ if (NULL == pszBuffer) return E_POINTER;
+ *pszBuffer = L'\0';
+
+ if (NULL == hLibrary || NULL == hRoot) return E_UNEXPECTED;
+
+ if (FAILED(Plugin_CopyResString(pszBuffer, cchBufferMax, MAKEINTRESOURCE(IDS_USERSERVICE_NAME))))
+ return E_UNEXPECTED;
+
+ INT cchName = lstrlen(pszBuffer);
+ LPWSTR pszFormat = pszBuffer + cchName;
+ INT cchFormatMax = cchBufferMax - cchName;
+
+ WCHAR szText[512] = {0};
+ NAVITEM item = {0};
+
+ item.cbSize = sizeof(item);
+ item.mask = NIMF_TEXT;
+ item.pszText = szText;
+ item.cchTextMax = ARRAYSIZE(szText);
+
+ BOOL fFound = TRUE;
+
+ for(INT index = 1; FALSE != fFound; index++)
+ {
+ fFound = FALSE;
+ if (FAILED(StringCchPrintf(pszFormat, cchFormatMax, L" %d", index)))
+ {
+ pszFormat = L'\0';
+ return E_FAIL;
+ }
+
+ item.hItem = MLNavItem_GetChild(hLibrary, hRoot);
+ while(NULL != item.hItem)
+ {
+ if (FALSE != MLNavItem_GetInfo(hLibrary, &item) &&
+ CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, item.pszText, -1, pszBuffer, -1))
+ {
+ fFound = TRUE;
+ break;
+ }
+ item.hItem = MLNavItem_GetNext(hLibrary, item.hItem);
+ }
+ }
+
+ return S_OK;
+}
+
+HRESULT Navigation::CreateUserService(HNAVITEM *itemOut)
+{
+ HRESULT hr;
+
+ if (NULL != itemOut)
+ *itemOut = NULL;
+
+ if (NULL == hRoot) return E_FAIL;
+
+ INT serviceId = 710;
+ while(NULL != FindService(serviceId, NULL)) serviceId++;
+
+ WCHAR szName[256];
+ if (FAILED(GenerateServiceName(szName, ARRAYSIZE(szName))))
+ return E_FAIL;
+
+ ifc_omservice *service;
+ hr = ServiceHelper_Create(serviceId, szName, NULL, L"about:blank", 0, TRUE, &service);
+
+ if (SUCCEEDED(hr))
+ {
+ HNAVITEM hItem = CreateItem(service);
+ if (NULL == hItem)
+ {
+ hr = E_FAIL;
+ }
+ else
+ {
+ if (NULL != itemOut)
+ *itemOut = hItem;
+ }
+
+ service->Release();
+ }
+
+ return hr;
+}
+
+HNAVITEM Navigation::CreateItemInt(HNAVITEM hParent, ifc_omservice *service)
+{
+ if (NULL == service) return NULL;
+ ServiceHelper_RegisterPreAuthorized(service);
+
+ WCHAR szName[256] = {0}, szInvariant[64] = {0};
+ if (FAILED(service->GetName(szName, ARRAYSIZE(szName))))
+ return NULL;
+
+ if (L'\0' == szName[0])
+ WASABI_API_LNGSTRINGW_BUF(IDS_UNKNOWNNAME, szName, ARRAYSIZE(szName));
+
+ if (FAILED(StringCchPrintf(szInvariant, ARRAYSIZE(szInvariant), NAVITEM_PREFIX L"%u", service->GetId())))
+ return NULL;
+
+ NAVINSERTSTRUCT nis = {0};
+ nis.hInsertAfter = NULL;
+ nis.hParent = hParent;
+
+ INT iIcon = -1;
+ ifc_mlnavigationhelper *navHelper;
+ if (NULL != OMUTILITY && SUCCEEDED(OMUTILITY->GetMlNavigationHelper(Plugin_GetLibrary(), &navHelper)))
+ {
+ WCHAR szIcon[2048] = {0};
+ if (FAILED(service->GetIcon(szIcon, ARRAYSIZE(szIcon))) ||
+ FAILED(navHelper->QueryIndex(szIcon, &iIcon, NULL)))
+ {
+ iIcon = -1;
+ }
+ navHelper->Release();
+ }
+
+ nis.item.cbSize = sizeof(NAVITEM);
+ nis.item.mask = NIMF_TEXT | NIMF_STYLE | NIMF_TEXTINVARIANT | NIMF_PARAM | NIMF_IMAGE | NIMF_IMAGESEL;
+
+ nis.item.id = 0;
+ nis.item.pszText = szName;
+ nis.item.pszInvariant = szInvariant;
+ nis.item.lParam = (LPARAM)service;
+
+ nis.item.style = 0;
+ UINT serviceFlags;
+ if (FAILED(service->GetFlags(&serviceFlags)))
+ serviceFlags = 0;
+
+ if (0 != (WDSVCF_ROOT & serviceFlags))
+ {
+ nis.item.style |= (NIS_HASCHILDREN | NIS_ALLOWCHILDMOVE);
+ iIcon = -1;
+ }
+ else if (0 != (WDSVCF_SPECIAL & serviceFlags))
+ nis.item.style |= NIS_ITALIC;
+ else
+ nis.item.style |= NIS_ALLOWEDIT;
+
+ nis.item.styleMask = nis.item.style;
+
+ nis.item.iImage = iIcon;
+ nis.item.iSelectedImage = iIcon;
+
+
+ HNAVITEM hItem = MLNavCtrl_InsertItem(hLibrary, &nis);
+ if (NULL != hItem)
+ {
+ WebDevServiceHost *serviceHost;
+ if (SUCCEEDED(WebDevServiceHost::GetCachedInstance(&serviceHost)))
+ {
+ ifc_omserviceeventmngr *eventManager;
+ if (SUCCEEDED(service->QueryInterface(IFC_OmServiceEventMngr, (void**)&eventManager)))
+ {
+ eventManager->RegisterHandler(serviceHost);
+ eventManager->Release();
+ }
+
+ serviceHost->Release();
+ }
+
+ service->AddRef();
+ }
+
+ return hItem;
+}
+
+HRESULT Navigation::SelectItemInt(HNAVITEM hItem, UINT serviceId, LPCWSTR pszUrl)
+{
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ if (NULL != pszUrl && L'\0' != *pszUrl)
+ {
+ HRESULT hr = ForceUrl_Set(serviceId, pszUrl);
+ if (FAILED(hr)) return hr;
+ }
+ else
+ {
+ ForceUrl_Remove();
+ }
+
+ if (FALSE == MLNavItem_Select(hLibrary, hItem))
+ {
+ ForceUrl_Remove();
+ return E_FAIL;
+ }
+
+ return S_OK;
+
+}
+HNAVITEM Navigation::GetMessageItem(INT msg, INT_PTR param1)
+{
+ return (msg < ML_MSG_NAVIGATION_FIRST) ?
+ MLNavCtrl_FindItemById(hLibrary, param1) :
+ (HNAVITEM)param1;
+}
+
+HRESULT Navigation::OnCreateView(HNAVITEM hItem, HWND hParent, HWND *hView)
+{
+ if (NULL == hView) return E_POINTER;
+ *hView = NULL;
+
+ if (NULL == hLibrary) return E_UNEXPECTED;
+ if (NULL == hItem || NULL == hParent) return E_INVALIDARG;
+
+ HRESULT hr;
+
+ ifc_omservice *service;
+ hr = GetService(hItem, &service);
+ if (SUCCEEDED(hr))
+ {
+ if (NULL == OMBROWSERMNGR)
+ hr = E_UNEXPECTED;
+
+ if (SUCCEEDED(hr))
+ {
+ hr = OMBROWSERMNGR->Initialize(NULL, Plugin_GetWinamp());
+ if (SUCCEEDED(hr))
+ {
+ LPCWSTR forceUrl;
+ if (FAILED(ForceUrl_Get(service->GetId(), &forceUrl)))
+ forceUrl = NULL;
+
+ hr = OMBROWSERMNGR->CreateView(service, hParent, forceUrl, 0, hView);
+
+ ForceUrl_Remove();
+ }
+ }
+
+ service->Release();
+ }
+ return hr;
+}
+
+HRESULT Navigation::OnContextMenu(HNAVITEM hItem, HWND hHost, POINTS pts)
+{
+ if (NULL == hItem || NULL == hHost)
+ return E_INVALIDARG;
+
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ HRESULT hr;
+ ifc_omservice *service;
+ hr = GetService(hItem, &service);
+ if (FAILED(hr)) return hr;
+
+ POINT pt;
+ POINTSTOPOINT(pt, pts);
+ if (-1 == pt.x || -1 == pt.y)
+ {
+ NAVITEMGETRECT itemRect;
+ itemRect.fItem = FALSE;
+ itemRect.hItem = hItem;
+ if (MLNavItem_GetRect(hLibrary, &itemRect))
+ {
+ MapWindowPoints(hHost, HWND_DESKTOP, (POINT*)&itemRect.rc, 2);
+ pt.x = itemRect.rc.left + 2;
+ pt.y = itemRect.rc.top + 2;
+ }
+ }
+
+ HMENU hMenu = Menu_GetMenu(MENU_SERVICECONTEXT, service);
+ if (NULL != hMenu)
+ {
+ INT commandId = Menu_TrackPopup(Plugin_GetLibrary(), hMenu,
+ TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD,
+ pt.x, pt.y, hHost, NULL);
+ Menu_ReleaseMenu(hMenu, MENU_SERVICECONTEXT);
+ CommandManager_Process(hItem, service, commandId);
+ }
+
+ service->Release();
+
+ return hr;
+}
+
+HRESULT Navigation::OnEndTitleEdit(HNAVITEM hItem, LPCWSTR pszNewTitle)
+{
+ if (NULL == hItem) return E_INVALIDARG;
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ HRESULT hr;
+
+ ifc_omservice *service;
+ hr = GetService(hItem, &service);
+ if (SUCCEEDED(hr))
+ {
+ if (NULL != pszNewTitle)
+ {
+ ifc_omserviceeditor *editor;
+ hr = service->QueryInterface(IFC_OmServiceEditor, (void**)&editor);
+ if (SUCCEEDED(hr))
+ {
+ hr = editor->SetName(pszNewTitle, FALSE);
+ editor->Release();
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ ServiceHelper_Save(service);
+
+ service->Release();
+ }
+ return hr;
+}
+
+HRESULT Navigation::OnDeleteItem(HNAVITEM hItem)
+{
+ if (NULL == hItem) return E_INVALIDARG;
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ WCHAR szBuffer[2048] = {0};
+ NAVITEM itemInfo = {0};
+
+ itemInfo.cbSize = sizeof(itemInfo);
+ itemInfo.hItem = hItem;
+ itemInfo.pszInvariant = szBuffer;
+ itemInfo.cchInvariantMax = ARRAYSIZE(szBuffer);
+ itemInfo.mask = NIMF_PARAM | NIMF_TEXTINVARIANT | NIMF_IMAGE;
+
+ if (FALSE == MLNavItem_GetInfo(hLibrary, &itemInfo))
+ return E_FAIL;
+ if (FALSE == Navigation_CheckInvariantName(szBuffer))
+ return E_NAVITEM_UNKNOWN;
+
+ ifc_omservice *service = (ifc_omservice*)itemInfo.lParam;
+
+ if (NULL != service)
+ {
+ if (SUCCEEDED(service->GetIcon(szBuffer, ARRAYSIZE(szBuffer))))
+ {
+ ifc_mlnavigationhelper *navHelper;
+ if (SUCCEEDED(OMUTILITY->GetMlNavigationHelper(Plugin_GetLibrary(), &navHelper)))
+ {
+ navHelper->ReleaseIndex(szBuffer);
+ navHelper->Release();
+ }
+ }
+
+ ifc_ombrowserwndmngr *windowManager;
+ if (NULL != OMBROWSERMNGR && SUCCEEDED(OMBROWSERMNGR->QueryInterface(IFC_OmBrowserWindowManager, (void**)&windowManager)))
+ {
+ UINT serviceId = service->GetId();
+
+ ifc_ombrowserwndenum *windowEnum;
+ if (SUCCEEDED(windowManager->Enumerate(NULL, &serviceId, &windowEnum)))
+ {
+ HWND hwnd;
+ while (S_OK == windowEnum->Next(1, &hwnd, NULL))
+ {
+ DestroyWindow(hwnd);
+ }
+ windowEnum->Release();
+ }
+
+ windowManager->Release();
+ }
+
+ WebDevServiceHost *serviceHost;
+ if (SUCCEEDED(WebDevServiceHost::GetCachedInstance(&serviceHost)))
+ {
+ ifc_omserviceeventmngr *eventManager;
+ if (SUCCEEDED(service->QueryInterface(IFC_OmServiceEventMngr, (void**)&eventManager)))
+ {
+ eventManager->UnregisterHandler(serviceHost);
+ eventManager->Release();
+ }
+ serviceHost->Release();
+ }
+
+ itemInfo.mask = NIMF_PARAM;
+ itemInfo.lParam = 0L;
+ MLNavItem_SetInfo(hLibrary, &itemInfo);
+
+ service->Release();
+ }
+
+
+
+ return S_OK;
+}
+
+HRESULT Navigation::OnKeyDown(HNAVITEM hItem, NMTVKEYDOWN *pnmkd)
+{
+ if (NULL == hItem) return E_INVALIDARG;
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ ifc_omservice *service;
+ HRESULT hr = GetService(hItem, &service);
+ if (SUCCEEDED(hr))
+ {
+ switch(pnmkd->wVKey)
+ {
+ case VK_DELETE:
+ Command_DeleteItem(hItem);
+ break;
+ }
+
+ service->Release();
+ }
+ return hr;
+}
+
+HRESULT Navigation::OnControlDestroy()
+{
+ SaveOrder();
+ return S_OK;
+}
+
+#define CBCLASS Navigation
+START_DISPATCH;
+CB(ADDREF, AddRef)
+CB(RELEASE, Release)
+CB(QUERYINTERFACE, QueryInterface)
+VCB(API_IMAGECHANGED, ImageChanged)
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/navigation.h b/Src/Plugins/Library/ml_webdev/navigation.h
new file mode 100644
index 00000000..c2575a0a
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/navigation.h
@@ -0,0 +1,83 @@
+#ifndef NULLSOFT_WEBDEV_PLUGIN_NAVIGATION_HEADER
+#define NULLSOFT_WEBDEV_PLUGIN_NAVIGATION_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include <ifc_mlnavigationcallback.h>
+#include <vector>
+
+typedef LPVOID HNAVITEM;
+class ifc_omservice;
+
+class Navigation : public ifc_mlnavigationcallback
+{
+protected:
+ Navigation();
+ ~Navigation();
+
+public:
+ static HRESULT CreateInstance(Navigation **instance);
+
+public:
+ /* Dispatchable */
+ size_t AddRef();
+ size_t Release();
+ int QueryInterface(GUID interface_guid, void **object);
+
+ /* ifc_mlnavigationcallback */
+ void ImageChanged(LPCWSTR pszName, INT index);
+
+public:
+ HRESULT Finish();
+
+ BOOL ProcessMessage(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3, INT_PTR *result);
+
+ HNAVITEM GetActive(ifc_omservice **serviceOut);
+ HWND GetActiveView(ifc_omservice **serviceOut);
+
+ HRESULT SelectItem(HNAVITEM hItem, LPCWSTR pszUrl);
+ HRESULT DeleteItem(HNAVITEM hItem);
+ HRESULT DeleteAll();
+
+ HRESULT CreatePopup(HNAVITEM hItem, HWND *hwnd);
+
+ HRESULT GetService(HNAVITEM hItem, ifc_omservice **service);
+ HRESULT UpdateService(ifc_omservice *service, UINT modifiedFlags);
+ HNAVITEM FindService(UINT serviceId, ifc_omservice **serviceOut);
+ HRESULT ShowService(UINT serviceId, LPCWSTR pszUrl);
+
+ HNAVITEM CreateItem(ifc_omservice *service);
+ HRESULT CreateUserService(HNAVITEM *itemOut);
+
+protected:
+ HRESULT Initialize();
+ HRESULT SaveOrder();
+ HRESULT Order(std::vector<ifc_omservice *>&list);
+
+ HNAVITEM GetMessageItem(INT msg, INT_PTR param1);
+ HNAVITEM CreateItemInt(HNAVITEM hParent, ifc_omservice *service);
+ HRESULT SelectItemInt(HNAVITEM hItem, UINT serviceId, LPCWSTR pszUrl);
+
+ HRESULT GenerateServiceName(LPWSTR pszBuffer, INT cchBufferMax);
+
+ HRESULT OnCreateView(HNAVITEM hItem, HWND hParent, HWND *hView);
+ HRESULT OnContextMenu(HNAVITEM hItem, HWND hHost, POINTS pts);
+ HRESULT OnEndTitleEdit(HNAVITEM hItem, LPCWSTR pszNewTitle);
+ HRESULT OnDeleteItem(HNAVITEM hItem);
+ HRESULT OnKeyDown(HNAVITEM hItem, NMTVKEYDOWN *pnmkd);
+ HRESULT OnControlDestroy();
+
+protected:
+ size_t ref;
+ HNAVITEM hRoot;
+ HWND hLibrary;
+ UINT cookie;
+
+protected:
+ RECVS_DISPATCH;
+};
+
+#endif //NULLSOFT_WEBDEV_PLUGIN_NAVIGATION_HEADER
diff --git a/Src/Plugins/Library/ml_webdev/png.rc b/Src/Plugins/Library/ml_webdev/png.rc
new file mode 100644
index 00000000..12f4920e
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/png.rc
@@ -0,0 +1,9 @@
+#include "resource.h"
+/////////////////////////////////////////////////////////////////////////////
+//
+// Data
+//
+IDR_HELP_ICON RCDATA
+".\\resources\\help.png"
+IDR_GEAR_ICON RCDATA
+".\\resources\\gear.png" \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resource.h b/Src/Plugins/Library/ml_webdev/resource.h
new file mode 100644
index 00000000..8a64aac5
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resource.h
@@ -0,0 +1,60 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by ml_webdev.rc
+//
+#define IDS_ROOTSERVICE_NAME 101
+#define IDS_DOCSERVICE_NAME 102
+#define IDR_HTML_EDITOR 102
+#define IDS_TESTSERVICE_NAME 103
+#define IDD_OPENURL 103
+#define IDS_USERSERVICE_NAME 104
+#define IDS_FILEFILTER_ALL 105
+#define IDS_FILEFILTER_INI 106
+#define IDS_FILEFILTER_ALLKNOWN 106
+#define IDS_LOADSERVICE 107
+#define IDS_UNKNOWNNAME 108
+#define IDS_IMPORT_FILES 109
+#define IDS_FILEFILER_XML 110
+#define IDR_CONTEXTMENU 400
+#define IDR_HTML_WELCOME 1000
+#define IDC_EDIT_ADDRESS 1000
+#define IDC_ADDRESS 1000
+#define IDR_HTML_TEST 1001
+#define IDR_HELP_ICON 20001
+#define IDR_GEAR_ICON 20002
+#define ID_NAVIGATION_CREATESEVICE 40001
+#define ID_SERVICE_EDIT 40010
+#define ID_NAVIGATION_BACK 40011
+#define ID_NAVIGATION_FORWARD 40012
+#define ID_NAVIGATION_HOME 40013
+#define ID_NAVIGATION_REFRESH 40014
+#define ID_NAVIGATION_STOP 40015
+#define ID_VIEW_NEWWINDOW 40016
+#define ID_VIEW_OPEN 40017
+#define ID_SERVICE_DELETE 40020
+#define ID_SERVICE_RELOAD 40021
+#define ID_SERVICEMNGR_CREATENEW 40022
+#define ID_SERVICE_RESETPERMISSIONS 40024
+#define ID_SERVICE_NEW 40026
+#define ID_SERVICE_LOAD 40027
+#define ID_SERVICE_LOCATE 40029
+#define ID_SERVICE_EDITEXTERNAL 40031
+#define ID_BROWSER_PREFERENCES 40032
+#define ID_OMBROWSER_OPTIONS 40034
+#define ID_SERVICE_IMPORT 40035
+#define ID_SERVICE_IMPORT_FILE 40039
+#define ID_SERVICE_IMPORT_FOLDER 40040
+#define ID_SERVICE_IMPORT_URL 40041
+#define ID_SERVICE_DELETEALL 40042
+#define IDS_PLUGIN_NAME 65534
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 104
+#define _APS_NEXT_COMMAND_VALUE 40043
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/Plugins/Library/ml_webdev/resources/gear.png b/Src/Plugins/Library/ml_webdev/resources/gear.png
new file mode 100644
index 00000000..da8a31d9
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/gear.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_webdev/resources/help.png b/Src/Plugins/Library/ml_webdev/resources/help.png
new file mode 100644
index 00000000..b9eab4e1
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/help.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/api2.js b/Src/Plugins/Library/ml_webdev/resources/pages/api2.js
new file mode 100644
index 00000000..f61eb4bd
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/api2.js
@@ -0,0 +1,624 @@
+function startup(){
+}
+
+function NotImplementedYet(){
+ alert("This method has not been implemented yet");
+}
+
+function tStopClicked(){
+ var t_rc = window.external.Transport.Stop();
+}
+
+function tPlayClicked(){
+ var t_rc = window.external.Transport.Play();
+}
+
+function tPauseClicked(){
+ var t_rc = window.external.Transport.Pause();
+}
+
+function tPrevClicked(){
+ var t_rc = window.external.Transport.Prev();
+}
+
+function tNextClicked(){
+ var t_rc = window.external.Transport.Next();
+}
+
+function tGetMetadataClicked(){
+ var metaTag = document.getElementById("t_t_mTag_in").value;
+ var metadata = window.external.Transport.GetMetadata(metaTag);
+ document.getElementById("t_t_mTag_Response").value = metadata;
+}
+
+function tGetShuffleClicked(){
+ var t_shuffle = window.external.Transport.shuffle;
+ document.getElementById("t_t_shuffle").value = t_shuffle;
+}
+
+function tSetShuffleClicked(){
+ var t_shuffle = document.getElementById("t_t_shuffle_in").value;
+ if (t_shuffle == "true") window.external.Transport.shuffle = true;
+ else window.external.Transport.shuffle = false;
+ tGetShuffleClicked();
+}
+
+function tGetRepeatClicked(){
+ var t_repeat = window.external.Transport.repeat;
+ document.getElementById("t_t_repeat").value = t_repeat;
+}
+
+function tSetRepeatClicked(){
+ var t_repeat = document.getElementById("t_t_repeat_in").value;
+ if (t_repeat == "true") window.external.Transport.repeat = true;
+ else window.external.Transport.repeat = false;
+ tGetRepeatClicked();
+}
+
+function tGetPositionClicked(){
+ var t_position = window.external.Transport.position;
+ document.getElementById("t_t_position").value = t_position;
+}
+
+function tSetPositionClicked(){
+ var t_position = document.getElementById("t_t_position_in").value;
+ window.external.Transport.position = parseInt(t_position);
+ tGetPositionClicked();
+}
+
+function tLengthClicked(){
+ var length = window.external.Transport.length;
+ document.getElementById("t_t_Length").value = length;
+}
+
+function tURLClicked(){
+ var url = window.external.Transport.url;
+ document.getElementById("t_t_URL").value = url;
+}
+
+function tTitleClicked(){
+ var title = window.external.Transport.title;
+ document.getElementById("t_t_Title").value = title;
+}
+
+function tPlayingClicked(){
+ var playing = window.external.Transport.playing;
+ document.getElementById("t_t_Playing").value = playing;
+}
+
+function tPausedClicked(){
+ var paused = window.external.Transport.paused;
+ document.getElementById("t_t_Paused").value = paused;
+}
+
+function pqPlayClicked() {
+ var myMusic = document.getElementById("pq_t_PlayURL_in").value;
+ var title = document.getElementById("pq_t_PlayTitle_in").value
+ var length = document.getElementById("pq_t_PlayLength_in").value;
+ var ilength = parseInt(length);
+ if (isNaN(ilength)) {ilength = 0;}
+ var pq_play = window.external.PlayQueue.Play(myMusic, title, ilength);
+}
+
+function pqEnqueueClicked() {
+ var myMusic = document.getElementById("pq_t_EnqueueURL_in").value;
+ var title = document.getElementById("pq_t_EnqueueTitle_in").value
+ var length = document.getElementById("pq_t_EnqueueLength_in").value;
+ var ilength = parseInt(length);
+ if (isNaN(ilength)) {ilength = 0};
+ var pq_play = window.external.PlayQueue.Enqueue(myMusic, title, ilength);
+}
+
+function pqInsertClicked() {
+ var position = document.getElementById("pq_t_InsertPosition_in").value;
+ var iposition = parseInt(position);
+ if (isNaN(iposition)) {iposition = 0;}
+ var myMusic = document.getElementById("pq_t_InsertURL_in").value;
+ var title = document.getElementById("pq_t_InsertTitle_in").value
+ var length = document.getElementById("pq_t_InsertLength_in").value;
+ var ilength = parseInt(length);
+ if (isNaN(ilength)) {ilength = 0;}
+ var pq_play = window.external.PlayQueue.Insert(iposition, myMusic, title, ilength);
+}
+
+function pqClearQClicked() {
+ var pq_rc = window.external.PlayQueue.ClearQueue();
+}
+
+function pqGetMetadataClicked(){
+ var metaPos = document.getElementById("pq_t_MetadataPosition_in").value;
+ var imetaPos = parseInt(metaPos);
+ var metaTag = document.getElementById("pq_t_MTag_in").value;
+ var metadata = window.external.PlayQueue.GetMetadata(imetaPos, metaTag);
+ document.getElementById("pq_t_MTag_Response").value = metadata;
+}
+
+function pqGetTitleClicked(){
+ var position = document.getElementById("pq_t_TitlePosition_in").value;
+ var iposition = parseInt(position);
+ var title = window.external.PlayQueue.GetTitle(iposition);
+ document.getElementById("pq_t_Title").value = title;
+}
+
+function pqGetURLClicked() {
+ var position = document.getElementById("pq_t_URLPosition_in").value;
+ var iposition = parseInt(position);
+ var url = window.external.PlayQueue.GetURL(iposition);
+ document.getElementById("pq_t_URL").value = url;
+}
+
+function pqGetLengthClicked(){
+ var length = window.external.PlayQueue.length;
+ document.getElementById("pq_t_Length").value = length;
+}
+
+function pqGetCursorClicked(){
+ var cursor = window.external.PlayQueue.cursor;
+ document.getElementById("pq_t_Cursor").value = cursor;
+}
+
+function pqSetCursorClicked(){
+ var pq_cursor = document.getElementById("pq_t_Cursor_in").value;
+ window.external.PlayQueue.cursor = parseInt(pq_cursor);
+ pqGetCursorClicked();
+}
+
+var listPlaylists;
+function plsGetPlaylists(){
+ listPlaylists = window.external.Playlists.GetPlaylists();
+ var numPlaylists = listPlaylists.length;
+ document.getElementById("pls_t_Count").value = numPlaylists;
+}
+
+function plsViewIndexClicked(){
+ var view_index = document.getElementById("pls_t_Index_in").value;
+ var iview_index = parseInt(view_index);
+ document.getElementById("pls_t_Filename").value = listPlaylists[iview_index].filename;
+ document.getElementById("pls_t_Title").value = listPlaylists[iview_index].title;
+ document.getElementById("pls_t_PlaylistId").value = listPlaylists[iview_index].playlistId;
+ document.getElementById("pls_t_Length").value = listPlaylists[iview_index].length;
+ document.getElementById("pls_t_NumItems").value = listPlaylists[iview_index].numitems;
+ document.getElementById("pls_t_OpenPlaylistId_in").value = listPlaylists[iview_index].playlistId;
+ document.getElementById("pls_t_SavePlaylistId_in").value = listPlaylists[iview_index].playlistId;
+}
+
+var currentPlaylist;
+function plsOpenPlaylistClicked(){
+ var id = document.getElementById("pls_t_OpenPlaylistId_in").value;
+ currentPlaylist = window.external.Playlists.OpenPlaylist(id);
+ document.getElementById("playlist_t_NumItems").value = currentPlaylist.numitems;
+ plsDisableListMethods(false);
+ showPlaylist();
+}
+
+var currentItemIndex = 0;
+
+function plsDisablePlaylistMethods(disable){
+ var playlistSection = document.getElementById("playlistmethods");
+ playlistSection.disabled = disable;
+ for (var i=0; i < playlistSection.childNodes.length; i++){
+ if (playlistSection.childNodes[i].nodeType == 1) {
+ playlistSection.childNodes[i].disabled = disable;
+ }
+ }
+}
+
+function plsDisableListMethods(disable){
+ var listSection = document.getElementById("listmethods");
+ listSection.disabled = disable;
+ for (var i=0; i < listSection.childNodes.length; i++){
+ if (listSection.childNodes[i].nodeType == 1) {
+ listSection.childNodes[i].disabled = disable;
+ }
+ }
+}
+
+function plsPlaylistViewIndexClicked(){
+ var view_index = document.getElementById("playlist_t_ItemIndex_in").value;
+ var iview_index = parseInt(view_index);
+ currentItemIndex = iview_index;
+ document.getElementById("playlist_t_Filename").value = currentPlaylist.GetItemFilename(currentItemIndex);
+ document.getElementById("playlist_t_Title").value = currentPlaylist.GetItemTitle(currentItemIndex);
+ document.getElementById("playlist_t_Length").value = currentPlaylist.GetItemLength(currentItemIndex);
+ plsDisablePlaylistMethods(false);
+ showPlaylist();
+}
+
+var playlistwin;
+var playlistWinShown = false;
+function showPlaylist(){
+ var output = "";
+ for (var i = 0; i < currentPlaylist.numitems; i++) {
+ var title = currentPlaylist.GetItemTitle(i);
+ output += i + ":" + title + "\n";
+ }
+ alert(output);
+}
+
+function plsPlaylistSetItemFilenameClicked(){
+ var filename = document.getElementById("playlist_t_SetItemFilename_in").value;
+ currentPlaylist.SetItemFilename(currentItemIndex, filename);
+ document.getElementById("playlist_t_Filename").value = filename;
+}
+
+function plsPlaylistSetItemTitleClicked(){
+ var title = document.getElementById("playlist_t_SetItemTitle_in").value;
+ currentPlaylist.SetItemTitle(currentItemIndex, title);
+ document.getElementById("playlist_t_Title").value = title;
+}
+
+function plsPlaylistSetItemLengthClicked(){
+ var length = document.getElementById("playlist_t_SetItemLength_in").value;
+ var iLength = parseInt(length);
+ currentPlaylist.SetItemLengthMilliseconds(currentItemIndex, iLength);
+ document.getElementById("playlist_t_Length").value = length;
+}
+
+function plsPlaylistReverseClicked(){
+ currentPlaylist.Reverse();
+ showPlaylist();
+}
+
+function plsPlaylistSwapItemsClicked(){
+ var swap1 = document.getElementById("playlist_t_Swap1_in").value;
+ var swap2 = document.getElementById("playlist_t_Swap2_in").value;
+ var iswap1 = parseInt(swap1);
+ var iswap2 = parseInt(swap2);
+ var rc = currentPlaylist.SwapItems(iswap1, iswap2);
+ showPlaylist();
+}
+
+function plsPlaylistRandomizeClicked(){
+ currentPlaylist.Randomize();
+ showPlaylist();
+}
+
+function plsPlaylistRemoveClicked(){
+ alert("Inside Remove, currentItemIndex=" + currentItemIndex);
+ currentPlaylist.RemoveItem(currentItemIndex);
+ showPlaylist();
+}
+
+function plsPlaylistSortByTitleClicked(){
+ currentPlaylist.SortByTitle();
+ showPlaylist();
+}
+
+function plsPlaylistSortByFilenameClicked(){
+ currentPlaylist.SortByFilename();
+ showPlaylist();
+}
+
+function plsPlaylistInsertUrlClicked(){
+ var url = document.getElementById("playlist_t_InsertUrl_in").value;
+ var title = document.getElementById("playlist_t_InsertTitle_in").value;
+ var length = document.getElementById("playlist_t_InsertLength_in").value;
+ var iLength = parseInt(length);
+ var rc = currentPlaylist.InsertURL(currentItemIndex, url, title, iLength);
+ showPlaylist();
+}
+
+function plsPlaylistAppendUrlClicked(){
+ var url = document.getElementById("playlist_t_AppendUrl_in").value;
+ var title = document.getElementById("playlist_t_AppendTitle_in").value;
+ var length = document.getElementById("playlist_t_AppendLength_in").value;
+ var iLength = parseInt(length);
+ var rc = currentPlaylist.AppendURL(url, title, iLength);
+ showPlaylist();
+}
+
+function plsPlaylistClearClicked(){
+ var rc = currentPlaylist.Clear();
+ showPlaylist();
+}
+
+function plsSavePlaylistClicked() {
+ var playlistId = document.getElementById("pls_t_SavePlaylistId_in").value;
+ var rc = window.external.Playlists.SavePlaylist(playlistId, currentPlaylist);
+ document.getElementById("playlistmethods").disabled = true;
+}
+
+function BMarkAddClicked(){
+ var url = document.getElementById("bmark_t_AddURL_in").value;
+ var title = document.getElementById("bmark_t_AddTitle_in").value;
+ var rc = window.external.Bookmarks.Add(url, title);
+}
+
+function PodcastSubscribeClicked(){
+ var podcUrl = document.getElementById("podc_t_SubscribeURL_in").value;
+ var rc = window.external.Podcasts.Subscribe(podcUrl);
+}
+
+function ConfigSetPropertyClicked(){
+ var inParam = document.getElementById("config_t_SetPropertyName_in").value;
+ var inValue = document.getElementById("config_t_SetPropertyValue_in").value;
+ if (pType[0].checked) {
+ inValue = "'" + inValue + "'";
+ } else if (pType[2].checked){
+ if (inValue != "false"){
+ inValue = "true";
+ }
+ }
+ var funcBody = "var rc = window.external.Config." + inParam + "=" + inValue;
+ var configPropSet = new Function(funcBody);
+ configPropSet();
+}
+
+function ConfigGetPropertyClicked(){
+ var inParam = document.getElementById("config_t_GetPropertyName_in").value;
+ var funcBody = "return window.external.Config." + inParam;
+ var configPropGet = new Function(funcBody);
+ var propValue = configPropGet();
+ document.getElementById("config_t_GetPropertyValue").value = propValue;
+}
+
+function ApplicationLaunchURLClicked(){
+ var url = document.getElementById("application_t_URL_in").value;
+ var forceExternal = document.getElementById("application_t_ForceExternal_in").checked;
+ var rc = window.external.Application.LaunchURL(url, forceExternal);
+}
+
+function ApplicationNumVersionClicked(){
+ var numVer = window.external.Application.version;
+ document.getElementById("application_t_NumVersion").value = parseInt(numVer);
+}
+
+function ApplicationStringVersionClicked(){
+ var stringVer = window.external.Application.versionstring;
+ document.getElementById("application_t_StringVersion").value = stringVer;
+}
+
+function ApplicationLanguageClicked(){
+ var lang = window.external.Application.language;
+ document.getElementById("application_t_Language").value = lang;
+}
+
+function ApplicationLanguagePackClicked(){
+ var langPack = window.external.Application.languagepack;
+ document.getElementById("application_t_LanguagePack").value = langPack;
+}
+
+function SkinGetClassicColorClicked(){
+ var colorNum = document.getElementById("skin_t_ClassicColorNumber_in").value;
+ var iColorNum = parseInt(colorNum);
+ var classicColor = window.external.Skin.GetClassicColor(iColorNum);
+ document.getElementById("skin_t_ClassicColor").value = classicColor;
+}
+
+function SkinGetPlaylistColorClicked(){
+ var colorNum = document.getElementById("skin_t_PlaylistColorNumber_in").value;
+ var iColorNum = parseInt(colorNum);
+ var playlistColor = window.external.Skin.GetPlaylistColor(iColorNum);
+ document.getElementById("skin_t_PlaylistColor").value = playlistColor;
+}
+
+function SkinGetSkinColorClicked(){
+ var colorName = document.getElementById("skin_t_SkinColorName_in").value;
+ var skinColor = window.external.Skin.GetSkinColor(colorName);
+ document.getElementById("skin_t_SkinColor").value = skinColor;
+}
+
+function SkinGetNameClicked(){
+ var name = window.external.Skin.name;
+ document.getElementById("skin_t_Name").value = name;
+}
+
+function SkinSetNameClicked() {
+ var skinName = document.getElementById("skin_t_Name_in").value;
+ window.external.Skin.name = skinName;
+}
+
+function SkinGetFontClicked(){
+ var font = window.external.Skin.font;
+ document.getElementById("skin_t_Font").value = font;
+}
+
+function SkinGetFontSizeClicked(){
+ var fontsize = window.external.Skin.fontsize;
+ document.getElementById("skin_t_FontSize").value = fontsize;
+}
+
+function mcGetMetadataClicked(){
+ var metaFile = document.getElementById("mc_t_MediaCoreFilename_in").value;
+ var metaTag = document.getElementById("mc_t_MediaCoreTag_in").value;
+ var metadata = window.external.MediaCore.GetMetadata(metaFile, metaTag);
+ document.getElementById("mc_t_MediaCoreMetadata_Response").value = metadata;
+}
+
+function mcIsRegisteredExtensionClicked(){
+ var extension = document.getElementById("mc_t_MediaCoreExtension_in").value;
+ var supported = window.external.MediaCore.IsRegisteredExtension(extension);
+ document.getElementById("mc_t_MediaCoreRegisteredExtension_Response").value = supported;
+}
+
+function mcAddMetadataHookClicked(){
+ var mUrl = document.getElementById("mc_t_AddMetadataHookUrl_in").value;
+ var mTag = document.getElementById("mc_t_AddMetadataHookTag_in").value;
+ var mValue = document.getElementById("mc_t_AddMetadataHookValue_in").value;
+ window.external.MediaCore.AddMetadataHook(mUrl,mTag,mValue);
+}
+
+function mcRemoveMetadataHookClicked(){
+ var mUrl = document.getElementById("mc_t_RemoveMetadataHookUrl_in").value;
+ var mTag = document.getElementById("mc_t_RemoveMetadataHookTag_in").value;
+ window.external.MediaCore.RemoveMetadataHook(mUrl,mTag);
+}
+
+function teRegisterClicked(){
+ var rc = window.external.Transport.RegisterForEvents(onEvents);
+ document.getElementById("teRegister").disabled = true;
+ document.getElementById("teUnregister").disabled = false;
+}
+
+function teUnregisterClicked(){
+ var rc = window.external.Transport.UnregisterFromEvents(onEvents);
+ document.getElementById("teRegister").disabled = false;
+ document.getElementById("teUnregister").disabled = true;
+}
+
+var eventsArray = new Array();
+var eventCount = 0;
+function onEvents(event){
+ eventsArray[eventCount] = event;
+
+ // populate the select box
+ var eventSelect = document.getElementById("te_s_Select");
+ var newOption = new Option((eventCount + 1) + ":" + event.event, event);
+ eventSelect.options[eventCount] = newOption;
+ eventCount++;
+}
+
+function teEventSelected(){
+ var sel = document.getElementById("te_s_Select");
+ var area = document.getElementById("te_ta_Area");
+ var tarea = "";
+ var obj = eventsArray[sel.selectedIndex];
+ for (var prop in obj){
+ tarea += "property:" + prop + " value:" + obj[prop] + "\n\n";
+ }
+ area.value = tarea;
+}
+
+function te_AreaCleared(){
+ eventCount = 0;
+ document.getElementById("te_ta_Area").value = "";
+ document.getElementById("te_s_Select").options.length = 0;
+}
+
+function hisQueryClicked(){
+ var query = document.getElementById("his_t_Query_in").value;
+ var respArray = window.external.History.Query(query);
+ alert(respArray[0].filename);
+ var textOut = "";
+ for (var obj in respArray){
+ if (textOut != ""){
+ textOut += "\n";
+ }
+ textOut += "==============";
+ textOut += "\nTitle: " + respArray[obj].title;
+ textOut += "\nLastPlay: " + respArray[obj].lastplay;
+ textOut += "\nPlaycount: " + respArray[obj].playcount;
+ textOut += "\nFilename: " + respArray[obj].filename;
+ textOut += "\nLength: " + respArray[obj].length;
+ }
+ var textAreaOut = document.getElementById("his_t_Query");
+ textAreaOut.value = textOut;
+}
+
+function AsyncDownloadMediaClicked(){
+ var url = document.getElementById("asyncdownloader_t_URL_in").value;
+ var destFile = document.getElementById("asyncdownloader_t_DestinationFile_in").value;
+ document.getElementById("progressbar").firstChild.nodeValue = "";
+ document.getElementById("slider").style.clip = "rect(0px 0px 16px 0px)";
+ if (destFile)
+ var rc = window.external.AsyncDownloader.DownloadMedia(url, destFile);
+ else
+ var rc = window.external.AsyncDownloader.DownloadMedia(url);
+}
+
+function AsyncDownloadMediaClicked1(){
+ var url = document.getElementById("asyncdownloader_t_URL_in_1").value;
+ var destFile = document.getElementById("asyncdownloader_t_DestinationFile_in_1").value;
+ document.getElementById("progressbar1").firstChild.nodeValue = "";
+ document.getElementById("slider1").style.clip = "rect(0px 0px 16px 0px)";
+ if (destFile)
+ var rc = window.external.AsyncDownloader.DownloadMedia(url, destFile);
+ else
+ var rc = window.external.AsyncDownloader.DownloadMedia(url);
+}
+
+function AsyncDownloadMultipleMediaClicked() {
+ var multipleUrls = document.getElementById("multiple_urls").value;
+ var urls = multipleUrls.split(";");
+ for(index = 0; index < urls.length; index++){
+ if (urls[index].length > 0) {
+ if (urls[index].indexOf("http://") != -1) urls[index] = urls[index].substr(7);
+ var rc = window.external.AsyncDownloader.DownloadMedia("http://"+escape(urls[index]));
+ }
+ }
+}
+
+function downloaderRegisterClicked(){
+ var rc = window.external.AsyncDownloader.RegisterForEvents(onDownloaderEvents);
+ document.getElementById("downloaderRegister").disabled = true;
+ document.getElementById("downloaderUnregister").disabled = false;
+}
+
+function downloaderUnregisterClicked(){
+ var rc = window.external.AsyncDownloader.UnregisterFromEvents(onDownloaderEvents);
+ document.getElementById("downloaderRegister").disabled = false;
+ document.getElementById("downloaderUnregister").disabled = true;
+}
+
+var downloaderEventsArray = new Array();
+var downloaderEventCount = 0;
+function downloader_AreaCleared(){
+ downloaderEventCount = 0;
+ document.getElementById("downloader_ta_Area").value = "";
+ document.getElementById("downloader_s_Select").options.length = 0;
+}
+
+function onDownloaderEvents(event){
+ downloaderEventsArray[downloaderEventCount] = event;
+
+ // populate the select box
+ var eventSelect = document.getElementById("downloader_s_Select");
+ var newOption = new Option((downloaderEventCount + 1) + ":" + event.event, event);
+ eventSelect.options[downloaderEventCount] = newOption;
+ downloaderEventCount++;
+
+ var progress, slider;
+ if (event.url == document.getElementById("asyncdownloader_t_URL_in").value)
+ {
+ progress = document.getElementById("progressbar");
+ slider = document.getElementById("slider");
+ }
+ if (event.url == document.getElementById("asyncdownloader_t_URL_in_1").value)
+ {
+ progress = document.getElementById("progressbar1");
+ slider = document.getElementById("slider1");
+ }
+
+ if (progress && slider)
+ {
+ if (event.event == 'OnInit')
+ {
+ progress.firstChild.nodeValue = 'Progress: Start downloading ...';
+ }
+ if (event.event == 'OnData')
+ {
+ progress.firstChild.nodeValue = 'Progress: Downloading ...';
+ var factor = event.downloadedlen/event.totallen;
+ slider.firstChild.nodeValue = Math.ceil(factor * 100) + '%';
+ slider.style.clip = "rect(0px " + parseInt(factor * 417) + "px 16px 0px)";
+ }
+ if (event.event == 'OnFinish')
+ {
+ progress.firstChild.nodeValue = 'Progress: Downloading Succeed';
+ slider.firstChild.nodeValue = '100%';
+ }
+ if (event.event == 'OnError' || event.event == 'OnCancel')
+ {
+ progress.firstChild.nodeValue = 'Progress: Downloading Failed';
+ }
+ }
+}
+
+function downloaderEventSelected(){
+ var sel = document.getElementById("downloader_s_Select");
+ var area = document.getElementById("downloader_ta_Area");
+ var tarea = "";
+ var obj = downloaderEventsArray[sel.selectedIndex];
+ if (obj.url)
+ tarea += "property:url value:" + obj.url + "\n\n";
+ if (obj.event == 'OnData')
+ {
+ tarea += "property:downloadedlen value:" + obj.downloadedlen + "\n\n";
+ tarea += "property:totallen value:" + obj.totallen + "\n\n";
+ }
+ if (obj.event == 'OnFinish')
+ tarea += "property:destfilename value:" + obj.destfilename + "\n\n";
+ if (obj.event == 'OnError')
+ tarea += "property:error value:" + obj.error + "\n\n";
+ area.value = tarea;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/applicationApi.htm b/Src/Plugins/Library/ml_webdev/resources/pages/applicationApi.htm
new file mode 100644
index 00000000..96bf4137
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/applicationApi.htm
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>JSAPI2::Application Api</title>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
+ <script type="text/javascript" src="api2.js"></script>
+</head>
+<body style="FONT-FAMILY: MS Shell Dlg 2;">
+ <h2>Application API</h2>
+ <h3>Methods:</h3>
+ <input type="button" value="Launch URL" onclick="ApplicationLaunchURLClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;URL:<input type="text" size="20" id="application_t_URL_in" value="http://dev.winamp.com">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Force External Browser:<input type="checkbox" checked="unchecked" id="application_t_ForceExternal_in">
+ <h3>Properties:</h3>
+ <input type="button" value="Get number version" onclick="ApplicationNumVersionClicked();">
+ <input type="text" readonly size="20" id="application_t_NumVersion">
+ <br>
+ <br>
+ <input type="button" value="Get string version" onclick="ApplicationStringVersionClicked();">
+ <input type="text" readonly size="20" id="application_t_StringVersion">
+ <br>
+ <br>
+ <input type="button" value="Get language" onclick="ApplicationLanguageClicked();">
+ <input type="text" readonly size="20" id="application_t_Language">
+ <br>
+ <br>
+ <input type="button" value="Get languagepack" onclick="ApplicationLanguagePackClicked();">
+ <input type="text" readonly size="20" id="application_t_LanguagePack">
+</body>
+</html> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/asyncDownloaderApi.htm b/Src/Plugins/Library/ml_webdev/resources/pages/asyncDownloaderApi.htm
new file mode 100644
index 00000000..4aab77d1
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/asyncDownloaderApi.htm
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>JSAPI2::AsyncDownloader Api</title>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
+ <style type="text/css">
+ #slider{position:absolute; left:20px; top:180px; width:420px; height:15px; clip:rect(0px 0px 15px 0px); background-color:#666699; text-align:left; color:#ffffff; font-size:12px;}
+ #slider1{position:absolute; left:20px; top:280px; width:420px; height:15px; clip:rect(0px 0px 15px 0px); background-color:#666699; text-align:left; color:#ffffff; font-size:12px;}
+ </style>
+ <script type="text/javascript" src="api2.js"></script>
+</head>
+<body style="FONT-FAMILY: MS Shell Dlg 2;">
+ <h2>AsyncDownloader API</h2>
+ <h3>Methods:</h3>
+ <input type="button" value="Download Media" onclick="AsyncDownloadMediaClicked();">&nbsp;&nbsp;URL:&nbsp;<input type="text" size="40" id="asyncdownloader_t_URL_in">&nbsp;&nbsp;Destination File:&nbsp;<input type="text" size="20" id="asyncdownloader_t_DestinationFile_in">
+ <div id="progressbardiv"><h5 id="progressbar">Progress: </h5>
+ <br>
+ <div id="slider">0%</div>
+ </div>
+ <input type="button" value="Download Media" onclick="AsyncDownloadMediaClicked1();">&nbsp;&nbsp;URL:&nbsp;<input type="text" size="40" id="asyncdownloader_t_URL_in_1">&nbsp;&nbsp;Destination File:&nbsp;<input type="text" size="20" id="asyncdownloader_t_DestinationFile_in_1">
+ <div id="progressbardiv1"><h5 id="progressbar1">Progress: </h5>
+ <br>
+ <div id="slider1">0%</div>
+ </div>
+ <input type="button" value="Download Multiple Medias" onclick="AsyncDownloadMultipleMediaClicked();"><br>&nbsp;&nbsp;URLs:&nbsp;<textarea cols="60" rows="10" id="multiple_urls"></textarea>
+ <br>
+ <h3>Events:</h3>
+ <input type="button" value="Register For Downloader Events" id="downloaderRegister" onclick="downloaderRegisterClicked();">
+ <input type="button" value="Unregister From Downloader Events" id="downloaderUnregister" onclick="downloaderUnregisterClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Callback function:&nbsp;<input readonly type="text" id="downloader_t_Callback" value="OnDownloaderEvents">
+ <br><br>
+ <b>Events Received</b>
+ &nbsp;&nbsp;&nbsp;&nbsp;
+ <a href="javascript:downloader_AreaCleared();"><small>clear</small></a>
+ <br>
+ <select size="10" id="downloader_s_Select" style="width: 100px" onchange="downloaderEventSelected();">
+ <option>No Events</option>
+ </select>
+ <textarea rows="10" cols="40" id="downloader_ta_Area"></textarea>
+</body>
+</html> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/bookmarkApi.htm b/Src/Plugins/Library/ml_webdev/resources/pages/bookmarkApi.htm
new file mode 100644
index 00000000..c31bacb5
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/bookmarkApi.htm
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>JSAPI2::Bookmarks Api</title>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
+ <script type="text/javascript" src="api2.js"></script>
+</head>
+<body style="FONT-FAMILY: MS Shell Dlg 2;">
+ <h2>Bookmarks API</h2>
+ <h3>Methods:</h3>
+ <input type="button" value="Add Bookmark" onclick="BMarkAddClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;URL:<input type="text" size="20" id="bmark_t_AddURL_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Title:<input type="text" size="20" id="bmark_t_AddTitle_in">
+</body>
+</html> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/configApi.htm b/Src/Plugins/Library/ml_webdev/resources/pages/configApi.htm
new file mode 100644
index 00000000..a114144e
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/configApi.htm
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>JSAPI2::Config Api</title>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
+ <script type="text/javascript" src="api2.js"></script>
+</head>
+<body style="FONT-FAMILY: MS Shell Dlg 2;">
+ <h2>Config API</h2>
+ <h3>Methods:</h3>
+ <input type="button" value="Set Property" onclick="ConfigSetPropertyClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Property Name:<input type="text" size="20" id="config_t_SetPropertyName_in" value="myProperty">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Property Type:
+ <input type="radio" name="pType" id="config_t_SetPropertyType_in" checked>string</input>
+ <input type="radio" name="pType" id="config_t_SetPropertyType_in">number</input>
+ <input type="radio" name="pType" id="config_t_SetPropertyType_in">boolean</input>
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Property Value:<input type="text" size="20" id="config_t_SetPropertyValue_in" value="myPropertyValue">
+ <br><br>
+ <input type="button" value="Get Property" onclick="ConfigGetPropertyClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Property Value:<input type="text" readonly size="20" id="config_t_GetPropertyValue">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Property Name:<input type="text" size="20" id="config_t_GetPropertyName_in" value="myProperty">
+</body>
+</html> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/historyApi.htm b/Src/Plugins/Library/ml_webdev/resources/pages/historyApi.htm
new file mode 100644
index 00000000..b7732c01
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/historyApi.htm
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>JSAPI2::History Api</title>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
+ <script type="text/javascript" src="api2.js"></script>
+</head>
+<body style="FONT-FAMILY: MS Shell Dlg 2;">
+ <h2>History API</h2>
+ <h3>Methods:</h3>
+ <input type="button" value="Query" onClick="hisQueryClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<input type="text" size="80" value="LASTPLAY<[now]" id="his_t_Query_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<textarea rows="24" cols="50" id="his_t_Query">
+ </textarea>
+</body>
+</html> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/mediaCoreApi.htm b/Src/Plugins/Library/ml_webdev/resources/pages/mediaCoreApi.htm
new file mode 100644
index 00000000..3daee11f
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/mediaCoreApi.htm
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>JSAPI2::MediaCore Api</title>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
+ <script type="text/javascript" src="api2.js"></script>
+</head>
+<body style="FONT-FAMILY: MS Shell Dlg 2;">
+ <h2>MediaCore API</h2>
+ <h3>Methods:</h3>
+ <input type="button" value="Get Metadata" onclick="mcGetMetadataClicked();">
+ <input type="text" readonly size="40" id="mc_t_MediaCoreMetadata_Response">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Filename:&nbsp;<input type="text" value="C:\Program Files\Winamp\demoedit.aac" id="mc_t_MediaCoreFilename_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Metadata Tag:&nbsp;<input type="text" value="artist" id="mc_t_MediaCoreTag_in">
+ <br><br>
+ <input type="button" value="Is Registered Extension" onclick="mcIsRegisteredExtensionClicked();">
+ <input type="text" readonly size="40" id="mc_t_MediaCoreRegisteredExtension_Response">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Extension:&nbsp;<input type="text" value=".mp3" id="mc_t_MediaCoreExtension_in">
+ <br><br>
+ <input type="button" value="AddMetadataHook" onclick="mcAddMetadataHookClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;URL:&nbsp;<input type="text" size="50" id="mc_t_AddMetadataHookURL_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Metadata Tag:&nbsp;<input type="text" id="mc_t_AddMetadataHookTag_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Metadata Value:&nbsp;<input type="text" id="mc_t_AddMetadataHookValue_in">
+ <br><br>
+ <input type="button" value="RemoveMetadataHook" onclick="mcRemoveMetadataHookClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;URL:&nbsp;<input type="text" size="50" id="mc_t_RemoveMetadataHookURL_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Metadata Tag:&nbsp;<input type="text" id="mc_t_RemoveMetadataHookTag_in">
+</body>
+</html> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/playQueueApi.htm b/Src/Plugins/Library/ml_webdev/resources/pages/playQueueApi.htm
new file mode 100644
index 00000000..6fc63c82
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/playQueueApi.htm
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>JSAPI2::PlayQueue Api</title>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
+ <script type="text/javascript" src="api2.js"></script>
+</head>
+<body style="FONT-FAMILY: MS Shell Dlg 2;">
+ <h2>PlayQueue API</h2>
+ <h3>Methods:</h3>
+ <input type="button" value="Play" onclick="pqPlayClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;URL:&nbsp;<input type="text" id="pq_t_PlayURL_in" value="c:\Documents and Settings\smontgo444\My Documents\My Music\Winamp\01 - Hello City.mp3">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;title:&nbsp;<input type="text" id="pq_t_PlayTitle_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;length:&nbsp;<input type="text" id="pq_t_PlayLength_in">
+ <br>
+ <br>
+ <input type="button" value="Enqueue" onclick="pqEnqueueClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;URL:&nbsp;<input type="text" id="pq_t_EnqueueURL_in" value="c:\Documents and Settings\smontgo444\My Documents\My Music\Winamp\01 - Hello City.mp3">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;title:&nbsp;<input type="text" id="pq_t_EnqueueTitle_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;length:&nbsp;<input type="text" id="pq_t_EnqueueLength_in">
+ <br>
+ <br>
+ <input type="button" value="Insert" onclick="pqInsertClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;position:&nbsp;<input type="text" id="pq_t_InsertPosition_in" value="0">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;URL:&nbsp;<input type="text" id="pq_t_InsertURL_in" value="c:\Documents and Settings\smontgo444\My Documents\My Music\Winamp\08 - The King Of Bedside Manor.mp3">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;title:&nbsp;<input type="text" id="pq_t_InsertTitle_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;length:&nbsp;<input type="text" id="pq_t_InsertLength_in">
+ <br>
+ <br>
+ <input type="button" value="Clear Queue" onclick="pqClearQClicked();">
+ <br>
+ <br>
+ <input type="button" value="Get Metadata" onclick="pqGetMetadataClicked();">
+ <input type="text" readonly size="40" id="pq_t_MTag_Response">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;position:&nbsp;<input type="text" value="0" id="pq_t_MetadataPosition_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Metadata Tag:&nbsp;<input type="text" value="artist" id="pq_t_MTag_in">
+ <br>
+ <br>
+ <input type="button" value="Get Title" onclick="pqGetTitleClicked();">
+ <input type="text" readonly size="20" id="pq_t_Title">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;position:&nbsp;<input type="text" value="0" id="pq_t_TitlePosition_in">
+ <br>
+ <br>
+ <input type="button" value="Get URL" onclick="pqGetURLClicked();">
+ <input type="text" readonly size="20" id="pq_t_URL">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;position:&nbsp;<input type="text" value="0" id="pq_t_URLPosition_in">
+ <br>
+ <br>
+ <h2>Properties</h2>
+ <input type="button" value="Get length" onclick="pqGetLengthClicked();">
+ <input type="text" readonly size="20" id="pq_t_Length">
+ <br>
+ <br>
+ <input type="button" value="Get cursor" onclick="pqGetCursorClicked();">
+ <input type="text" readonly size="20" id="pq_t_Cursor">
+ <br>
+ <input type="button" value="Set cursor" onclick="pqSetCursorClicked();">
+ <input type="text" size="20" id="pq_t_Cursor_in">
+</body>
+</html> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/playlistApi.htm b/Src/Plugins/Library/ml_webdev/resources/pages/playlistApi.htm
new file mode 100644
index 00000000..1553e875
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/playlistApi.htm
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>JSAPI2::Playlists Api</title>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
+ <script type="text/javascript" src="api2.js"></script>
+</head>
+<body onload="plsDisablePlaylistMethods(true);plsDisableListMethods(true);" style="FONT-FAMILY: MS Shell Dlg 2;">
+ <h2>Playlists API</h2>
+ <p><small>Since it is hard to obtain a playlist ID to use in the OpenPlaylist and SavePlaylist methods, click "Get Playlists" to get all the playlists and then "Set Index" to choose one of the playlists. This will pre-populate the playlist IDs for the OpenPlaylist and SavePlaylist buttons.</small>
+ <h3>Methods:</h3>
+ <input type="button" value="Get Playlists" onclick="plsGetPlaylists();">
+ <br>
+ &nbsp;&nbsp;Count (of playlists):<input type="text" readonly size="20" id="pls_t_Count">
+ <br>
+ &nbsp;&nbsp;<input type="button" value="Set Playlist Index" onclick="plsViewIndexClicked();">
+ <input type="text" size="10" value="0" id="pls_t_Index_in">
+ <br>
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Playlist Summary Info:
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Playlist ID: <input type="text" readonly size="60" id="pls_t_PlaylistId">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Playlist Num Items: <input type="text" readonly size="20" id="pls_t_NumItems">
+ &nbsp;&nbsp;<small>(cached, may not be current)</small>
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Playlist Filename: <input type="text" size="80" id="pls_t_Filename">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Playlist Title: <input type="text" size="80" id="pls_t_Title">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Playlist (total) Length: <input type="text" readonly size="20" id="pls_t_Length">
+ &nbsp;&nbsp;<small>(cached, may not be current)</small>
+ <br>
+ <br>
+ <input type="button" value="OpenPlaylist" onclick="plsOpenPlaylistClicked();">
+ <small>(will also display the playlist)</small>
+ <br>
+ Count (of items in playlist):<input type="text" readonly size="20" id="playlist_t_NumItems">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Playlist ID: <input type="text" size="60" id="pls_t_OpenPlaylistId_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<input type="button" value="Set Item Index" onclick="plsPlaylistViewIndexClicked()">
+ <input type="text" size="10" value="0" id="playlist_t_ItemIndex_in">
+ <br>
+ <br>
+ <div id="playlistmethods">
+ &nbsp;&nbsp;&nbsp;&nbsp;Get Item Filename: <input type="text" size="80" id="playlist_t_Filename">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Get Item Title: <input type="text" size="80" id="playlist_t_Title">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Get Item Length: <input type="text" readonly size="20" id="playlist_t_Length">
+ <br>
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<input type="button" value="Set Item Filename" onclick="plsPlaylistSetItemFilenameClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Item Filename: <input type="text" size="80" id="playlist_t_SetItemFilename_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<input type="button" value="Set Item Title" onclick="plsPlaylistSetItemTitleClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Item Title: <input type="text" size="80" id="playlist_t_SetItemTitle_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<input type="button" value="Set Item Length" onclick="plsPlaylistSetItemLengthClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Item Length: <input type="text" id="playlist_t_SetItemLength_in">
+ <br>
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<input type="button" value="Remove" onclick="plsPlaylistRemoveClicked();">
+ <br>
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<input type="button" value="Insert URL" onclick="plsPlaylistInsertUrlClicked();">
+ <small>(before the currently selected index)</small>
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;URL: <input type="text" size="80" id="playlist_t_InsertURL_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;title: <input type="text" size="80" id="playlist_t_InsertTitle_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;length: <input type="text" size="20" id="playlist_t_InsertLength_in">
+ <br>
+ </div>
+ <div id="listmethods">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<input type="button" value="Append URL" onclick="plsPlaylistAppendUrlClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;URL: <input type="text" size="80" id="playlist_t_AppendURL_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;title: <input type="text" size="80" id="playlist_t_AppendTitle_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;length: <input type="text" size="20" id="playlist_t_AppendLength_in">
+ <br>
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<input type="button" value="Reverse" onclick="plsPlaylistReverseClicked();">
+ <br>
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<input type="button" value="Swap Items" onclick="plsPlaylistSwapItemsClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Index #1: <input type="text" value="0" id="playlist_t_Swap1_in">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Index #2: <input type="text" value="1" id="playlist_t_Swap2_in">
+ <br>
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<input type="button" value="Randomize" onclick="plsPlaylistRandomizeClicked();">
+ <br>
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<input type="button" value="Sort By Title" onclick="plsPlaylistSortByTitleClicked();">
+ <br>
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<input type="button" value="Sort By Filename" onclick="plsPlaylistSortByFilenameClicked();">
+ <br>
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;<input type="button" value="Clear" onclick="plsPlaylistClearClicked();">
+ </div>
+ <br>
+ <br>
+ <input type="button" value="SavePlaylist" onclick="plsSavePlaylistClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Playlist ID: <input type="text" size="80" id="pls_t_SavePlaylistId_in">
+</body>
+</html> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/podcastApi.htm b/Src/Plugins/Library/ml_webdev/resources/pages/podcastApi.htm
new file mode 100644
index 00000000..5008aba7
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/podcastApi.htm
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>JSAPI2::Podcast Api</title>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
+ <script type="text/javascript" src="api2.js"></script>
+</head>
+<body style="FONT-FAMILY: MS Shell Dlg 2;">
+ <h2>Podcasts API</h2>
+ <h3>Methods:</h3>
+ <input type="button" value="Subscribe URL" onclick="PodcastSubscribeClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;URL:<input type="text" size="20" id="podc_t_SubscribeURL_in" value="http://services.winamp.com/rss/news">
+</body>
+</html> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/securityApi.htm b/Src/Plugins/Library/ml_webdev/resources/pages/securityApi.htm
new file mode 100644
index 00000000..72850262
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/securityApi.htm
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>JSAPI2::Security Api</title>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
+ <script type="text/javascript" src="api2.js"></script>
+</head>
+<body style="FONT-FAMILY: MS Shell Dlg 2;">
+ <h2>Security API</h2>
+ <h3>TBD</h3>
+ <!-- <h2>Methods:</h2>
+ <input type="button" value="Get Action Authorization" onclick="SecurityGetAuthorizationClicked();">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;URL:<input type="text" readonly size="20" id="security_t_Authorization">
+ -->
+</body>
+</html> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/serviceEditor.htm b/Src/Plugins/Library/ml_webdev/resources/pages/serviceEditor.htm
new file mode 100644
index 00000000..c23c73a2
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/serviceEditor.htm
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Online Services Editor</title>
+ <meta content="http://schemas.microsoft.com/intellisense/ie5" name="vs_targetSchema"/>
+ <meta content="True" name="vs_snapToGrid"/>
+ <meta content="False" name="vs_showGrid"/>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8"/>
+ <script src="webdev.js" type="text/javascript"></script>
+ <style type="text/css">#Checkbox1 {width: 321px; top: 160px; left: 64px;}</style>
+</head>
+<body style="overflow-y: auto; overflow-x: auto; overflow: visible; FONT-FAMILY: MS Shell Dlg 2;" onload="WebDevEditor_Init();">
+ <h2 align="center">Online Services Editor</h2>
+ <div align="left" nowrap>
+ <div id="DIV1" style="border: 1px solid gray; clear: both; DISPLAY: block; left: 50%; float: none; visibility: visible; margin-left: -204px; overflow: visible; width: 408px; color: black; position: absolute; height: 217px; background-color: dimgray; top: 126px;" align="center" nowrap ms_positioning="GridLayout">
+ <div style="z-index: 110; left: 16px; position: absolute; top: 14px">Id:</div>
+ <input id="scvedt_edt_id" style="z-index: 101; left: 64px; position: absolute; top: 14px" readonly maxlength="16" size="9" tabindex="1"/>
+ <div style="z-index: 102; left: 16px; position: absolute; top: 46px">Name:</div>
+ <input id="scvedt_edt_name" style="z-index: 103; left: 64px; position: absolute; top: 46px; width: 322px;" maxlength="512" size="48" tabindex="2"/>
+ <div style="z-index: 104; left: 16px; position: absolute; top: 78px">Url:</div>
+ <input id="scvedt_edt_url" style="z-index: 105; left: 64px; position: absolute; top: 78px; width: 321px;" maxlength="2048" size="48" name="Text1" tabindex="3"/>
+ <div style="z-index: 106; left: 16px; position: absolute; top: 110px">Icon:</div>
+ <input id="scvedt_edt_icon" style="z-index: 107; left: 64px; position: absolute; top: 110px; width: 321px;" maxlength="512" size="48" name="Text2" tabindex="4"/>
+ <div align = "left" style="z-index: 108; position: absolute; width: 320px; top: 143px; left: 60px;" >
+ <input id="svcedt_chk_bypass" name="Checkbox1" tabindex="5" title="Preauthorized service" type="checkbox" value="bypass"/>Preauthorized service</div>
+ <input id="svcedt_btn_save" style="z-index: 109; left: 200px; width: 88px; position: absolute; top: 180px; height: 24px; right: 118px;" onclick="WebDevEditor_Save();" type="submit" value="Save" name="buttonOk" tabindex="6"/>
+ <input id="svcedt_btn_close" style="z-index: 110; left: 297px; width: 88px; position: absolute; top: 180px; height: 24px" onclick="WebDevEditor_Close();" type="button" value="Cancel" name="buttonCancel" tabindex="7"/>
+ </div>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/skinApi.htm b/Src/Plugins/Library/ml_webdev/resources/pages/skinApi.htm
new file mode 100644
index 00000000..425c3217
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/skinApi.htm
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>JSAPI2::Skin Api</title>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
+ <script type="text/javascript" src="api2.js"></script>
+</head>
+<body style="FONT-FAMILY: MS Shell Dlg 2;">
+ <h2>Skin API</h2>
+ <h3>Methods:</h3>
+ <input type="button" value="Get Classic Color" onclick="SkinGetClassicColorClicked();">
+ <input type="text" size="20" id="skin_t_ClassicColor">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;color number:<input type="text" size="20" id="skin_t_ClassicColorNumber_in" value="1">
+ <br><br>
+ <input type="button" value="Get Playlist Color" onclick="SkinGetPlaylistColorClicked();">
+ <input type="text" readonly size="20" id="skin_t_PlaylistColor">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;color number:<input type="text" size="20" id="skin_t_PlaylistColorNumber_in" value="1">
+ <br><br>
+ <input type="button" value="Get Skin Color" onclick="SkinGetSkinColorClicked();">
+ <input type="text" readonly size="20" id="skin_t_SkinColor">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;color name:<input type="text" size="20" id="skin_t_SkinColorName_in" value="wasabi.text.color">
+ <br>
+ <h3>Properties:</h3>
+ <input type="button" value="Get name" onclick="SkinGetNameClicked();">
+ <input type="text" readonly size="20" id="skin_t_Name">
+ <br>
+ <input type="button" value="Set name" onclick="NotImplementedYet();">
+ <!-- <input type="button" value="Set name" onclick="SkinSetNameClicked();"> -->
+ <input type="text" size="20" id="skin_t_Name_in" value="Bento">
+ <br><br>
+ <input type="button" value="Get font" onclick="SkinGetFontClicked();">
+ <input type="text" readonly size="20" id="skin_t_Font">
+ <br><br>
+ <input type="button" value="Get fontsize" onclick="SkinGetFontSizeClicked();">
+ <input type="text" readonline size="20" id="skin_t_FontSize">
+</body>
+</html> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/transportApi.htm b/Src/Plugins/Library/ml_webdev/resources/pages/transportApi.htm
new file mode 100644
index 00000000..c2f9d70d
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/transportApi.htm
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>JSAPI2::Transport Api</title>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
+ <script type="text/javascript" src="api2.js"></script>
+</head>
+<body style="FONT-FAMILY: MS Shell Dlg 2;">
+ <h2>Transport API</h2>
+ <h3>Methods:</h3>
+ <input type="button" value="Prev" onclick="tPrevClicked();">
+ <input type="button" value="Play" onclick="tPlayClicked();">
+ <input type="button" value="Pause" onclick="tPauseClicked();">
+ <input type="button" value="Stop" onclick="tStopClicked();">
+ <input type="button" value="Next" onclick="tNextClicked();">
+ <br>
+ <br>
+ <input type="button" value="Get Metadata" onclick="tGetMetadataClicked();">
+ <input type="text" readonline size="40" id="t_t_mTag_Response">
+ <br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Metadata Tag:&nbsp;<input type="text" value="artist" id="t_t_mTag_in">
+ <br>
+ <h3>Properties</h3>
+ <input type="button" value="Get shuffle" onclick="tGetShuffleClicked();">
+ <input type="text" readonline size="20" id="t_t_shuffle">
+ <br>
+ <input type="button" value="Set shuffle" onclick="tSetShuffleClicked();">
+ <input type="text" size="20" id="t_t_shuffle_in">
+ <br>
+ <br>
+ <input type="button" value="Get repeat" onclick="tGetRepeatClicked();">
+ <input type="text" readonly size="20" id="t_t_repeat">
+ <br>
+ <input type="button" value="Set repeat" onclick="tSetRepeatClicked();">
+ <input type="text" size="20" id="t_t_repeat_in">
+ <br>
+ <br>
+ <input type="button" value="Get position" onclick="tGetPositionClicked();">
+ <input type="text" readonly size="20" id="t_t_position">
+ <br>
+ <input type="button" value="Set position" onclick="tSetPositionClicked();">
+ <input type="text" size="20" id="t_t_position_in">
+ <br>
+ <br>
+ <input type="button" value="Get length" id="b_t_length" onclick="tLengthClicked();">
+ <input type="text" readonly size="20" id="t_t_Length">
+ <br>
+ <br>
+ <input type="button" value="Get url" id="b_t_url" onclick="tURLClicked();">
+ <input type="text" size="95" id="t_t_URL">
+ <br>
+ <br>
+ <input type="button" value="Get title" id="b_t_title" onclick="tTitleClicked();">
+ <input type="text" size="95" id="t_t_Title">
+ <br>
+ <br>
+ <input type="button" value="Get playing" id="b_t_playing" onclick="tPlayingClicked();">
+ <input type="text" readonly size="20" id="t_t_Playing">
+ <br>
+ <br>
+ <input type="button" value="Get paused" id="b_t_paused" onclick="tPausedClicked();">
+ <input type="text" readonly size="20" id="t_t_Paused">
+</body>
+</html> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/transportEventsApi.htm b/Src/Plugins/Library/ml_webdev/resources/pages/transportEventsApi.htm
new file mode 100644
index 00000000..61d91e2b
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/transportEventsApi.htm
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>JSAPI2::Transport Events Api</title>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
+ <script type="text/javascript" src="api2.js"></script>
+</head>
+<body onload="teUnregisterClicked();" onunload="teUnregisterClicked();" style="FONT-FAMILY: MS Shell Dlg 2;">
+ <h2>Transport Events API</h2>
+ <h3>Methods:</h3>
+ <input type="button" value="Register For Events" id="teRegister" onclick="teRegisterClicked();">
+ <input type="button" value="Unregister From Events" id="teUnregister" onclick="teUnregisterClicked();">
+ <br><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;Callback function:&nbsp;<input readonly type="text" id="te_t_Callback" value="OnEvents">
+ <br><br><br>
+ <b>Events Received</b>
+ &nbsp;&nbsp;&nbsp;&nbsp;
+ <a href="javascript:te_AreaCleared();"><small>clear</small></a>
+ <br><br>
+ <select size="10" id="te_s_Select" style="width: 100px" onchange="teEventSelected();">
+ <option>No Events</option>
+ </select>
+ <textarea rows="10" cols="40" id="te_ta_Area"></textarea>
+</body>
+</html> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/webdev.htm b/Src/Plugins/Library/ml_webdev/resources/pages/webdev.htm
new file mode 100644
index 00000000..b48a8515
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/webdev.htm
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>JSAPI2::Main Page</title>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
+ <script type="text/javascript" src="api2.js"></script>
+ <script type="text/javascript" src="webdev.js"></script>
+</head>
+<body style="FONT-FAMILY: MS Shell Dlg 2;">
+ <h2>Winamp Online Services API - Test Web Page</h2>
+ <small>
+ <p>This page is to help with testing the different API methods of the Winamp Javascript API. In order to point Winamp to your own online service, click <a href="javascript:void(0);" onclick="WebDev_CreateService(); return false;">here</a> and set the URL to the URL of the main page of your service when prompted.</p>
+ <p>Check the <a target="_blank" href="http://dev.winamp.com/wiki/Complete_JavaScript_API_technology_framework">Winamp Javascript API</a> for more info on the API methods used on these test web pages.</p>
+ <a href="transportApi.htm">Transport API</a><br><br>
+ <a href="transportEventsApi.htm">Transport Events</a><br><br>
+ <a href="playQueueApi.htm">Playqueue API</a><br><br>
+ <a href="playlistApi.htm">Playlists API</a><br><br>
+ <a href="bookmarkApi.htm">Bookmarks API</a><br><br>
+ <a href="podcastApi.htm">Podcasts API</a><br><br>
+ <a href="historyApi.htm">History API</a><br><br>
+ <a href="configApi.htm">Config API</a><br><br>
+ <a href="applicationApi.htm">Application API</a><br><br>
+ <a href="securityApi.htm">Security API</a><br><br>
+ <a href="skinApi.htm">Skins API</a><br><br>
+ <a href="mediaCoreApi.htm">MediaCore API</a><br><br>
+ <a href="asyncDownloaderApi.htm">AsyncDownloader API</a>
+ </small>
+</body>
+</html> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/webdev.js b/Src/Plugins/Library/ml_webdev/resources/pages/webdev.js
new file mode 100644
index 00000000..14f5d199
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/webdev.js
@@ -0,0 +1,97 @@
+function GetUrlParam(name)
+{
+ name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
+ var regexS = "[\\?&]"+name+"=([^&#]*)";
+ var regex = new RegExp( regexS );
+ var results = regex.exec( window.location.href );
+ if( results == null )
+ return "";
+ else
+ return results[1];
+}
+
+function WebDev_OpenService(serviceId, forceUrl)
+{
+ if (typeof(window.external.WebDev) == "undefined")
+ alert("Cannot access Webdev Api");
+ else if (false == window.external.WebDev.serviceOpen(serviceId, forceUrl))
+ alert("Unable to open service");
+}
+function WebDev_OpenDocumentation()
+{
+ WebDev_OpenService(701, null);
+}
+
+function WebDev_OpenJSAPI2Test()
+{
+ WebDev_OpenService(702, null);
+}
+
+function WebDev_CreateService()
+{
+ if (typeof(window.external.WebDev) == "undefined")
+ alert("Cannot access Webdev Api");
+ else if (false == window.external.WebDev.serviceCreate())
+ alert("Unable to create service");
+}
+
+function WebDevEditor_Init(url)
+{
+ var serviceId = parseInt(GetUrlParam("serviceId"), 10);
+
+ if (typeof(window.external.WebDev) == "undefined")
+ alert("Cannot access Webdev Api");
+ else
+ {
+ var info = window.external.WebDev.serviceGetInfo(serviceId);
+ if (null == info)
+ {
+ alert("Unable to get service information");
+ }
+ else
+ {
+ document.getElementById("scvedt_edt_id").value = info.id;
+ document.getElementById("scvedt_edt_name").value = info.name;
+ document.getElementById("scvedt_edt_url").value = info.url;
+ document.getElementById("scvedt_edt_icon").value = info.icon;
+ document.getElementById("svcedt_chk_bypass").checked = (true == info.preauthorized) ? "checked" : null;
+ }
+ }
+}
+
+function WebDevEditor_Save()
+{
+ if (typeof(window.external.WebDev) == "undefined")
+ alert("Cannot access Webdev Api");
+ else
+ {
+ var serviceId = parseInt(document.getElementById("scvedt_edt_id").value, 10);
+ if (0 != serviceId)
+ {
+ var serviceName = document.getElementById("scvedt_edt_name").value;
+ var serviceUrl = document.getElementById("scvedt_edt_url").value;
+ var serviceIcon = document.getElementById("scvedt_edt_icon").value;
+ var serviceAuth = document.getElementById("svcedt_chk_bypass").checked;
+ if (false == window.external.WebDev.serviceSetInfo(serviceId, serviceName, serviceIcon, serviceUrl, serviceAuth))
+ {
+ alert("Unable to set service info");
+ }
+ if (false == window.external.WebDev.serviceOpen(serviceId, 1))
+ {
+ alert("Unable to navigate");
+ }
+ }
+ }
+}
+
+function WebDevEditor_Close()
+{
+ if (typeof(window.external.WebDev) == "undefined")
+ alert("Cannot access Webdev Api");
+ else
+ {
+ var serviceId = parseInt(document.getElementById("scvedt_edt_id").value, 10);
+ if (0 == serviceId || false == window.external.WebDev.serviceOpen(serviceId, 1))
+ alert("Unable to navigate");
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/resources/pages/welcome.htm b/Src/Plugins/Library/ml_webdev/resources/pages/welcome.htm
new file mode 100644
index 00000000..a89bc2c5
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/resources/pages/welcome.htm
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>WebDev Home Page</title>
+ <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
+ <meta name="vs_snapToGrid" content="True">
+ <meta name="vs_showGrid" content="True">
+ <meta content="text/html;charset=utf-8" http-equiv="content-type">
+ <script type="text/javascript" src="webdev.js"></script>
+</head>
+<body style="OVERFLOW-Y: auto; OVERFLOW-X: auto; OVERFLOW: visible; FONT-FAMILY: MS Shell Dlg 2;">
+ <div align="left">
+ <div style="DISPLAY: inline; FONT-WEIGHT: bold; FONT-SIZE: x-large; LEFT: 50%; MARGIN-LEFT: -216px; WIDTH: 432px; POSITION: absolute" id="DIV4" ms_positioning="FlowLayout" language="javascript" onclick="return DIV2_onclick()" align="center">Welcome to WebDev</DIV>
+ <div style="DISPLAY: inline; FONT-SIZE: 9pt; LEFT: 50%; MARGIN-LEFT: -212px; WIDTH: 424px; POSITION: absolute; TOP: 96px; HEIGHT: 248px" ms_positioning="FlowLayout" align="justify" id='DIV1"'>
+ <p>The Webdev plug-in is designed to help with the process of creating Online Services for Winamp. The plug-in uses the same browser engine as the Online Media plug-in in the Media Library and also has access to JSAPI2, making it perfect for you to work with.</p>
+ <p>If you have any questions about how to write an Online Service or just want to share knowledge with others - the <a href="javascript:void(0);" onclick="WebDev_OpenDocumentation(); return false;">Online Documentation</a> is your best friend.</p>
+ <p>New to JSAPI? Want to know what it is capable of and see the client web interaction with your own eyes? Then visit the <a href="javascript:void(0);" onclick="WebDev_OpenJSAPI2Test(); return false;">JSAPI2 Test Page</a>.</p>
+ <p>Having your own service is always better than using another, so if you feel like it is time to change the world - <a href="javascript:void(0);" onclick="WebDev_CreateService(); return false;">Create Your Own Service</a> and make the world a better place!</p>
+ </div>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/serviceHelper.cpp b/Src/Plugins/Library/ml_webdev/serviceHelper.cpp
new file mode 100644
index 00000000..4f0723e2
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/serviceHelper.cpp
@@ -0,0 +1,232 @@
+#include "main.h"
+#include "./serviceHelper.h"
+#include "./wasabi.h"
+#include "./serviceHost.h"
+
+#include <ifc_omservice.h>
+#include <storageIni.h>
+#include <ifc_omserviceeditor.h>
+#include <ifc_omserviceeditor.h>
+#include <ifc_mlnavigationhelper.h>
+
+#include <strsafe.h>
+
+HRESULT ServiceHelper_QueryStorage(ifc_omstorage **storage)
+{
+ if (NULL == storage) return E_POINTER;
+
+ if (NULL == OMSERVICEMNGR)
+ {
+ *storage = NULL;
+ return E_UNEXPECTED;
+ }
+
+ return OMSERVICEMNGR->QueryStorage(&SUID_OmStorageIni, storage);
+}
+
+HRESULT ServiceHelper_Create(UINT serviceId, LPCWSTR pszName, LPCWSTR pszIcon, LPCWSTR pszUrl, UINT flags, BOOL fSave, ifc_omservice **serviceOut)
+{
+ if (NULL == serviceOut)
+ return E_POINTER;
+
+ *serviceOut = NULL;
+
+ if (NULL == OMSERVICEMNGR)
+ return E_UNEXPECTED;
+
+ WebDevServiceHost *serviceHost;
+ if (FAILED(WebDevServiceHost::GetCachedInstance(&serviceHost)))
+ serviceHost = NULL;
+
+ HRESULT hr = OMSERVICEMNGR->CreateService(serviceId, serviceHost, serviceOut);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omserviceeditor *editor;
+ if (SUCCEEDED((*serviceOut)->QueryInterface(IFC_OmServiceEditor, (void**)&editor)))
+ {
+ WCHAR szBuffer[4096] = {0};
+ editor->BeginUpdate();
+ if (NULL != pszName && IS_INTRESOURCE(pszName))
+ {
+ if (NULL != WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)pszName, szBuffer, ARRAYSIZE(szBuffer)))
+ editor->SetName(szBuffer, FALSE);
+ }
+ else
+ editor->SetName(pszName, FALSE);
+
+ if (NULL != pszIcon && IS_INTRESOURCE(pszIcon))
+ {
+ if (SUCCEEDED(Plugin_MakeResourcePath(szBuffer, ARRAYSIZE(szBuffer), RT_RCDATA, pszIcon, RESPATH_COMPACT)))
+ editor->SetIcon(szBuffer, FALSE);
+ }
+ else
+ editor->SetIcon(pszIcon, FALSE);
+
+ if (NULL != pszUrl && IS_INTRESOURCE(pszUrl))
+ {
+ if (SUCCEEDED(Plugin_MakeResourcePath(szBuffer, ARRAYSIZE(szBuffer), RT_HTML, pszUrl, RESPATH_TARGETIE | RESPATH_COMPACT)))
+ editor->SetUrl(szBuffer, FALSE);
+ }
+ else
+ editor->SetUrl(pszUrl, FALSE);
+
+ editor->SetFlags(flags, 0xFFFFFFFF);
+
+ if (FALSE != fSave)
+ {
+ hr = ServiceHelper_Save(*serviceOut);
+ if (FAILED(hr))
+ {
+ (*serviceOut)->Release();
+ *serviceOut = NULL;
+ }
+ }
+ else
+ {
+ editor->SetModified(0, (UINT)-1);
+ }
+ editor->EndUpdate();
+ editor->Release();
+ }
+ }
+
+ if (NULL != serviceHost)
+ serviceHost->Release();
+
+ return hr;
+}
+
+HRESULT ServiceHelper_Save(ifc_omservice *service)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ if (NULL == OMSERVICEMNGR)
+ return E_UNEXPECTED;
+
+ HRESULT hr;
+ ifc_omstorage *storage;
+ hr = ServiceHelper_QueryStorage(&storage);
+ if (SUCCEEDED(hr))
+ {
+ hr = storage->Save(&service, 1, ifc_omstorage::saveModifiedOnly | ifc_omstorage::saveClearModified, NULL);
+ storage->Release();
+ }
+ return hr;
+}
+
+HRESULT ServiceHelper_Delete(ifc_omservice *service)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ if (NULL == OMSERVICEMNGR)
+ return E_UNEXPECTED;
+
+ ifc_omstorage *storage;
+ HRESULT hr = ServiceHelper_QueryStorage(&storage);
+ if (SUCCEEDED(hr))
+ {
+ hr = storage->Delete(&service, 1, NULL);
+ storage->Release();
+ }
+ return hr;
+}
+
+HRESULT ServiceHelper_Reload(ifc_omservice *service)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ if (NULL == OMSERVICEMNGR)
+ return E_UNEXPECTED;
+
+ ifc_omstorage *storage;
+ HRESULT hr = ServiceHelper_QueryStorage(&storage);
+ if (SUCCEEDED(hr))
+ {
+ hr = storage->Reload(&service, 1, NULL);
+ storage->Release();
+ }
+
+ return hr;
+}
+
+HRESULT ServiceHelper_UpdateIcon(ifc_omserviceeditor *editor, LPCWSTR pszImage)
+{
+ WCHAR szBuffer[8192];
+ szBuffer[0] = L'\0';
+
+ if (NULL == editor)
+ return E_INVALIDARG;
+
+ ifc_omservice *service;
+ if (SUCCEEDED(editor->QueryInterface(IFC_OmService, (void**)&service)))
+ {
+ if (FAILED(service->GetIcon(szBuffer, ARRAYSIZE(szBuffer))))
+ szBuffer[0] = L'\0';
+ service->Release();
+ }
+
+ HRESULT hr = editor->SetIcon(pszImage, FALSE);
+ if (FAILED(hr) || S_FALSE == hr) return hr;
+
+ if (L'\0' != szBuffer)
+ {
+ ifc_mlnavigationhelper *navHelper;
+ if (SUCCEEDED(OMUTILITY->GetMlNavigationHelper(Plugin_GetLibrary(), &navHelper)))
+ {
+ navHelper->ReleaseIndex(szBuffer);
+ navHelper->Release();
+ }
+ }
+
+ return S_OK;
+}
+
+HRESULT ServiceHelper_IsSpecial(ifc_omservice *service)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ UINT serviceFlags;
+ HRESULT hr = service->GetFlags(&serviceFlags);
+ if (SUCCEEDED(hr))
+ {
+ hr = (0 != ((WDSVCF_ROOT | WDSVCF_SPECIAL) & serviceFlags)) ? S_OK : S_FALSE;
+ }
+
+ return hr;
+}
+
+HRESULT ServiceHelper_IsPreAuthorized(ifc_omservice *service)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ UINT serviceFlags;
+ HRESULT hr = service->GetFlags(&serviceFlags);
+ if (SUCCEEDED(hr))
+ {
+ hr = (0 != (WDSVCF_PREAUTHORIZED & serviceFlags)) ? S_OK : S_FALSE;
+ }
+
+ return hr;
+}
+
+HRESULT ServiceHelper_RegisterPreAuthorized(ifc_omservice *service)
+{
+ if (NULL == AGAVE_API_JSAPI2_SECURITY)
+ return E_UNEXPECTED;
+
+ HRESULT hr;
+ WCHAR szBuffer[64];
+ hr = StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), L"%u", service->GetId());
+ if (SUCCEEDED(hr))
+ {
+ UINT flags;
+ bool bypassEnabled = (SUCCEEDED(service->GetFlags(&flags)) && 0 != (WDSVCF_PREAUTHORIZED & flags));
+ AGAVE_API_JSAPI2_SECURITY->SetBypass(szBuffer, bypassEnabled);
+ }
+ return hr;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/serviceHelper.h b/Src/Plugins/Library/ml_webdev/serviceHelper.h
new file mode 100644
index 00000000..6359a199
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/serviceHelper.h
@@ -0,0 +1,28 @@
+#ifndef NULLSOFT_WEBDEV_PLUGIN_SERVICE_HELPER_HEADER
+#define NULLSOFT_WEBDEV_PLUGIN_SERVICE_HELPER_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+class ifc_omservice;
+class ifc_omserviceeditor;
+class ifc_omstorage;
+
+#define WDSVCF_ROOT 0x00000001
+#define WDSVCF_SPECIAL 0x00000002
+#define WDSVCF_PREAUTHORIZED 0x00000004
+
+HRESULT ServiceHelper_QueryStorage(ifc_omstorage **storage);
+HRESULT ServiceHelper_Create(UINT serviceId, LPCWSTR pszName, LPCWSTR pszIcon, LPCWSTR pszUrl, UINT flags, BOOL fSave, ifc_omservice **serviceOut);
+HRESULT ServiceHelper_Save(ifc_omservice *service);
+HRESULT ServiceHelper_Delete(ifc_omservice *service);
+HRESULT ServiceHelper_Reload(ifc_omservice *service);
+HRESULT ServiceHelper_UpdateIcon(ifc_omserviceeditor *editor, LPCWSTR pszImage);
+HRESULT ServiceHelper_IsSpecial(ifc_omservice *service);
+HRESULT ServiceHelper_IsPreAuthorized(ifc_omservice *service);
+HRESULT ServiceHelper_RegisterPreAuthorized(ifc_omservice *service);
+
+#endif //NULLSOFT_WEBDEV_PLUGIN_SERVICE_HELPER_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/serviceHost.cpp b/Src/Plugins/Library/ml_webdev/serviceHost.cpp
new file mode 100644
index 00000000..a5c746bb
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/serviceHost.cpp
@@ -0,0 +1,243 @@
+#include "main.h"
+#include "./serviceHost.h"
+#include "./wasabi.h"
+#include "./resource.h"
+#include "./external.h"
+#include "./navigation.h"
+#include "./commands.h"
+#include "./serviceHelper.h"
+
+#include <ifc_omservice.h>
+#include <ifc_omserviceeditor.h>
+#include <ifc_omservicecommand.h>
+#include <browserView.h>
+
+#include "../winamp/wa_ipc.h"
+#include "../winamp/IWasabiDispatchable.h"
+#include "../winamp/JSAPI_Info.h"
+
+#include <strsafe.h>
+
+
+#define IS_INVALIDISPATCH(__disp) (((IDispatch *)1) == (__disp) || NULL == (__disp))
+
+static WebDevServiceHost *cachedInstance = NULL;
+
+WebDevServiceHost::WebDevServiceHost()
+ : ref(1)
+{
+
+}
+
+WebDevServiceHost::~WebDevServiceHost()
+{
+
+}
+
+HRESULT WebDevServiceHost::CreateInstance(WebDevServiceHost **instance)
+{
+ if (NULL == instance)
+ return E_POINTER;
+
+ *instance = new WebDevServiceHost();
+ if (NULL == instance) return E_OUTOFMEMORY;
+
+ return S_OK;
+}
+
+HRESULT WebDevServiceHost::GetCachedInstance(WebDevServiceHost **instance)
+{
+ if (NULL == instance)
+ return E_POINTER;
+
+
+ if (NULL == cachedInstance)
+ {
+ HRESULT hr = CreateInstance(&cachedInstance);
+ if (FAILED(hr))
+ {
+ *instance = NULL;
+ return hr;
+ }
+ }
+
+ cachedInstance->AddRef();
+ *instance = cachedInstance;
+ return S_OK;
+}
+
+HRESULT WebDevServiceHost::ReleseCache()
+{
+ if (NULL == cachedInstance)
+ return S_FALSE;
+
+ WebDevServiceHost *t = cachedInstance;
+ cachedInstance = NULL;
+ t->Release();
+
+ return S_OK;
+}
+
+size_t WebDevServiceHost::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+size_t WebDevServiceHost::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+int WebDevServiceHost::QueryInterface(GUID interface_guid, void **object)
+{
+ if (NULL == object) return E_POINTER;
+
+ if (IsEqualIID(interface_guid, IFC_OmServiceHost))
+ *object = static_cast<ifc_omservicehost*>(this);
+ else if (IsEqualIID(interface_guid, IFC_OmServiceEvent))
+ *object = static_cast<ifc_omserviceevent*>(this);
+ else
+ {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+
+ if (NULL == *object)
+ return E_UNEXPECTED;
+
+ AddRef();
+ return S_OK;
+}
+
+HRESULT WebDevServiceHost::GetExternal(ifc_omservice *service, IDispatch **ppDispatch)
+{
+ if (NULL == ppDispatch)
+ return E_POINTER;
+
+ if (NULL != *ppDispatch)
+ {
+ // try to connect our external
+ IWasabiDispatchable *pWasabi;
+ if (SUCCEEDED((*ppDispatch)->QueryInterface(IID_IWasabiDispatchable, (void**)&pWasabi)))
+ {
+ JSAPI::ifc_info *pInfo;
+ if (SUCCEEDED(pWasabi->QueryDispatchable(JSAPI::IID_JSAPI_ifc_info, (Dispatchable**)&pInfo)))
+ {
+ ExternalDispatch *pExternal;
+ if (SUCCEEDED(ExternalDispatch::CreateInstance(&pExternal)))
+ {
+ pInfo->AddAPI(pExternal->GetName(), pExternal);
+ pExternal->Release();
+ }
+ pInfo->Release();
+ }
+ pWasabi->Release();
+ }
+ }
+
+ return S_OK;
+}
+
+HRESULT WebDevServiceHost::GetBasePath(ifc_omservice *service, LPWSTR pszBuffer, UINT cchBufferMax)
+{
+ if (NULL == pszBuffer)
+ return E_POINTER;
+
+ return StringCchCopy(pszBuffer, cchBufferMax, L".\\Plugins\\webDev");
+}
+
+HRESULT WebDevServiceHost::GetDefaultName(ifc_omservice *service, LPWSTR pszBuffer, UINT cchBufferMax)
+{
+ if (NULL == pszBuffer)
+ return E_POINTER;
+
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ return StringCchPrintf(pszBuffer, cchBufferMax, L"wdService_{%010u}.ini", service->GetId());
+}
+
+HRESULT WebDevServiceHost::QueryCommandState(ifc_omservice *service, HWND hBrowser, const GUID *commandGroup, UINT commandId)
+{
+ if (NULL == service || NULL == commandGroup)
+ return E_NOTIMPL;
+
+ if (IsEqualGUID(*commandGroup, CMDGROUP_ADDRESSBAR))
+ {
+ switch(commandId)
+ {
+ case ADDRESSCOMMAND_VISIBLE:
+ if (S_FALSE == ServiceHelper_IsSpecial(service))
+ return CMDSTATE_ENABLED;
+ return CMDSTATE_UNKNOWN;
+ case ADDRESSCOMMAND_READONLY:
+ if (S_FALSE == ServiceHelper_IsSpecial(service))
+ return CMDSTATE_DISABLED;
+ return CMDSTATE_ENABLED;
+ }
+ }
+
+ return E_NOTIMPL;
+}
+
+HRESULT WebDevServiceHost::ExecuteCommand(ifc_omservice *service, HWND hBrowser, const GUID *commandGroup, UINT commandId, ULONG_PTR commandArg)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT WebDevServiceHost::GetUrl(ifc_omservice *service, LPWSTR pszBuffer, UINT cchBufferMax)
+{
+ return E_NOTIMPL;
+}
+
+
+void WebDevServiceHost::ServiceChange(ifc_omservice *service, UINT nModified)
+{
+ if (NULL == service) return;
+ Navigation *navigation;
+ if (SUCCEEDED(Plugin_GetNavigation(&navigation)))
+ {
+ navigation->UpdateService(service, nModified);
+ navigation->Release();
+ }
+
+ if ( 0 != (ifc_omserviceeditor::modifiedUrl & nModified))
+ {
+ Command_PostNavigateSvc(service, MAKEINTRESOURCE
+ (NAVIGATE_HOME), TRUE);
+ }
+ if (0 != (ifc_omserviceeditor::modifiedFlags & nModified))
+ {
+ ServiceHelper_RegisterPreAuthorized(service);
+ }
+}
+
+#define CBCLASS WebDevServiceHost
+START_MULTIPATCH;
+ START_PATCH(MPIID_OMSVCHOST)
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, ADDREF, AddRef);
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, RELEASE, Release);
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, QUERYINTERFACE, QueryInterface);
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, API_GETEXTERNAL, GetExternal);
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, API_GETBASEPATH, GetBasePath);
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, API_GETDEFAULTNAME, GetDefaultName);
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, API_QUERYCOMMANDSTATE, QueryCommandState);
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, API_EXECUTECOMMAND, ExecuteCommand);
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, API_GETURL, GetUrl);
+
+ NEXT_PATCH(MPIID_OMSVCEVENT)
+ M_CB(MPIID_OMSVCEVENT, ifc_omserviceevent, ADDREF, AddRef);
+ M_CB(MPIID_OMSVCEVENT, ifc_omserviceevent, RELEASE, Release);
+ M_CB(MPIID_OMSVCEVENT, ifc_omserviceevent, QUERYINTERFACE, QueryInterface);
+ M_VCB(MPIID_OMSVCEVENT, ifc_omserviceevent, API_SERVICECHANGE, ServiceChange);
+
+ END_PATCH
+END_MULTIPATCH;
+#undef CBCLASS
diff --git a/Src/Plugins/Library/ml_webdev/serviceHost.h b/Src/Plugins/Library/ml_webdev/serviceHost.h
new file mode 100644
index 00000000..7dbce0e7
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/serviceHost.h
@@ -0,0 +1,55 @@
+#ifndef NULLSOFT_WEBDEV_PLUGIN_SERVICE_HOST_HEADER
+#define NULLSOFT_WEBDEV_PLUGIN_SERVICE_HOST_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <ifc_omservicehost.h>
+#include <ifc_omserviceevent.h>
+#include <bfc/multipatch.h>
+
+#define MPIID_OMSVCHOST 10
+#define MPIID_OMSVCEVENT 20
+
+class WebDevServiceHost : public MultiPatch<MPIID_OMSVCHOST, ifc_omservicehost>,
+ public MultiPatch<MPIID_OMSVCEVENT, ifc_omserviceevent>
+{
+
+protected:
+ WebDevServiceHost();
+ ~WebDevServiceHost();
+
+public:
+ static HRESULT CreateInstance(WebDevServiceHost **instance);
+ static HRESULT GetCachedInstance(WebDevServiceHost **instance);
+ static HRESULT ReleseCache();
+
+public:
+ /* Dispatchable */
+ size_t AddRef();
+ size_t Release();
+ int QueryInterface(GUID interface_guid, void **object);
+
+ /* ifc_omservicehost */
+ HRESULT GetExternal(ifc_omservice *service, IDispatch **ppDispatch);
+ HRESULT GetBasePath(ifc_omservice *service, LPWSTR pszBuffer, UINT cchBufferMax);
+ HRESULT GetDefaultName(ifc_omservice *service, LPWSTR pszBuffer, UINT cchBufferMax);
+ HRESULT QueryCommandState(ifc_omservice *service, HWND hBrowser, const GUID *commandGroup, UINT commandId);
+ HRESULT ExecuteCommand(ifc_omservice *service, HWND hBrowser, const GUID *commandGroup, UINT commandId, ULONG_PTR commandArg);
+ HRESULT GetUrl(ifc_omservice *service, LPWSTR pszBuffer, UINT cchBufferMax);
+
+ /* ifc_omsvceventhandler */
+ void ServiceChange(ifc_omservice *service, UINT nModified);
+
+protected:
+ ULONG ref;
+
+protected:
+ RECVS_MULTIPATCH;
+};
+
+
+
+
+#endif //NULLSOFT_WEBDEV_PLUGIN_SERVICE_HOST_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/testPages.rc b/Src/Plugins/Library/ml_webdev/testPages.rc
new file mode 100644
index 00000000..620c0138
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/testPages.rc
@@ -0,0 +1,22 @@
+#include "./resource.h"
+/////////////////////////////////////////////////////////////////////////////
+//
+// HTML
+//
+
+IDR_HTML_TEST HTML ".\\resources\\pages\\webdev.htm"
+APPLICATIONAPI.HTM HTML ".\\resources\\pages\\applicationApi.htm"
+BOOKMARKAPI.HTM HTML ".\\resources\\pages\\bookmarkApi.htm"
+CONFIGAPI.HTM HTML ".\\resources\\pages\\configApi.htm"
+HISTORYAPI.HTM HTML ".\\resources\\pages\\historyApi.htm"
+MEDIACOREAPI.HTM HTML ".\\resources\\pages\\mediaCoreApi.htm"
+PLAYLISTAPI.HTM HTML ".\\resources\\pages\\playlistApi.htm"
+PLAYQUEUEAPI.HTM HTML ".\\resources\\pages\\playQueueApi.htm"
+PODCASTAPI.HTM HTML ".\\resources\\pages\\podcastApi.htm"
+SECURITYAPI.HTM HTML ".\\resources\\pages\\securityApi.htm"
+SKINAPI.HTM HTML ".\\resources\\pages\\skinApi.htm"
+TRANSPORTAPI.HTM HTML ".\\resources\\pages\\transportApi.htm"
+TRANSPORTEVENTSAPI.HTM HTML ".\\resources\\pages\\transportEventsApi.htm"
+API2.JS HTML ".\\resources\\pages\\api2.js"
+WEBDEV.JS HTML ".\\resources\\pages\\webdev.js"
+ASYNCDOWNLOADERAPI.HTM HTML ".\\resources\\pages\\asyncDownloaderApi.htm" \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/version.rc2 b/Src/Plugins/Library/ml_webdev/version.rc2
new file mode 100644
index 00000000..1b875991
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 2,3,4,0
+ PRODUCTVERSION WINAMP_PRODUCTVER
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Winamp SA"
+ VALUE "FileDescription", "Winamp Media Library Plug-in"
+ VALUE "FileVersion", "2,3,4,0"
+ VALUE "InternalName", "Nullsoft WebDev Test Platform"
+ VALUE "LegalCopyright", "Copyright © 1997-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "ml_webdev.dll"
+ VALUE "ProductName", "Winamp"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/Src/Plugins/Library/ml_webdev/wasabi.cpp b/Src/Plugins/Library/ml_webdev/wasabi.cpp
new file mode 100644
index 00000000..136cfe5f
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/wasabi.cpp
@@ -0,0 +1,87 @@
+#include "main.h"
+#include "./wasabi.h"
+#include <api/service/waservicefactory.h>
+
+static ULONG wasabiRef = 0;
+
+api_application *WASABI_API_APP = NULL;
+api_language *WASABI_API_LNG = NULL;
+api_explorerfindfile *WASABI_API_EXPLORERFINDFILE = NULL;
+obj_ombrowser *browserManager = NULL;
+ifc_omservicemanager *serviceManager = NULL;
+ifc_omutility *omUtility = NULL;
+JSAPI2::api_security *AGAVE_API_JSAPI2_SECURITY = NULL;
+
+HINSTANCE WASABI_API_LNG_HINST = NULL;
+HINSTANCE WASABI_API_ORIG_HINST = NULL;
+EXTERN_C winampMediaLibraryPlugin plugin;
+
+void *Wasabi_QueryInterface(REFGUID interfaceGuid)
+{
+ waServiceFactory *serviceFactory = plugin.service->service_getServiceByGuid(interfaceGuid);
+ return (NULL != serviceFactory) ? serviceFactory->getInterface() : NULL;
+}
+
+void Wasabi_ReleaseInterface(REFGUID interfaceGuid, void *pInstance)
+{
+ if (NULL == pInstance) return;
+ waServiceFactory *serviceFactory = plugin.service->service_getServiceByGuid(interfaceGuid);
+ if (NULL != serviceFactory) serviceFactory->releaseInterface(pInstance);
+}
+
+void Wasabi_SafeRelease(Dispatchable *pDisp)
+{
+ if (NULL != pDisp)
+ pDisp->Release();
+}
+
+BOOL WasabiApi_Initialize(HINSTANCE hInstance)
+{
+ WASABI_API_APP = QueryWasabiInterface(api_application, applicationApiServiceGuid);
+ WASABI_API_LNG = QueryWasabiInterface(api_language, languageApiGUID);
+ WASABI_API_EXPLORERFINDFILE = QueryWasabiInterface(api_explorerfindfile, ExplorerFindFileApiGUID);
+ AGAVE_API_JSAPI2_SECURITY = QueryWasabiInterface(JSAPI2::api_security, JSAPI2::api_securityGUID);
+ OMBROWSERMNGR = QueryWasabiInterface(obj_ombrowser, OBJ_OmBrowser);
+ OMSERVICEMNGR = QueryWasabiInterface(ifc_omservicemanager, IFC_OmServiceManager);
+ OMUTILITY = QueryWasabiInterface(ifc_omutility, IFC_OmUtility);
+
+ if (NULL != WASABI_API_LNG)
+ WASABI_API_START_LANG(hInstance, WebDevLangUid);
+
+ WasabiApi_AddRef();
+ return TRUE;
+}
+
+static void WasabiApi_Uninitialize()
+{
+ ReleaseWasabiInterface(applicationApiServiceGuid, WASABI_API_APP);
+ ReleaseWasabiInterface(languageApiGUID, WASABI_API_LNG);
+ ReleaseWasabiInterface(ExplorerFindFileApiGUID, WASABI_API_EXPLORERFINDFILE);
+ ReleaseWasabiInterface(JSAPI2::api_securityGUID, AGAVE_API_JSAPI2_SECURITY);
+ ReleaseWasabiInterface(OBJ_OmBrowser, OMBROWSERMNGR);
+ ReleaseWasabiInterface(IFC_OmServiceManager, OMSERVICEMNGR);
+ ReleaseWasabiInterface(IFC_OmUtility, OMUTILITY);
+
+ WASABI_API_APP = NULL;
+ WASABI_API_LNG = NULL;
+ WASABI_API_EXPLORERFINDFILE = NULL;
+ AGAVE_API_JSAPI2_SECURITY = NULL;
+}
+
+ULONG WasabiApi_AddRef()
+{
+ return InterlockedIncrement((LONG*)&wasabiRef);
+}
+
+ULONG WasabiApi_Release()
+{
+ if (0 == wasabiRef)
+ return wasabiRef;
+
+ LONG r = InterlockedDecrement((LONG*)&wasabiRef);
+ if (0 == r)
+ {
+ WasabiApi_Uninitialize();
+ }
+ return r;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_webdev/wasabi.h b/Src/Plugins/Library/ml_webdev/wasabi.h
new file mode 100644
index 00000000..1149cac5
--- /dev/null
+++ b/Src/Plugins/Library/ml_webdev/wasabi.h
@@ -0,0 +1,45 @@
+#ifndef NULLSOFT_WEBDEV_PLUGIN_WASABI_HEADER
+#define NULLSOFT_WEBDEV_PLUGIN_WASABI_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+#include <api/application/api_application.h>
+#define WASABI_API_APP applicationApi
+
+#include "../Agave/Language/api_language.h"
+
+#include "../Agave/ExplorerFindFile/api_explorerfindfile.h"
+
+#include "../Winamp/JSAPI2_api_security.h"
+extern JSAPI2::api_security *jsapi2_securityApi;
+#define AGAVE_API_JSAPI2_SECURITY jsapi2_securityApi
+
+#include <obj_ombrowser.h>
+extern obj_ombrowser *browserManager;
+#define OMBROWSERMNGR browserManager
+
+#include <ifc_omservicemanager.h>
+extern ifc_omservicemanager *serviceManager;
+#define OMSERVICEMNGR serviceManager
+
+#include <ifc_omutility.h>
+extern ifc_omutility *omUtility;
+#define OMUTILITY omUtility
+
+BOOL WasabiApi_Initialize(HINSTANCE hInstance);
+ULONG WasabiApi_AddRef(void);
+ULONG WasabiApi_Release(void);
+
+void *Wasabi_QueryInterface(REFGUID interfaceGuid);
+void Wasabi_ReleaseInterface(REFGUID interfaceGuid, void *pInstance);
+
+#define QueryWasabiInterface(__interfaceType, __interfaceGuid) ((##__interfaceType*)Wasabi_QueryInterface(__interfaceGuid))
+#define ReleaseWasabiInterface(__interfaceGuid, __interfaceInstance) (Wasabi_ReleaseInterface((__interfaceGuid), (__interfaceInstance)))
+
+void Wasabi_SafeRelease(Dispatchable *pDisp);
+
+#endif // NULLSOFT_WEBDEV_PLUGIN_WASABI_HEADER