কীভাবে নকআউট.জেএস-এ পর্যবেক্ষণযোগ্য বাইন্ডিংগুলি সাফ / মুছে ফেলা যায়?


113

আমি কোনও ওয়েবপৃষ্ঠায় কার্যকারিতা তৈরি করছি যা ব্যবহারকারী একাধিকবার সম্পাদন করতে পারে। ব্যবহারকারীর ক্রিয়াকলাপের মাধ্যমে, একটি অবজেক্ট / মডেল তৈরি করা হয় এবং কো-এপ্লাইবাইন্ডিংস () ব্যবহার করে এইচটিএমএলে প্রয়োগ করা হয়।

ডেটা-বেঁধে এইচটিএমএল jQuery টেম্পলেট মাধ্যমে তৈরি করা হয়।

এ পর্যন্ত সব ঠিকই.

আমি যখন দ্বিতীয় পদক্ষেপ / মডেল তৈরি করে এই পদক্ষেপটি পুনরাবৃত্তি করি এবং ko.applyBindings () কল করি তখন আমার দুটি সমস্যার মুখোমুখি হয়:

  1. মার্কআপটি পূর্ববর্তী অবজেক্ট / মডেলের পাশাপাশি নতুন অবজেক্ট / মডেলটি দেখায়।
  2. একটি জাভাস্ক্রিপ্ট ত্রুটি ঘটেছে অবজেক্ট / মডেলের যে কোনও একটি বৈশিষ্ট্য সম্পর্কিত, যদিও এটি এখনও মার্কআপে রেন্ডার করা হয়েছে।

এই সমস্যাটি সরাতে, প্রথম পাসের পরে আমি jQuery এর .empty () কে কল করি যে সমস্ত ডেটা-বাইন্ড বৈশিষ্ট্যযুক্ত টেম্প্লেটেড এইচটিএমএল সরিয়ে ফেলতে, যাতে এটি আর ডোমে থাকে না। ব্যবহারকারী দ্বিতীয় পাসের জন্য প্রক্রিয়া শুরু করলে ডেটা-ভিত্তিতে এইচটিএমএলটি ডিওমে আবার যুক্ত হয়।

তবে আমি যেমন বলেছিলাম, যখন এইচটিএমএলটি ডিওমে আবার যুক্ত হয় এবং নতুন অবজেক্ট / মডেলটিতে পুনরায় আবদ্ধ হয়, তখনও এটি প্রথম অবজেক্ট / মডেল থেকে ডেটা অন্তর্ভুক্ত করে এবং আমি এখনও জেএস ত্রুটি পাই যা ঘটে না প্রথম পাসের সময়

উপসংহারটি উপস্থিত হয় যে নকআউট এই বাঁধা বৈশিষ্ট্যগুলিকে ধরে রেখেছে, যদিও মার্কটিপটি ডিওএম থেকে সরানো হয়েছে।

সুতরাং আমি যা খুঁজছি তা হ'ল নকআউট থেকে এই সীমাবদ্ধ বৈশিষ্ট্যগুলি সরিয়ে ফেলার একটি মাধ্যম; নকআউট বলছে যে আর কোনও পর্যবেক্ষণযোগ্য মডেল নেই। এই কাজ করতে একটি উপায় আছে কি?

সম্পাদনা

মূল প্রক্রিয়াটি হ'ল ব্যবহারকারী কোনও ফাইল আপলোড করে; সার্ভারটি একটি JSON অবজেক্টের সাথে প্রতিক্রিয়া জানায়, ডেটা-আবদ্ধ এইচটিএমএলটি ডোমে যুক্ত হয়, তারপরে JSON অবজেক্ট মডেলটি এই এইচটিএমএল ব্যবহার করে আবদ্ধ হয়

mn.AccountCreationModel = new AccountViewModel(jsonData.Account);
ko.applyBindings(mn.AccountCreationModel);

একবার ব্যবহারকারীর মডেলটিতে কিছু নির্বাচন করা হয়ে গেলে একই বস্তুটি আবার সার্ভারে পোস্ট করা হয়, তখন ডেটা-ভিত্তিক এইচটিএমএলটি তখন ডিওএম থেকে সরানো হয়, এবং আমার পরে নিম্নলিখিত জেএস থাকে

mn.AccountCreationModel = null;

যখন ব্যবহারকারী আরও একবার এটি করতে চায়, তখন এই সমস্ত পদক্ষেপগুলি পুনরাবৃত্তি হয়।

আমি ভয় পাচ্ছি যে জেএসফিডাল ডেমো করতে কোডটি খুব 'জড়িত'।


Ko.applyBindings বিশেষত একই ডম উপাদান সহ একাধিকবার কল করার পরামর্শ দেওয়া হয় না। আপনি যা চান তা অর্জনের আর একটি উপায় থাকতে পারে। যদিও আপনাকে আরও কোড সরবরাহ করতে হবে। যদি সম্ভব হয় তবে একটি jsfiddle অন্তর্ভুক্ত করুন।
madcapnmckay

এমন কোনও initক্রিয়াকলাপটি কেন প্রকাশ করা হয় না যেখানে আপনি প্রয়োগ করতে ডেটা পাস করেন?
কিওরকোড

উত্তর:


169

আপনি কি মেমরি সীমাবদ্ধ অবজেক্টগুলিকে নিষ্পত্তি করতে আপনার ডম উপাদানটিতে নকআউটের ক্লিন নোড পদ্ধতিটি কল করার চেষ্টা করেছেন?

var element = $('#elementId')[0]; 
ko.cleanNode(element);

তারপরে আপনার নতুন ভিউ মডেলগুলির সাথে ঠিক সেই উপাদানটিতে আবার নকআউট বাইন্ডিংগুলি প্রয়োগ করা আপনার ভিউ বাইন্ডিং আপডেট করবে।


33
এটি কাজ করে - ধন্যবাদ। যদিও আমি এই পদ্ধতিতে কোনও ডকুমেন্টেশন খুঁজে পাচ্ছি না।
awj

আমিও এই নকআউট ইউটিলিটি ফাংশনে ডকুমেন্টেশন অনুসন্ধান করেছি। সোর্স কোড থেকে যতটুকু আমি বলতে পারি, এটি deleteডোম উপাদানগুলির নিজস্ব কিছু কীগুলি কল করছে যা স্পষ্টতই যেখানে সমস্ত নকআউট যাদুটি সঞ্চিত রয়েছে। কারও কাছে ডকুমেন্টেশনের উত্স থাকলে আমি অনেক বেশি বাধ্য li
প্যাট্রিক এম

2
আপনি এটি পাবেন না। আমি কো ইউটিলিটি ফাংশনে ডক্সের জন্য উচ্চ এবং নিম্ন সন্ধান করেছি, কিন্তু এর কোনটিই বিদ্যমান নেই। এই ব্লগ পোস্টটি আপনার সর্বাধিক নিকটতম জিনিস যা আপনি খুঁজে পাবেন তবে এটি কেবল কো.ইউটিলেসগুলির সদস্যদের কভার করে: knockmeout.net/2011/04/util-function-in-knockoutjs.html
নিক ড্যানিয়েল

1
আপনি নীচে আমার উত্তরে দেখানো হিসাবে ম্যানুয়ালি ইভেন্টগুলি মুছে ফেলতে চাইবেন।
মাইকেল বারকম্পাস

1
@ কোডেক্রেচর আমি ইতিমধ্যে নীচের কাজের উদাহরণ পোস্ট করেছিলাম। আমার বক্তব্যটি হ'ল তার পরিবর্তে ভিউমোডেলের মধ্যে ডেটা প্রকাশ করার সময়, বাঁধাই অক্ষত রাখা ভাল be এইভাবে আপনাকে কখনই আন / রি-বাইন্ডিংয়ের সাথে ডিল করতে হবে না। এটি সরাসরি ডিওএম থেকে আনবাইন্ড করতে অলিখিত শোধিত পদ্ধতিগুলি ব্যবহার করার চেয়ে পরিষ্কার বলে মনে হচ্ছে। এর বাইরে, ক্লিননোড সমস্যাযুক্ত কারণ এটি ইভেন্ট হ্যান্ডলারের কোনওটি মুক্তি দেয় না (আরও বিশদে বিশদটির জন্য উত্তরটি এখানে দেখুন: stackoverflow.com/questions/15063794/… )
Zac

31

যে প্রকল্পে আমি কাজ করছি তার জন্য আমি একটি সাধারণ ko.unapplyBindingsফাংশন লিখেছিলাম যা একটি jQuery নোড এবং অপসারণ বুলিয়ান গ্রহণ করে। এটি প্রথমে সমস্ত jQuery ইভেন্টগুলিকে আবদ্ধ ko.cleanNodeকরে কারণ পদ্ধতিটি তার যত্ন নেয় না। আমি মেমরি ফাঁস জন্য পরীক্ষা করেছি, এবং এটি ঠিক কাজ করে বলে মনে হচ্ছে।

ko.unapplyBindings = function ($node, remove) {
    // unbind events
    $node.find("*").each(function () {
        $(this).unbind();
    });

    // Remove KO subscriptions and references
    if (remove) {
        ko.removeNode($node[0]);
    } else {
        ko.cleanNode($node[0]);
    }
};

কেবলমাত্র একটি সতর্কতামূলক, আমি এমন কিছু যা পুনরায় আবদ্ধ করার জন্য পুনরায় বাইন্ডিং পরীক্ষা করে দেখিনি ko.cleanNode()যা পুরো এইচটিএমএল প্রতিস্থাপন করা হয়নি।
মাইকেল বারকম্পাস

4
আপনার সমাধানটি অন্য সমস্ত ইভেন্টের বাইন্ডিংগুলিকেও আবদ্ধ করবে না? কেবল কো-এর ইভেন্ট হ্যান্ডলারগুলি অপসারণ করার কোন সম্ভাবনা আছে?
লর্ডভ্ল্যাড

কো কোর যে পরিবর্তন ছাড়া
lordvlad

1
সত্য, তবে এটি সম্ভব না যতক্ষণ আমি মূল পরিবর্তন ছাড়াই বলতে পারি। এই সমস্যাটি আমি এখানে নিয়ে এসেছি দেখুন: github.com/SteveSanderson/knockout/issues/724
মাইকেল বারকম্পাস

KO এর ধারণাটি নয় যে আপনি খুব কমই নিজেকে ডমকে স্পর্শ করবেন? এই উত্তরটি ডোমটির মধ্য দিয়ে যায় এবং এটি আমার ব্যবহারের ক্ষেত্রে অবশ্যই অব্যর্থ।
ব্লুজি

12

বাঁধাইয়ের অফারটি সহ আপনি বাইন্ডিংটি ব্যবহার করে চেষ্টা করতে পারেন : http://knockoutjs.com/docamentation/with-binding.html একবার প্রয়োগ বাইন্ডিং ব্যবহার করা উচিত এবং যখনই আপনার ডেটা পরিবর্তন হয় কেবলমাত্র আপনার মডেলটি আপডেট করুন।

আমাদের শীর্ষ স্তরের ভিউ মডেল স্টোর ভিউমোডেল, কার্টভিউমোডেল দ্বারা প্রতিনিধিত্ব করা আপনার কার্ট এবং সেই কার্টের আইটেমের একটি তালিকা রয়েছে - কথায় আইটেমভিউমোডেল বলে ts

আপনি উপরের স্তরের মডেলটিকে সংযুক্ত করে রাখবেন - স্টোর ভিউমোডেল পুরো পৃষ্ঠায়। তারপরে, আপনি নিজের পৃষ্ঠার অংশগুলি আলাদা করতে পারেন যা কার্ট বা কার্ট আইটেমগুলির জন্য দায়ী।

ধরে নেওয়া যাক কার্টআইটেমভিউমোডেলের নিম্নলিখিত কাঠামো রয়েছে:

var actualCartItemsModel = { CartItems: [
  { ItemName: "FirstItem", Price: 12 }, 
  { ItemName: "SecondItem", Price: 10 }
] }

কার্টআইটেমভিউমোডেল শুরুতে খালি থাকতে পারে।

পদক্ষেপগুলি দেখতে এই রকম হবে:

  1. এইচটিএমএলে বাইন্ডিংগুলি সংজ্ঞায়িত করুন। কার্টআইটেমভিউমোডেল বাইন্ডিং পৃথক করুন।

      
        <div data-bind="with: cartItemsViewModel">
          <div data-bind="foreach: CartItems">
            <span data-bind="text: ItemName"></span>
            <span data-bind="text: Price"></span>
          </div>
        </div>
      
    
  2. স্টোর মডেলটি আপনার সার্ভার থেকে আসে (বা অন্য কোনওভাবে তৈরি হয়)।

    var storeViewModel = ko.mapping.fromJS(modelFromServer)

  3. আপনার শীর্ষ স্তরের ভিউ মডেলের খালি মডেলগুলি সংজ্ঞায়িত করুন। তারপরে সেই মডেলের একটি কাঠামো প্রকৃত ডেটা দিয়ে আপডেট করা যেতে পারে।

      
        storeViewModel.cartItemsViewModel = ko.observable();
        storeViewModel.cartViewModel = ko.observable();
     
    
  4. শীর্ষ স্তরের ভিউ মডেল বাঁধুন।

    ko.applyBindings(storeViewModel);

  5. কার্টআইটেমভিউমোডেল অবজেক্টটি যখন উপলব্ধ থাকে তখন এটি পূর্বনির্ধারিত স্থানধারককে নির্ধারণ করুন।

    storeViewModel.cartItemsViewModel(actualCartItemsModel);

আপনি যদি কার্টের আইটেমগুলি সাফ করতে চান: storeViewModel.cartItemsViewModel(null);

নকআউট এইচটিএমএল-এর যত্ন নেবে - উদাহরণস্বরূপ এটি প্রদর্শিত হবে যখন মডেলটি খালি না থাকে এবং ডিভের ("বাইন্ডিং সহ") থাকা সামগ্রীটি অদৃশ্য হয়ে যায়।


9

আমি ko.applyBinding প্রতিবার অনুসন্ধান বোতাম ক্লিক করতে হবে, এবং ফিল্টার ডেটা সার্ভার থেকে ফিরে আসে, এবং এই ক্ষেত্রে ko.cleanNode ব্যবহার না করে আমার জন্য কাজ করে।

আমি অভিজ্ঞ, যদি আমরা ফর্চাকে টেম্পলেট দিয়ে প্রতিস্থাপন করি তবে সংগ্রহ / পর্যবেক্ষণযোগ্য অ্যারে ক্ষেত্রে এটি কাজ করা উচিত।

আপনি এই পরিস্থিতিতে দরকারী মনে হতে পারে।

<ul data-bind="template: { name: 'template', foreach: Events }"></ul>

<script id="template" type="text/html">
    <li><span data-bind="text: Name"></span></li>
</script>

1
আমি অনুরূপ সমস্যা সমাধানের চেষ্টা করতে কমপক্ষে 4 ঘন্টা ব্যয় করেছি এবং কেবল আমিরের সমাধানই আমার পক্ষে কাজ করে।
আন্তোনিন জ্লিনেঙ্ক

এইচটিএমএল সম্পূর্ণরূপে অপসারণ করতে এবং আমার দৃশ্যে আমি যে বিষয়টিকে অন্যভাবে অনুভব করেছি তা অ্যান্টোনিনজলিনেকে, এবং গতিশীলতার সাথে পুনরায় যুক্ত করে সমস্ত কিছু নিখুঁতভাবে মুছে ফেলার জন্য। উদাহরণস্বরূপ, আমার কাছে নকআউট কোড ধারক ডিভ <ডি আইডি = "নকআউটকন্টেনারডিভ"> </ ডিভি> আছে $ A .আজাক্স সাফল্যের ফলাফল আমি প্রতিবারই সার্ভার পদ্ধতিতে calls ("# নকআউটআউট কনটেনারডিভ") কল করি children / এর বিষয়বস্তু সরান নকআউট কোডের সাথে ডায়নামিক এইচটিএমএল সংযুক্ত করার পদ্ধতিটিকে কল করুন "(" # নকআউটকন্টিনারডিভ ")। অ্যাপেন্ড (" নকআউট বাইন্ডিং কোড সহ চিলডিলেন্টস ") এবং অ্যাপ্লিকেশনকে আবার কল করা হচ্ছে
আমির

1
আরে আম্মির, তোমার মতো অবস্থাও আমার বেশ ভালোই ছিল। আপনার উল্লেখ করা সমস্ত কিছুই আমাকে কাজ করার ব্যতীত দুর্দান্ত কাজ করেছে; ko.cleanNode (উপাদান); পূর্বে প্রতিটি পুনর্নির্দিষ্ট।
রাদোস্লাভ মিনচেভ

@ রেডোস্লাভ মিনচেভ আপনি কি ভাবেন যে আমি আপনাকে আরও সাহায্য করতে পারি, যদি হ্যাঁ তবে কীভাবে, আমি বিশেষ প্রশ্ন সম্পর্কে আমার চিন্তাভাবনা / অভিজ্ঞতা ভাগ করে নিতে পেরে খুশি হব।
আমির সাজ্জাদ

ধন্যবাদ. @ আমিরসজ্জাদ, কেবল উল্লেখ করতে চেয়েছিলেন যে আমার জন্য যা কাজ করেছে তা হল এটি ক্লিননড () ফাংশনটিকে কাজ করার জন্য কল করা।
রডোস্লাভ মিনচেভ

6

কো এর অভ্যন্তরীণ ফাংশনগুলি ব্যবহার করার এবং জিকুয়েরির কম্বল ইভেন্ট হ্যান্ডলার অপসারণের সাথে মোকাবিলা করার পরিবর্তে আরও ভাল ধারণা ব্যবহার করা withবা templateবাঁধাই করা। আপনি যখন এটি করেন, কো DOM এর সেই অংশটি পুনরায় তৈরি করে এবং তাই এটি স্বয়ংক্রিয়ভাবে পরিষ্কার হয়ে যায়। এটিও প্রস্তাবিত উপায়ে, এখানে দেখুন: https://stackoverflow.com/a/15069509/207661


4

আমি মনে করি পুরো সময় বেঁধে রাখা আরও ভাল হতে পারে এবং কেবল এটির সাথে সম্পর্কিত ডেটা আপডেট করে। আমি এই ইস্যুটিতে দৌড়েছি এবং দেখতে পেলাম যে .resetAll()অ্যারেতে আমি আমার ডেটা রাখছিলাম সেই পদ্ধতিটি ব্যবহার করে কল করাটাই এটি করার সবচেয়ে কার্যকর উপায় ছিল।

মূলত আপনি কিছু গ্লোবাল ভার দিয়ে শুরু করতে পারেন যা ভিউমোডেলের মাধ্যমে উপস্থাপন করতে ডেটা ধারণ করে:

var myLiveData = ko.observableArray();

আমি ঠিক myLiveDataএকটি সাধারণ অ্যারে তৈরি করতে পারি না তা বুঝতে আমার কিছুটা সময় লেগেছিল - ko.oberservableArrayঅংশটি গুরুত্বপূর্ণ ছিল।

তারপরে আপনি এগিয়ে যেতে পারেন এবং যা করতে চান তা করতে পারেন myLiveData। উদাহরণস্বরূপ, একটি $.getJSONকল করুন:

$.getJSON("http://foo.bar/data.json?callback=?", function(data) {
    myLiveData.removeAll();
    /* parse the JSON data however you want, get it into myLiveData, as below */
    myLiveData.push(data[0].foo);
    myLiveData.push(data[4].bar);
});

একবার এটি হয়ে গেলে আপনি নিজের ভিউমোডেলটি যথারীতি ব্যবহার করে এগিয়ে যেতে পারেন এবং বাইন্ডিংগুলি প্রয়োগ করতে পারেন:

function MyViewModel() {
    var self = this;
    self.myData = myLiveData;
};
ko.applyBindings(new MyViewModel());

তারপরে এইচটিএমএলটিতে কেবল myDataআপনি যেমন ব্যবহার করেন তেমন ব্যবহার করুন ।

এইভাবে, আপনি যে কোনও ফাংশন থেকে মাইলিভিডেটা সাথে ম্যাক করতে পারেন। উদাহরণস্বরূপ, আপনি যদি প্রতি কয়েক সেকেন্ডে আপডেট করতে চান তবে কেবল $.getJSONএকটি ফাংশনে সেই লাইনটি জড়িয়ে রাখুন এবং এতে কল setIntervalকরুন। myLiveData.removeAll();লাইনটি মনে রাখা যতক্ষণ না মনে থাকে ততক্ষণ আপনাকে কখনই বাইন্ডিং অপসারণ করতে হবে না।

আপনার ডেটা সত্যিই বিশাল না হলে ব্যবহারকারীরা অ্যারেটি পুনরায় সেট করতে এবং তারপরে সর্বাধিক বর্তমান ডেটা যুক্ত করার মধ্যে সময়টি লক্ষ্য করতে সক্ষম হবে না।


প্রশ্ন পোস্ট করার পর থেকে, আমি এখন এটিই করি। এটি ঠিক যে এই নকআউট পদ্ধতিগুলির মধ্যে কিছু হয় হয় বিনা প্রমাণিত বা এটি কী করে তা খুঁজে পেতে আপনার সত্যই চারপাশে (ইতিমধ্যে ফাংশনটির নামটি জেনে রাখা) দরকার।
awj

এটিও খুঁজে পেতে আমাকে খুব কঠোরভাবে দেখতে হয়েছিল (যখন ডক্সের মাধ্যমে খননের সময় সংখ্যা কোডের লাইনগুলির ফলাফলের সংখ্যা ছাড়িয়ে যায় ... বাহ)। ভাল লাগছে আপনাকে কাজ করতে দেখে।
জ্যাক

2

আমি সম্প্রতি একটি মেমরি ফাঁস সমস্যা ছিল এবং ko.cleanNode(element);আমার জন্য এটি না - ko.removeNode(element);করেনি। জাভাস্ক্রিপ্ট + নকআউট.জেএস মেমরি ফাঁস - কীভাবে নিশ্চিত করা যায় যে অবজেক্টটি ধ্বংস হচ্ছে?


নকআউট ৩.১-এ, কো.রেমভনোড আসলে কো-ক্লিননোডকে কল করে। যদিও আমি জানিনা যে এটি পূর্ববর্তী সংস্করণগুলির ক্ষেত্রে ছিল।
কিপ্রেনে

1

আপনি কি এই সম্পর্কে চিন্তাভাবনা করেছেন:

try {
    ko.applyBindings(PersonListViewModel);
}
catch (err) {
    console.log(err.message);
}

আমি এটি নিয়ে এসেছি কারণ নকআউটে, আমি এই কোডটি পেয়েছি

    var alreadyBound = ko.utils.domData.get(node, boundElementDomDataKey);
    if (!sourceBindings) {
        if (alreadyBound) {
            throw Error("You cannot apply bindings multiple times to the same element.");
        }
        ko.utils.domData.set(node, boundElementDomDataKey, true);
    }

সুতরাং আমার কাছে এটি আসলেই ইস্যু নয় যা এরই মধ্যে আবদ্ধ, ত্রুটিটি ধরা পড়েনি এবং মোকাবেলা করা হয়নি ...


0

আমি দেখতে পেয়েছি যে ভিউ মডেলে যদি অনেকগুলি ডিভ বাইন্ডিং থাকে তবে এটি পরিষ্কার করার সর্বোত্তম উপায়টি ko.applyBindings(new someModelView);ব্যবহার করা হয়: ko.cleanNode($("body")[0]);এটি ko.applyBindings(new someModelView2);পূর্ববর্তী ভিউ মডেলটিকে এখনও বেঁধে দেওয়া ছাড়া উদ্বেগ ছাড়াই আপনাকে নতুন গতিবেগের কল করতে দেয় ।


4
আমি যুক্ত করতে চাই এমন কয়েকটি পয়েন্ট রয়েছে: (1) এটি আপনার ওয়েবপৃষ্ঠা থেকে সমস্ত সংযোগগুলি সাফ করবে, যা আপনার অ্যাপ্লিকেশনটির উপযুক্ত হতে পারে তবে আমি ধারণা করি যে এখানে প্রচুর অ্যাপ্লিকেশন রয়েছে যেখানে পৃথকভাবে পৃষ্ঠার একাধিক অংশে বাইন্ডিং যুক্ত করা হয়েছে কারণ। একক, স্যুইপিং কমান্ডে সমস্ত বাইন্ডিং পরিষ্কার করা অনেক ব্যবহারকারীর পক্ষে সহায়ক নাও হতে পারে। (2) দ্রুততর, আরো দক্ষ, নেটিভ জাভাস্ক্রিপ্ট উদ্ধার করতে পদ্ধতি $("body")[0]হল document.body
awj
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.